From 14b8fa60853fc30101a5d0bf514aafe3c562ba59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A3=AE2003?= <2384017988@qq.com> Date: Fri, 31 Jul 2026 17:57:24 +0800 Subject: [PATCH] 1 --- Assets/MyGame/Scripts/Turret/SitPosData.cs | 10 ++++ .../HHWK/Boss/XiaoBaiLong/Game177_lm.prefab | 2 +- .../aa/UI/Game/GameMainPanel_FishBase.cs | 52 ++++++++++++++----- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/Assets/MyGame/Scripts/Turret/SitPosData.cs b/Assets/MyGame/Scripts/Turret/SitPosData.cs index bd4bb2158..334db0fdc 100644 --- a/Assets/MyGame/Scripts/Turret/SitPosData.cs +++ b/Assets/MyGame/Scripts/Turret/SitPosData.cs @@ -100,6 +100,14 @@ public class SitPosData : MonoBehaviour public MonsterContral monCotl_Lock; public Transform monCotl_Lock_Position; public bool isLock = false; + /// + /// ⭐ 当前锁定是否为玩家手动锁定。 + /// - true:玩家点击锁定的鱼 → 不受路径进度(0.1~0.9)限制,鱼离场前一直锁定 + /// - false:自动锁定/三发查找/bot → 受路径进度限制 + /// 由 GameMainPanel_FishBase 在 SetLockTarget 前设置 + /// + [System.NonSerialized] + public bool isManualLock = false; public GameObject lockFX; // 主锁定图标(单发/双倍用) public List lockFX_Triple = new List(); // 三发锁定额外2个图标 public List tripleLockTargets = new List(); @@ -744,6 +752,8 @@ public class SitPosData : MonoBehaviour monCotl_Lock = null; monCotl_Lock_Position = null; isLock = false; + // ⭐ 清除锁定时重置手动锁定标记 + isManualLock = false; lockFX.SetActive(false); HideTripleLockIcons(); ChangeState(TurretState.Idle); diff --git a/Assets/MyGame/aa/Game/HHWK/Boss/XiaoBaiLong/Game177_lm.prefab b/Assets/MyGame/aa/Game/HHWK/Boss/XiaoBaiLong/Game177_lm.prefab index d8e74fd37..2a3f09b19 100644 --- a/Assets/MyGame/aa/Game/HHWK/Boss/XiaoBaiLong/Game177_lm.prefab +++ b/Assets/MyGame/aa/Game/HHWK/Boss/XiaoBaiLong/Game177_lm.prefab @@ -6043,7 +6043,7 @@ PrefabInstance: - target: {fileID: 9054138907369387089, guid: b91d0e921164f564a98591ba27f3c976, type: 3} propertyPath: m_IsActive - value: 1 + value: 0 objectReference: {fileID: 0} - target: {fileID: 9054138907369387089, guid: b91d0e921164f564a98591ba27f3c976, type: 3} diff --git a/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs b/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs index a73f6c780..f0df3cd2e 100644 --- a/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs +++ b/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs @@ -1373,6 +1373,12 @@ public class GameMainPanel_FishBase : GameMainPanel_Base private void FireBullet(SitPosData sit, float angle, bool isSanShe, bool isDouble, bool isLock, List bullitIDs, List fireTargetMonsterIDs = null, List bulletGolds = null) { + // ⭐ 防御:sit 或 bullitIDs 为空时直接返回,避免空引用 + if (sit == null || bullitIDs == null || bullitIDs.Count == 0) + { + Debug.LogWarning("[FireBullet] sit 或 bullitIDs 为空,跳过本次开火"); + return; + } Vector2 startPos = sit.GetBulletStartPos(); // ⭐ 按索引取该发子弹的服务端权威扣费金额(bullets[i].gold),没有则回退到0(ConsumeGold内部用consumeGoldNum) @@ -1401,7 +1407,9 @@ public class GameMainPanel_FishBase : GameMainPanel_Base { if (CreateFishManager.Instance.fishDatas_hashCodekey.TryGetValue(fireTargetMonsterIDs[i], out var m) && m != null && m.gameObject.activeInHierarchy - && IsFishPathLockable(m)) + // ⭐ 手动锁定的主目标(i==0 且是本地玩家主锁)不受路径进度限制 + && ((i == 0 && sit == currSitData && m == currSitData.monCotl_Lock && currSitData.isManualLock) + || IsFishPathLockable(m))) { targetMonster = m; targetHitPos = ResolveLockTransform(m); @@ -1425,9 +1433,11 @@ public class GameMainPanel_FishBase : GameMainPanel_Base } else if (isSanShe) { - CreateBullet(sit, startPos, angle - 20, isDouble, isLock, bullitIDs[0], null, null, GetBulletGold(0)); - CreateBullet(sit, startPos, angle, isDouble, isLock, bullitIDs[1], null, null, GetBulletGold(1)); - CreateBullet(sit, startPos, angle + 20, isDouble, isLock, bullitIDs[2], null, null, GetBulletGold(2)); + // ⭐ 安全取值:bullitIDs 不足3个时复用最后一个,避免越界 + string SafeBid(int i) => i < bullitIDs.Count ? bullitIDs[i] : bullitIDs[bullitIDs.Count - 1]; + CreateBullet(sit, startPos, angle - 20, isDouble, isLock, SafeBid(0), null, null, GetBulletGold(0)); + CreateBullet(sit, startPos, angle, isDouble, isLock, SafeBid(1), null, null, GetBulletGold(1)); + CreateBullet(sit, startPos, angle + 20, isDouble, isLock, SafeBid(2), null, null, GetBulletGold(2)); } else { @@ -1624,9 +1634,9 @@ public class GameMainPanel_FishBase : GameMainPanel_Base #region 路径进度过滤(锁定范围控制) /// bot/玩家锁定时,鱼路径进度下限:小于此值表示鱼刚出生,不锁定 - private const float BotLockPathMin = 0.2f; + private const float BotLockPathMin = 0.1f; /// bot/玩家锁定时,鱼路径进度上限:大于此值表示鱼即将离场,不锁定 - private const float BotLockPathMax = 0.8f; + private const float BotLockPathMax = 0.9f; /// /// 计算鱼在路径上的进度 [0, 1]。 @@ -1647,7 +1657,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base /// /// 判断鱼的路径进度是否在可锁定范围内 [BotLockPathMin, BotLockPathMax] - /// 刚出生(< 0.2)或即将离场(> 0.8)的鱼不能锁定,避免锁定后立即丢失目标 + /// 刚出生(< 0.1)或即将离场(> 0.9)的鱼不能锁定,避免锁定后立即丢失目标 + /// ⭐ 注意:本函数仅用于自动锁定/bot/三发查找;玩家手动锁定不调用此函数 /// private bool IsFishPathLockable(MonsterContral m) { @@ -1684,10 +1695,11 @@ public class GameMainPanel_FishBase : GameMainPanel_Base if (CreateFishManager.Instance == null) return result; - // ① 主锁目标排第一(打哪锁哪)—— 必须也满足路径可锁定,否则三发列表会被它污染 + // ① 主锁目标排第一(打哪锁哪) + // ⭐ 手动锁定:只看鱼是否还活着;自动锁定:还要满足路径可锁定 if (currSitData.isLock && currSitData.monCotl_Lock != null && currSitData.monCotl_Lock.gameObject.activeInHierarchy && - IsFishPathLockable(currSitData.monCotl_Lock)) + (currSitData.isManualLock || IsFishPathLockable(currSitData.monCotl_Lock))) { Transform pos = currSitData.monCotl_Lock_Position != null && currSitData.monCotl_Lock_Position.gameObject.activeInHierarchy ? currSitData.monCotl_Lock_Position @@ -1820,6 +1832,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base Transform lockTr = ResolveLockTransform(monster, out colliderIndex, true); // ⭐ lockPoint 和 colliderList 全部未激活 → 不锁定这条鱼 if (lockTr == null) return; + // ⭐ 标记为手动锁定:不受路径进度(0.1~0.9)限制,鱼离场前一直锁定 + currSitData.isManualLock = true; currSitData.SetLockTarget(monster, lockTr); SyncLock(new List { monster?.guid }, colliderIndex); @@ -1902,11 +1916,11 @@ public class GameMainPanel_FishBase : GameMainPanel_Base var monster = currSitData.monCotl_Lock; - // ⭐ 目标无效判定:被回收/销毁 OR 路径离场(progress > 0.8) - // 离场的鱼仍 active,但不应该继续锁定 + // ⭐ 目标无效判定:被回收/销毁 + // 路径进度检查仅对自动锁定生效;手动锁定不检查(玩家自己锁的鱼离场前一直跟) bool mainInvalid = monster == null || !monster.gameObject.activeInHierarchy - || !IsFishPathLockable(monster); + || (!currSitData.isManualLock && !IsFishPathLockable(monster)); if (mainInvalid) { @@ -1955,11 +1969,13 @@ public class GameMainPanel_FishBase : GameMainPanel_Base // 激活后,当锁定的鱼死亡,自动转换到下一个目标 if (!_autoLockActivated) return; - // 已经有锁定目标并且还活着且未离场,就不重复找 + // 已经有锁定目标并且还活着,就不重复找 + // ⭐ 手动锁定:只看鱼是否还活着,不检查路径进度(玩家自己锁的鱼离场前一直跟) + // 自动锁定:还要检查路径进度(超出 0.1~0.9 范围视为失效,换下一条) if (currSitData.isLock && currSitData.monCotl_Lock != null && currSitData.monCotl_Lock.gameObject.activeInHierarchy && - IsFishPathLockable(currSitData.monCotl_Lock)) + (currSitData.isManualLock || IsFishPathLockable(currSitData.monCotl_Lock))) { return; } @@ -1973,6 +1989,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base Transform lockTr = ResolveLockTransform(target, out colliderIndex); // ⭐ lockPoint 和 colliderList 全部未激活(出场效果中) → 不锁定,等下一帧再找 if (lockTr == null) return; + // ⭐ 标记为自动锁定:受路径进度(0.1~0.9)限制 + currSitData.isManualLock = false; currSitData.SetLockTarget(target, lockTr); SyncLock(new List { target?.guid }, colliderIndex); @@ -2348,6 +2366,12 @@ public class GameMainPanel_FishBase : GameMainPanel_Base } var data = FrameDataParser.Parse(sync.extra.frameData); + // ⭐ 边界检查:防止 sitPos 越界导致 IndexOutOfRangeException + if (sync.extra.sitPos < 0 || sync.extra.sitPos >= list_Sits.Count) + { + Debug.LogWarning($"[SyncGameData_FireBullit] sitPos 越界: {sync.extra.sitPos}"); + return; + } var targetSit = list_Sits[sync.extra.sitPos]; FireBullet(targetSit, data.angle, data.isSanShe, data.twoRTruuect, data.isSuoDing, sync.bulletIds, data.monsterIDs, bulletGolds); }