This commit is contained in:
2026-08-18 18:06:30 +08:00
parent 7bcef0f625
commit 8fdf0fd052
5 changed files with 373 additions and 17 deletions
@@ -122,7 +122,7 @@ MonoBehaviour:
cachedCamera: {fileID: 0}
cachedUirootRect: {fileID: 0}
skeleton: {fileID: 0}
isSpine: 1
isSpine: 0
fishTypeID: 12028
currState: 0
HP: 5
@@ -8,10 +8,26 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 1146886045327190, guid: 44623ca91d5f59049b506c55c76f2aef, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1622780724854902, guid: 44623ca91d5f59049b506c55c76f2aef, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1658730911317380, guid: 44623ca91d5f59049b506c55c76f2aef, type: 3}
propertyPath: m_Name
value: PW_Enemy_4000
objectReference: {fileID: 0}
- target: {fileID: 1707060374769447, guid: 44623ca91d5f59049b506c55c76f2aef, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1981398447280843, guid: 44623ca91d5f59049b506c55c76f2aef, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4231307354823747, guid: 44623ca91d5f59049b506c55c76f2aef, type: 3}
propertyPath: m_LocalPosition.x
value: 0
@@ -52,6 +68,16 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: 199488493079174609, guid: 44623ca91d5f59049b506c55c76f2aef,
type: 3}
propertyPath: m_Materials.Array.size
value: 1
objectReference: {fileID: 0}
- target: {fileID: 199656832037048821, guid: 44623ca91d5f59049b506c55c76f2aef,
type: 3}
propertyPath: m_Materials.Array.size
value: 1
objectReference: {fileID: 0}
m_RemovedComponents:
- {fileID: 7031818894128611810, guid: 44623ca91d5f59049b506c55c76f2aef, type: 3}
m_RemovedGameObjects: []
@@ -149,5 +175,10 @@ MonoBehaviour:
nonFinalDeathBjEndUI: {fileID: -382508373279050920, guid: 75a52b8c1537cad49b3d521483fe134e,
type: 3}
resumeMoveDelay: 2
nonFinalDeathAnimationName: stand02
normalAnimationName: stand01
skeletonChildName:
hittingPath: body/bone/hitting
hittingDuration: 2
useServerParam: 1
maxDeathCount: 2
@@ -178,6 +178,9 @@ public class XingJiXunHangEvent : BJEventBase
[Tooltip("4条激光伸展到最长位置的耗时(秒)")]
public float laserExpandSpeed = 0.6f;
[Tooltip("光子激光结束后等待时长(秒),然后直接隐藏 Prop(默认1)")]
public float symmetraPropHideDelay = 1f;
[Header("光子 - 电网扫动杀鱼")]
[Tooltip("电网(Prop整体)上下扫动幅度(世界单位)")]
public float netSweepAmplitude = 5f;
@@ -202,9 +205,9 @@ public class XingJiXunHangEvent : BJEventBase
public string[] gammaLaserNames = { "laser1", "laser2", "laser3", "laser4" };
[Tooltip("射线显示后到开始旋转的等待(秒,默认1)")]
public float gammaRotateDelay = 1f;
[Tooltip("总旋转角度(度,默认80°)")]
public float gammaRotateAngle = 80f;
[Tooltip("转到该角度后激光开始缩短(度,默认45°)")]
[Tooltip("总旋转角度(度,默认90°,转完为止)")]
public float gammaRotateAngle = 90f;
[Tooltip("转到该角度后激光开始一点一点缩短(度,默认45°)")]
public float gammaShrinkStartAngle = 45f;
[Tooltip("旋转速度(度/秒,正=逆时针)")]
public float gammaRotateSpeed = 45f;
@@ -271,6 +274,12 @@ public class XingJiXunHangEvent : BJEventBase
/// <summary>本局电网已炸的鱼 guid(防止扫动中重复炸同一鱼)</summary>
private readonly HashSet<string> _netKilledFish = new HashSet<string>();
/// <summary>Boss 分数面板 TMP(挂在独立 UI 层,Boss 删除后仍有效)</summary>
private TMPro.TextMeshProUGUI _bossScoreTmp;
/// <summary>本局杀鱼累计加到 Boss 面板的分值</summary>
private long _bossPanelScore = 0;
/// <summary>爆奖过程中实例化的所有临时特效(die() 时统一销毁,防止残留)</summary>
private readonly System.Collections.Generic.List<GameObject> _spawnedEffects = new System.Collections.Generic.List<GameObject>();
@@ -294,6 +303,9 @@ public class XingJiXunHangEvent : BJEventBase
scoreCount = bjCount;
totalBJCount = bjCount;
// ⭐ 星际巡航不需要 ScoreNumScroll(Clone) 加分数字滚动特效(杀鱼分已加 Boss 面板)
showAddScoreFX = false;
// 解析武器 ID
ParseWeaponId();
@@ -324,6 +336,8 @@ public class XingJiXunHangEvent : BJEventBase
bossCtrl = null;
diePos = Vector3.zero;
_netKilledFish.Clear();
_bossScoreTmp = null;
_bossPanelScore = 0;
// ⭐ 对象池复用:清理上次残留的临时特效
for (int i = 0; i < _spawnedEffects.Count; i++)
@@ -383,7 +397,7 @@ public class XingJiXunHangEvent : BJEventBase
/// <summary>根据武器 ID 选择对应的预制体和标签</summary>
private void SelectWeaponPrefabs()
{
_weaponId = 8;
_weaponId = 7;
switch (_weaponId)
{
case 0:
@@ -842,10 +856,16 @@ public class XingJiXunHangEvent : BJEventBase
// ⭐ 逐个杀:每只间隔 = 总时长/数量,整体约 1 秒
float interval = ironFistKillTotalTime / fishes.Count;
Vector3 sitPos = GetSeatPosition();
Vector3 turretPos = GetSeatPosition(); // 金币飞向炮台
int killed = 0;
foreach (var monster in fishes)
{
// ⭐ 分数加满(=maxScore)后停止杀鱼
if (BossScoreFull)
{
Debug.Log($"[XingJiXunHang] 铁拳: 面板分已达上限 {maxScore},停止杀鱼");
break;
}
if (monster == null) continue;
if (!_netKilledFish.Add(monster.guid)) continue;
@@ -853,7 +873,9 @@ public class XingJiXunHangEvent : BJEventBase
{
Amount = paoBei * monster.monsterData.fishTypeConfig.fishScore
};
CreateFishManager.Instance.Id_DieFish_FX(monster.guid, coinNum, sitPos, isLocal: isSelf);
// ⭐ 飞机死亡掉金币(飞向炮台),分数同时加到 Boss 面板(封顶 maxScore)
CreateFishManager.Instance.Id_DieFish_FX(monster.guid, coinNum, turretPos, isLocal: isSelf);
AddBossPanelScore(coinNum.Amount);
killed++;
Debug.Log($"[XingJiXunHang] 铁拳: 从近到远杀鱼[{killed}/{fishes.Count}] 距离={Vector2.Distance(monster.transform.position, center):0.00}");
yield return new WaitForSeconds(interval);
@@ -959,7 +981,13 @@ public class XingJiXunHangEvent : BJEventBase
yield return StartCoroutine(ElectricNetSweep(propObj, propL, propR));
}
yield return new WaitForSeconds(0.3f);
// ⭐ 激光结束:等 1 秒后直接隐藏 Prop(不再一直显示到爆奖结束才回收)
yield return new WaitForSeconds(symmetraPropHideDelay);
if (propObj != null)
{
propObj.SetActive(false);
Debug.Log($"[XingJiXunHang] 光子: 激光结束等待 {symmetraPropHideDelay}s 后隐藏 Prop");
}
}
/// <summary>
@@ -1010,8 +1038,9 @@ public class XingJiXunHangEvent : BJEventBase
}
/// <summary>
/// 电网扫动:Prop 整体上下移动(幅度 netSweepAmplitude,来回 netSweepCycles 次),
/// 电网扫动:Prop 整体上下移动(幅度 netSweepAmplitude,最多 netSweepCycles 次),
/// 扫动过程中每帧检测 PropL~PropR 之间的矩形区域,碰到普通鱼 → 爆炸(Id_DieFish_FX)。
/// ⭐ 不需要一直按设定的圈数移动:面板分加满(=maxScore)后提前停止扫动。
/// </summary>
private IEnumerator ElectricNetSweep(GameObject propObj, Transform propL, Transform propR)
{
@@ -1020,14 +1049,21 @@ public class XingJiXunHangEvent : BJEventBase
Vector3 basePos = propObj.transform.position;
for (int i = 0; i < netSweepCycles; i++)
{
// ⭐ 面板分加满:提前停止扫动(不需要一直按圈数移动)
if (BossScoreFull)
{
Debug.Log($"[XingJiXunHang] 光子: 面板分已达上限 {maxScore},提前停止电网扫动");
break;
}
// 上扫
yield return StartCoroutine(SweepSegment(propObj, propL, propR, basePos + Vector3.up * netSweepAmplitude));
if (BossScoreFull) break;
// 下扫
yield return StartCoroutine(SweepSegment(propObj, propL, propR, basePos - Vector3.up * netSweepAmplitude));
}
// 回原位
yield return StartCoroutine(SweepSegment(propObj, propL, propR, basePos));
Debug.Log($"[XingJiXunHang] 光子: 电网扫动完成 cycles={netSweepCycles} 炸鱼数={_netKilledFish.Count}");
Debug.Log($"[XingJiXunHang] 光子: 电网扫动结束(计划{netSweepCycles}圈)炸鱼数={_netKilledFish.Count}");
}
/// <summary>电网从当前位置移动到 dest(逐帧插值 + 途中杀鱼)</summary>
@@ -1045,10 +1081,12 @@ public class XingJiXunHangEvent : BJEventBase
propObj.transform.position = Vector3.Lerp(start, dest, t);
if (netKillFish)
KillFishInNet(propL, propR);
// ⭐ 分数加满:本段提前结束
if (BossScoreFull) break;
yield return null;
}
propObj.transform.position = dest;
if (netKillFish)
if (netKillFish && !BossScoreFull)
KillFishInNet(propL, propR);
}
@@ -1075,10 +1113,16 @@ public class XingJiXunHangEvent : BJEventBase
Collider2D[] hits = Physics2D.OverlapAreaAll(min, max, netKillLayer);
if (hits == null || hits.Length == 0) return;
Vector3 sitPos = GetSeatPosition();
Vector3 turretPos = GetSeatPosition(); // 金币飞向炮台
int killed = 0;
foreach (var hit in hits)
{
// ⭐ 分数加满(=maxScore)后停止杀鱼
if (BossScoreFull)
{
Debug.Log($"[XingJiXunHang] 电网: 面板分已达上限 {maxScore},停止杀鱼");
break;
}
if (hit == null || !hit.CompareTag("Fish")) continue;
var monster = hit.GetComponentInParent<MonsterContral>();
if (monster == null) continue;
@@ -1089,7 +1133,9 @@ public class XingJiXunHangEvent : BJEventBase
{
Amount = paoBei * monster.monsterData.fishTypeConfig.fishScore
};
CreateFishManager.Instance.Id_DieFish_FX(monster.guid, coinNum, sitPos, isLocal: isSelf);
// ⭐ 飞机死亡掉金币(飞向炮台),分数同时加到 Boss 面板(封顶 maxScore)
CreateFishManager.Instance.Id_DieFish_FX(monster.guid, coinNum, turretPos, isLocal: isSelf);
AddBossPanelScore(coinNum.Amount);
killed++;
}
if (killed > 0)
@@ -1209,6 +1255,17 @@ public class XingJiXunHangEvent : BJEventBase
// 终点:激光缩到 0 长度(隐藏)
UpdateGammaLaserEndpoints(lasers, propObj.transform.position, 0f);
Debug.Log($"[XingJiXunHang] 伽马风暴: 旋转完成 {rotated:0.#}°(45°后激光缩短至消失)炸鱼 {killCount} 条");
// ⭐ 分不够就直接补齐到 maxScore(旋转杀鱼没杀满时,面板补满)
if (!BossScoreFull)
{
long need = maxScore - _bossPanelScore;
if (need > 0)
{
AddBossPanelScore(need);
Debug.Log($"[XingJiXunHang] 伽马风暴: 分不够,直接补齐 {need} → {_bossPanelScore}");
}
}
}
/// <summary>
@@ -1282,7 +1339,7 @@ public class XingJiXunHangEvent : BJEventBase
return;
}
Vector3 sitPos = GetSeatPosition();
Vector3 turretPos = GetSeatPosition(); // 金币飞向炮台
foreach (var laser in lasers)
{
if (laser == null) continue;
@@ -1299,6 +1356,12 @@ public class XingJiXunHangEvent : BJEventBase
foreach (var hit in hits)
{
// ⭐ 分数加满(=maxScore)后停止杀鱼
if (BossScoreFull)
{
Debug.Log($"[XingJiXunHang] 伽马射线: 面板分已达上限 {maxScore},停止杀鱼");
return;
}
if (hit.collider == null || !hit.collider.CompareTag("Fish")) continue;
var monster = hit.collider.GetComponentInParent<MonsterContral>();
if (monster == null) continue;
@@ -1309,7 +1372,9 @@ public class XingJiXunHangEvent : BJEventBase
{
Amount = paoBei * monster.monsterData.fishTypeConfig.fishScore
};
CreateFishManager.Instance.Id_DieFish_FX(monster.guid, coinNum, sitPos, isLocal: isSelf);
// ⭐ 飞机死亡掉金币(飞向炮台),分数同时加到 Boss 面板(封顶 maxScore)
CreateFishManager.Instance.Id_DieFish_FX(monster.guid, coinNum, turretPos, isLocal: isSelf);
AddBossPanelScore(coinNum.Amount);
killCount++;
}
}
@@ -1363,6 +1428,55 @@ public class XingJiXunHangEvent : BJEventBase
// ==================== 辅助方法 ====================
/// <summary>每死亡一个飞机,就往 Boss 分数面板上加一个分(面板独立于 Boss 本体,删除后仍有效)</summary>
private void AddBossPanelScore(long amount)
{
if (amount <= 0) return;
// ⭐ 面板加分不能超过金币上限 maxScore(封顶)
long newScore = _bossPanelScore + amount;
if (newScore > maxScore)
newScore = maxScore;
if (newScore == _bossPanelScore) return; // 已封顶,无变化
_bossPanelScore = newScore;
EnsureBossScoreTmp();
if (_bossScoreTmp != null)
{
_bossScoreTmp.text = _bossPanelScore.ToString();
Debug.Log($"[XingJiXunHang] Boss分数面板 +{amount} = {_bossPanelScore}(上限{maxScore})");
}
}
/// <summary>面板分是否已加满(= maxScore 后停止杀鱼)</summary>
private bool BossScoreFull => _bossPanelScore >= maxScore;
/// <summary>确保 Boss 分数面板 TMP 可用(优先用 bossCtrl 已创建的,否则手动创建)</summary>
private void EnsureBossScoreTmp()
{
if (_bossScoreTmp != null) return;
// 1. 从 bossCtrl 拿(Boss 删除前已缓存的面板引用)
if (bossCtrl != null && bossCtrl.tMP_CurrBossScore != null)
{
_bossScoreTmp = bossCtrl.tMP_CurrBossScore;
return;
}
// 2. 手动创建分数面板(同 MonsterControl_BossBase.EnsureScorePanel 逻辑)
var prefab = CreateFishManager.Instance != null
? CreateFishManager.Instance.dieFXPrefabAwardTmp : null;
var parent = CreateFishManager.Instance != null && CreateFishManager.Instance.currGameType != null
? CreateFishManager.Instance.currGameType.subPanelParent : null;
if (prefab == null || parent == null) return;
var board = Instantiate(prefab, parent);
// ⭐ 加分面板位置放在座位上方才是正常的
board.transform.position = GetSeatPosition() + Vector3.up * 150f;
_bossScoreTmp = board.transform.GetChild(0).GetComponent<TMPro.TextMeshProUGUI>();
}
/// <summary>
/// 缓存 UI 层父节点 + Canvas:
/// 父节点 = currGameType.subPanelParent.parent(与爆奖预制体同层,随 UI 卸载自动清理)
@@ -1548,6 +1662,25 @@ public class XingJiXunHangEvent : BJEventBase
EffectPool.Instance.Return(gameObject);
}
/// <summary>
/// 加分完成:先走基类(显示 BJEndUI + 加分 + 收尾),
/// 然后删除/回收手动创建的 Boss 分数面板(_bossScoreTmp)。
/// </summary>
protected override void OnScoreEnd()
{
base.OnScoreEnd(); // ⭐ 弹出 BJEndUI
// ⭐ 弹出 BJEndUI 时回收/删除手动创建的 Boss 分数面板
if (_bossScoreTmp != null)
{
var board = _bossScoreTmp.transform.parent;
if (board != null)
Destroy(board.gameObject);
_bossScoreTmp = null;
Debug.Log("[XingJiXunHang] 弹出 BJEndUI:Boss 分数面板已删除");
}
}
private void OnDestroy()
{
StopAllCoroutines();
@@ -1,5 +1,6 @@
using Assets.MyGame.Scripts.Net.WebResponses;
using Newtonsoft.Json.Linq;
using Spine.Unity;
using System;
using System.Collections;
using UnityEngine;
@@ -29,6 +30,23 @@ public class XingJiXunHang_BOSS : MonsterControl_BossBase
[Tooltip("非最终死亡后,Boss 恢复游动的延迟(秒)")]
public float resumeMoveDelay = 2f;
[Header("星际巡航 - 非最终死亡动画(animId=1)")]
[Tooltip("不死时播放的 Spine 动画名(stand02 = 第一种不死亡爆奖动画)")]
public string nonFinalDeathAnimationName = "stand02";
[Tooltip("正常默认动画名(stand01,不死动画播放完恢复游动时切回)")]
public string normalAnimationName = "stand01";
[Tooltip("SkeletonAnimation 所在子对象名(留空=自动 GetComponentInChildren 查找)")]
public string skeletonChildName = "";
[Header("星际巡航 - Hitting 特效")]
[Tooltip("Hitting 特效子对象路径(相对 Boss 根,如 body/bone/hitting)")]
public string hittingPath = "body/bone/hitting";
[Tooltip("Hitting 特效激活持续时间(秒,默认2)")]
public float hittingDuration = 2f;
[Header("星际巡航 - 服务端参数解析")]
[Tooltip("是否从 param 解析 isKill(关闭则用 maxDeathCount 客户端兜底判定)")]
public bool useServerParam = true;
@@ -67,6 +85,9 @@ public class XingJiXunHang_BOSS : MonsterControl_BossBase
/// <summary>爆奖事件是否已 Init(决定特效实例由谁回收)</summary>
private bool _awardInitCalled = false;
/// <summary>正在运行的 Hitting 特效协程(同一时间只允许一个,防连续触发冲突)</summary>
private Coroutine _hittingRoutine = null;
// ==================== Unity 生命周期 ====================
public override void OnEnable()
@@ -79,7 +100,33 @@ public class XingJiXunHang_BOSS : MonsterControl_BossBase
_removed = false;
_diePosCached = false;
_awardInitCalled = false;
_hittingRoutine = null;
currEvent = null;
// ⭐ 恢复被修改过的激活状态(对象池复用:Hitting 特效强制恢复初始关闭)
ResetHittingState();
}
/// <summary>恢复 Hitting 特效的初始激活状态(关闭)</summary>
private void ResetHittingState()
{
// 停掉残留的 Hitting 协程
if (_hittingRoutine != null)
{
StopCoroutine(_hittingRoutine);
_hittingRoutine = null;
}
Transform hitting = null;
if (!string.IsNullOrEmpty(hittingPath))
{
hitting = transform.Find(hittingPath);
if (hitting == null)
hitting = FindChildPathIgnoreCase(transform, hittingPath);
}
if (hitting != null)
hitting.gameObject.SetActive(false);
}
// ==================== 死亡逻辑 ====================
@@ -142,13 +189,21 @@ public class XingJiXunHang_BOSS : MonsterControl_BossBase
// ==================== 非最终死亡 ====================
/// <summary>非最终死亡:显示 BJEndUI,Boss 保留,等待下次击杀</summary>
/// <summary>非最终死亡:显示 BJEndUI + 播放不死动画,Boss 保留,等待下次击杀</summary>
private void NonFinalDeath(RoomKillNotice obj, int beishu)
{
_cachedKillNotice = obj;
Debug.Log($"[XingJiXunHang] 非最终死亡:显示 BJEndUI,Boss 保留");
// ⭐ 播放不死动画(SkeletonAnimation,从子对象获取)
PlayNonFinalDeathAnimation();
// ⭐ 播放 Hitting 特效:只激活一个协程(连续触发先停旧的,防止特效状态错乱)
if (_hittingRoutine != null)
StopCoroutine(_hittingRoutine);
_hittingRoutine = StartCoroutine(PlayHittingEffect());
// 显示 BJEndUI(倍数+分数)
ShowNonFinalDeathBjEndUI(obj, beishu);
@@ -177,6 +232,140 @@ public class XingJiXunHang_BOSS : MonsterControl_BossBase
isMove = true;
Debug.Log("[XingJiXunHang] 非最终死亡后恢复游动");
}
// ⭐ 不死动画(stand02)播完,切回正常默认动画(stand01)
PlaySkeletonAnimation(normalAnimationName);
}
/// <summary>获取 SkeletonAnimation(指定子对象或递归查找)并播放指定动画;动画不存在时回退 fallbackName</summary>
private void PlaySkeletonAnimation(string animationName, string fallbackName = null)
{
if (string.IsNullOrEmpty(animationName)) return;
SkeletonAnimation sa = null;
// 1. 指定子对象名 → 直接找该子对象上的组件
if (!string.IsNullOrEmpty(skeletonChildName))
{
Transform child = transform.Find(skeletonChildName);
if (child != null)
{
sa = child.GetComponent<SkeletonAnimation>();
if (sa == null)
sa = child.GetComponentInChildren<SkeletonAnimation>(true);
}
}
// 2. 递归查找:⭐ 优先选"包含目标动画"的 SkeletonAnimation(避免选错骨架)
if (sa == null)
{
var all = GetComponentsInChildren<SkeletonAnimation>(true);
Debug.Log($"[XingJiXunHang] 找到 {all.Length} 个 SkeletonAnimation,目标动画={animationName}");
foreach (var s in all)
{
// Skeleton.data 是 protected,改用 SkeletonDataAsset 公共 API
var sd = s.SkeletonDataAsset != null ? s.SkeletonDataAsset.GetSkeletonData(false) : null;
if (sd != null && sd.FindAnimation(animationName) != null)
{
sa = s;
Debug.Log($"[XingJiXunHang] 选择包含动画 {animationName} 的骨架: {s.gameObject.name}");
break;
}
}
if (sa == null && all.Length > 0)
{
sa = all[0];
Debug.LogWarning($"[XingJiXunHang] 没有骨架包含动画 {animationName},用第一个 {sa.gameObject.name}");
}
}
if (sa == null)
{
Debug.LogWarning($"[XingJiXunHang] 找不到 SkeletonAnimation(子对象={skeletonChildName}),跳过动画 {animationName}");
return;
}
// ⭐ 目标动画在骨架里不存在 → 回退 fallbackName(防预制体存旧动画名如 "hit")
var data = sa.SkeletonDataAsset != null ? sa.SkeletonDataAsset.GetSkeletonData(false) : null;
if (data != null && data.FindAnimation(animationName) == null)
{
if (!string.IsNullOrEmpty(fallbackName) && data.FindAnimation(fallbackName) != null)
{
Debug.LogWarning($"[XingJiXunHang] 动画 {animationName} 不存在,回退 {fallbackName}");
animationName = fallbackName;
}
else
{
var animNames = new System.Collections.Generic.List<string>();
foreach (var a in data.Animations) animNames.Add(a.Name);
Debug.LogWarning($"[XingJiXunHang] 骨架 {sa.gameObject.name} 可用动画: {string.Join(", ", animNames)}");
}
}
sa.AnimationName = animationName;
sa.timeScale = 1f;
Debug.Log($"[XingJiXunHang] 播放 Spine 动画: {animationName}(SkeletonAnimation on {sa.gameObject.name})");
}
/// <summary>播放不死动画(stand02 = 第一种不死亡爆奖动画;Inspector 旧值找不到时回退 stand02)</summary>
private void PlayNonFinalDeathAnimation()
{
PlaySkeletonAnimation(nonFinalDeathAnimationName, "stand02");
}
/// <summary>
/// 播放 Hitting 特效:激活子对象(body/bone/hitting),等待 hittingDuration 秒后关闭。
/// ⭐ transform.Find 大小写敏感,找不到时用大小写不敏感路径查找兜底(Body/bone/hitting)。
/// </summary>
private IEnumerator PlayHittingEffect()
{
Transform hitting = null;
if (!string.IsNullOrEmpty(hittingPath))
{
hitting = transform.Find(hittingPath);
if (hitting == null)
hitting = FindChildPathIgnoreCase(transform, hittingPath);
}
if (hitting == null)
{
Debug.LogWarning($"[XingJiXunHang] 找不到 Hitting 特效子对象 {hittingPath},跳过");
yield break;
}
hitting.gameObject.SetActive(true);
Debug.Log($"[XingJiXunHang] Hitting 特效激活({hittingPath} → {hitting.gameObject.name})");
yield return new WaitForSeconds(hittingDuration);
if (hitting != null)
hitting.gameObject.SetActive(false);
_hittingRoutine = null; // 协程结束,允许下一次触发
Debug.Log($"[XingJiXunHang] Hitting 特效关闭({hittingDuration}s 后)");
}
/// <summary>按路径逐级查找子对象(忽略大小写)</summary>
private Transform FindChildPathIgnoreCase(Transform root, string path)
{
if (root == null || string.IsNullOrEmpty(path)) return null;
Transform cur = root;
string[] parts = path.Split('/');
foreach (var p in parts)
{
Transform found = null;
for (int i = 0; i < cur.childCount; i++)
{
if (string.Equals(cur.GetChild(i).name, p, StringComparison.OrdinalIgnoreCase))
{
found = cur.GetChild(i);
break;
}
}
if (found == null) return null;
cur = found;
}
return cur;
}
/// <summary>非最终死亡:显示 BJEndUI</summary>
@@ -45,7 +45,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
bjEndUI: {fileID: -382508373279050920, guid: 75a52b8c1537cad49b3d521483fe134e, type: 3}
useScorePanel: 1
useScorePanel: 0
tMP_Score: {fileID: 0}
scorePanelPrefab: {fileID: 0}
currScore: 0
@@ -126,6 +126,8 @@ MonoBehaviour:
serializedVersion: 2
m_Bits: 64
ironFistKillFish: 1
ironFistKillTotalTime: 1
ironFistAwardDelay: 2
symmetraLockName: Lock
symmetraPropLName: PropL
symmetraPropRName: PropR
@@ -137,6 +139,7 @@ MonoBehaviour:
laser3Name: laser3
laser4Name: laser4
laserExpandSpeed: 0.6
symmetraPropHideDelay: 1
netSweepAmplitude: 20
netSweepCycles: 3
netSweepSpeed: 3