diff --git a/Assets/_Game/Scripts/Enemies/AIBrain/PerceptionStateMachine.cs b/Assets/_Game/Scripts/Enemies/AIBrain/PerceptionStateMachine.cs index 6805d4ce..19a5efef 100644 --- a/Assets/_Game/Scripts/Enemies/AIBrain/PerceptionStateMachine.cs +++ b/Assets/_Game/Scripts/Enemies/AIBrain/PerceptionStateMachine.cs @@ -52,7 +52,8 @@ namespace BaseGames.Enemies .To(c.Chase).When(x => x.Sensor.InChaseZone(), "InChaseZone") .To(c.Rest).When(x => !x.Sensor.InVisionZone(), "leftVision"); - // 追击:委托能力实现移动/伤害;脱离视野 → R(永不回警觉);退出时中断追击能力 + // 追击:委托能力实现移动/伤害;脱离**全部感知**(追逐区 且 视野区都不在) → R(永不回警觉);退出时中断追击能力。 + // 维持用"追逐区 OR 视野区"任一:视野(los)可能被墙挡而漏检,仅靠视野会导致玩家在近身追逐区内却反复进出追击(抖动)。 b.State(c.Chase) .OnEnter(x => { @@ -60,7 +61,7 @@ namespace BaseGames.Enemies if (!string.IsNullOrEmpty(c.ChaseAbilityId)) x.Combat.UseAbility(c.ChaseAbilityId); }) .OnExit(x => c.OnChaseExit?.Invoke(x)) - .To(c.Rest).When(x => !x.Sensor.InVisionZone(), "leftVision"); + .To(c.Rest).When(x => !x.Sensor.InChaseZone() && !x.Sensor.InVisionZone(), "leftAllZones"); b.State(c.Death).OnEnter(x => c.OnDeathEnter?.Invoke(x)); } diff --git a/Assets/_Game/Scripts/Enemies/EnemyMovement.cs b/Assets/_Game/Scripts/Enemies/EnemyMovement.cs index cd2c8689..5eddd415 100644 --- a/Assets/_Game/Scripts/Enemies/EnemyMovement.cs +++ b/Assets/_Game/Scripts/Enemies/EnemyMovement.cs @@ -306,10 +306,7 @@ namespace BaseGames.Enemies if (!_isTurning) _facingDir = transform.localScale.x >= 0f ? _spriteDefaultFacingDir : -_spriteDefaultFacingDir; - // NavDriving: TBM 直接写 transform.position,零速防止物理重力积累和双重驱动冲突。 - if (NavDriving) - _rb.velocity = Vector2.zero; - + // 物理原生移动:nav 只提供方向,移动由 velocity+重力执行(不再让 TBM 写 transform.position)。 IsGrounded = IsGroundedCheck(); WallAndLedgeCheck(); #if UNITY_EDITOR @@ -379,7 +376,6 @@ namespace BaseGames.Enemies { if (_isTurning) return; UpdateFacing(dir); - if (NavDriving) return; // TBM 驱动位置,仅更新朝向 var vel = _rb.velocity; vel.x = dir * _config.WalkSpeed; _rb.velocity = vel; @@ -390,7 +386,6 @@ namespace BaseGames.Enemies { if (_isTurning) return; UpdateFacing(dir); - if (NavDriving) return; // TBM 驱动位置,仅更新朝向 var vel = _rb.velocity; vel.x = dir * speed; _rb.velocity = vel; diff --git a/Assets/_Game/Scripts/Enemies/Navigation/EnemyNavAgent.cs b/Assets/_Game/Scripts/Enemies/Navigation/EnemyNavAgent.cs index e4a5c232..88480c25 100644 --- a/Assets/_Game/Scripts/Enemies/Navigation/EnemyNavAgent.cs +++ b/Assets/_Game/Scripts/Enemies/Navigation/EnemyNavAgent.cs @@ -76,6 +76,11 @@ namespace BaseGames.Enemies.Navigation _movement = GetComponent(); _enemyMovement = GetComponent(); + // 物理原生移动:禁用 TBM 段移动——不让它直接写 transform.position(会与 Dynamic 刚体物理冲突)。 + // 段上的水平移动改由 EnemyMovement 用 velocity 执行、刚体重力保持贴地(见 FixedUpdate)。 + if (_movement != null) + _movement.enabledFeatures &= ~TransformBasedMovement.FeatureFlags.SegmentMovement; + // 自动发现 INavLinkHandler 组件并注册(包含子对象) foreach (var handler in GetComponentsInChildren(true)) RegisterLinkHandler(handler); @@ -225,26 +230,33 @@ namespace BaseGames.Enemies.Navigation private void HandlePathFailed(NavAgent _) => OnNavPathFailed?.Invoke(); private void HandleGoalReached(NavAgent _) => OnGoalReached?.Invoke(); - // ── NavDriving 信号桥 ─────────────────────────────────────────── + // ── 物理原生移动桥 ───────────────────────────────────────────── /// - /// 每物理帧向 写入导航方向信号,并设置 NavDriving 标志。 - /// NavDriving=true 时 EnemyMovement 只更新朝向,TBM 保留对 transform.position 的控制权。 + /// 跟随路径段时:向 写入朝当前子目标的水平方向+速度(由 velocity+重力执行), + /// 并在到达子目标时推进导航到下一段(TBM 段移动已在 Awake 禁用,改由本组件推进)。 + /// 不直接写 transform.position → 与 Dynamic 刚体物理不冲突,保留击退/击飞/落地等物理反馈。 /// private void FixedUpdate() { if (_enemyMovement == null || _navAgent == null) return; bool onSegment = _navAgent.IsMovingOnSegment; - _enemyMovement.NavDriving = onSegment; + _enemyMovement.NavDriving = onSegment; // 状态标记(供调试/上层判断"是否导航中") if (onSegment) { + // 物理原生:朝子目标水平移动,由 EnemyMovement 施加 velocity、刚体重力保持贴地 float dx = _navAgent.PathSubGoal.x - transform.position.x; - _enemyMovement.PendingInput.MoveDir = Mathf.Abs(dx) > 0.01f ? Mathf.Sign(dx) : 0f; + _enemyMovement.PendingInput.MoveDir = Mathf.Abs(dx) > 0.05f ? Mathf.Sign(dx) : 0f; + _enemyMovement.PendingInput.MoveSpeed = _movement != null ? _movement.movementSpeed : 0f; + + // 段推进:到达当前子目标 → 推进到下一段(原由 TBM 负责,禁用后由本组件负责) + if (_navAgent.HasReachedCurrentSubGoal(0.25f)) + _navAgent.CompleteSegmentTraversal(); } else if (_wasNavOnSegment && !_navAgent.IsFollowingAPath) { - // 导航刚结束(到达目标 / 路径失败)→ 清除残留 MoveDir + // 导航刚结束(到达目标 / 路径失败)→ 停下水平移动 _enemyMovement.PendingInput.WantStop = true; }