- Enhanced Physics2D layer collision report with new interactions between Player and Enemy layers. - Refactored BD_InvestigateLastKnown to streamline animation handling and improve readability. - Simplified BD_MaintainCombatDistance by consolidating movement stop logic. - Updated BD_MoveToPlayer to set AI phase on start. - Improved BD_Patrol logic with better handling of stuck states and path failures. - Enhanced BD_PatrolWaypoints to manage stuck conditions and retry logic more effectively. - Refined BD_ReturnToHome to remove unnecessary animation calls. - Updated BD_WalkRandom to ensure AI phase is set correctly on start. - Improved EnemyAbilityBase to delegate target facing to the movement system. - Enhanced EnemyBase with new movement methods for better control. - Refactored EnemyMovement to introduce a new input system for handling movement and facing. - Added EnemyMoveInput struct to encapsulate movement intentions. - Updated Physics2DSettings to reflect new layer collision matrix. - Introduced RTK CLI instructions for optimized command usage.
163 lines
5.9 KiB
C#
163 lines
5.9 KiB
C#
#if GRAPH_DESIGNER
|
||
using UnityEngine;
|
||
using Opsive.BehaviorDesigner.Runtime.Tasks;
|
||
using Opsive.BehaviorDesigner.Runtime.Tasks.Actions;
|
||
using BaseGames.Enemies;
|
||
|
||
namespace BaseGames.Enemies.AI
|
||
{
|
||
/// <summary>
|
||
/// BD Action:多步搜查(Navigate → LookAround → RandomWalk × N)。
|
||
///
|
||
/// <list type="bullet">
|
||
/// <item><b>Navigate</b>:导航至最后可见位置。</item>
|
||
/// <item><b>LookAround</b>:到达后原地环顾(播放 Look 动画,持续 LookAroundDuration)。</item>
|
||
/// <item><b>WalkRandom</b>:向搜查半径内的随机点移动,重复 RandomStepCount 次。</item>
|
||
/// <item>任意阶段重新发现玩家 → 返回 Failure,BD 树重入追击。</item>
|
||
/// <item>所有步骤完成仍未发现 → 返回 Success,BD 树归位/恢复巡逻。</item>
|
||
/// </list>
|
||
/// </summary>
|
||
[TaskName("Investigate Last Known Pos")]
|
||
[TaskCategory("BaseGames/Enemy/Movement")]
|
||
[TaskDescription("多步搜查:导航到最后已知位置 → 环顾四周 → 随机游走 N 次")]
|
||
public sealed class BD_InvestigateLastKnown : Action
|
||
{
|
||
[Tooltip("到达搜查点后环顾的时长(s)")]
|
||
[SerializeField] private float m_LookAroundDuration = 0.8f;
|
||
|
||
[Tooltip("随机行走步数(完成环顾后随机游荡的次数)")]
|
||
[SerializeField] [Min(0)] private int m_RandomStepCount = 2;
|
||
|
||
[Tooltip("随机行走半径(m)")]
|
||
[SerializeField] private float m_SearchRadius = 2.5f;
|
||
|
||
[Tooltip("到达目标点的判定半径(m)")]
|
||
[SerializeField] private float m_ArriveRadius = 0.6f;
|
||
|
||
private enum InvestigateSubStep { Navigate, LookAround, WalkRandom }
|
||
|
||
private EnemyBase _enemy;
|
||
private InvestigateSubStep _step;
|
||
private float _stepTimer;
|
||
private int _stepsRemaining;
|
||
private Vector2 _randomTarget;
|
||
private bool _pathFailed;
|
||
private bool _subscribed;
|
||
|
||
public override void OnAwake() => _enemy = gameObject.GetComponent<EnemyBase>();
|
||
|
||
public override void OnStart()
|
||
{
|
||
_enemy.SetAiPhase(AiPhase.Investigate);
|
||
_step = InvestigateSubStep.Navigate;
|
||
_stepTimer = 0f;
|
||
_stepsRemaining = m_RandomStepCount;
|
||
_pathFailed = false;
|
||
|
||
if (!_subscribed && _enemy.Nav != null)
|
||
{
|
||
_enemy.Nav.OnNavPathFailed += HandlePathFailed;
|
||
_subscribed = true;
|
||
}
|
||
|
||
_enemy.MoveTo(_enemy.LastKnownPlayerPosition);
|
||
}
|
||
|
||
public override TaskStatus OnUpdate()
|
||
{
|
||
if (_enemy == null) return TaskStatus.Failure;
|
||
|
||
if (_enemy.IsPlayerVisible()) return TaskStatus.Failure;
|
||
|
||
switch (_step)
|
||
{
|
||
case InvestigateSubStep.Navigate: return UpdateNavigate();
|
||
case InvestigateSubStep.LookAround: return UpdateLookAround();
|
||
case InvestigateSubStep.WalkRandom: return UpdateWalkRandom();
|
||
default: return TaskStatus.Success;
|
||
}
|
||
}
|
||
|
||
public override void OnEnd()
|
||
{
|
||
if (_subscribed && _enemy?.Nav != null)
|
||
_enemy.Nav.OnNavPathFailed -= HandlePathFailed;
|
||
_subscribed = false;
|
||
_enemy?.StopMovement();
|
||
}
|
||
|
||
// ── 阶段逻辑 ────────────────────────────────────────────────────
|
||
private TaskStatus UpdateNavigate()
|
||
{
|
||
Vector2 self = _enemy.transform.position;
|
||
bool arrived = _pathFailed ||
|
||
(self - _enemy.LastKnownPlayerPosition).sqrMagnitude <= m_ArriveRadius * m_ArriveRadius;
|
||
|
||
if (!arrived) return TaskStatus.Running;
|
||
|
||
_enemy.StopMovement();
|
||
EnterLookAround();
|
||
return TaskStatus.Running;
|
||
}
|
||
|
||
private TaskStatus UpdateLookAround()
|
||
{
|
||
_stepTimer += Time.deltaTime;
|
||
if (_stepTimer < m_LookAroundDuration) return TaskStatus.Running;
|
||
|
||
if (_stepsRemaining > 0)
|
||
EnterRandomWalk();
|
||
else
|
||
return TaskStatus.Success;
|
||
|
||
return TaskStatus.Running;
|
||
}
|
||
|
||
private TaskStatus UpdateWalkRandom()
|
||
{
|
||
Vector2 self = _enemy.transform.position;
|
||
bool arrived = _pathFailed ||
|
||
(self - _randomTarget).sqrMagnitude <= m_ArriveRadius * m_ArriveRadius;
|
||
|
||
if (!arrived) return TaskStatus.Running;
|
||
|
||
_enemy.StopMovement();
|
||
_stepsRemaining--;
|
||
_pathFailed = false;
|
||
|
||
if (_stepsRemaining > 0)
|
||
EnterRandomWalk();
|
||
else
|
||
return TaskStatus.Success;
|
||
|
||
return TaskStatus.Running;
|
||
}
|
||
|
||
// ── 辅助方法 ────────────────────────────────────────────────────
|
||
private void EnterLookAround()
|
||
{
|
||
_step = InvestigateSubStep.LookAround;
|
||
_stepTimer = 0f;
|
||
_enemy.BeginLookAround();
|
||
}
|
||
|
||
private void EnterRandomWalk()
|
||
{
|
||
_step = InvestigateSubStep.WalkRandom;
|
||
_pathFailed = false;
|
||
|
||
Vector2 origin = _enemy.LastKnownPlayerPosition;
|
||
// 横板地形中只在水平方向随机偏移,保留 origin.y;
|
||
// PathBerserker2d 寻路负责处理跨平台的纵向路径规划。
|
||
float dir = Random.value > 0.5f ? 1f : -1f;
|
||
float dist = Random.Range(0.5f, m_SearchRadius);
|
||
_randomTarget = new Vector2(origin.x + dir * dist, origin.y);
|
||
|
||
_enemy.MoveTo(_randomTarget);
|
||
}
|
||
|
||
private void HandlePathFailed() => _pathFailed = true;
|
||
}
|
||
}
|
||
#endif
|