diff --git a/DungeonShooting_Godot/src/game/role/IState.cs b/DungeonShooting_Godot/src/game/role/IState.cs deleted file mode 100644 index 0a7c2b0..0000000 --- a/DungeonShooting_Godot/src/game/role/IState.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System;/// -/// 状态接口 -/// -public interface IState where T : ActivityObject where S : Enum -{ - /// - /// 当前状态对象对应的状态枚举类型 - /// - S StateType { get; } - - /// - /// 当前状态对象挂载的角色对象 - /// - T Master { get; set; } - - /// - /// 当前状态对象所处的状态机对象 - /// - StateController StateController { get; set; } - - /// - /// 当从其他状态进入到当前状态时调用 - /// - /// 上一个状态类型 - /// 切换当前状态时附带的参数 - void Enter(S prev, params object[] args); - - /// - /// 物理帧每帧更新 - /// - void PhysicsProcess(float delta); - - /// - /// 是否允许切换至下一个状态 - /// - /// 下一个状态类型 - bool CanChangeState(S next); - - /// - /// 从当前状态退出时调用 - /// - /// 下一个状态类型 - void Exit(S next); -} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/StateBase.cs b/DungeonShooting_Godot/src/game/role/StateBase.cs new file mode 100644 index 0000000..531f049 --- /dev/null +++ b/DungeonShooting_Godot/src/game/role/StateBase.cs @@ -0,0 +1,63 @@ +using System; + +/// +/// 状态基类 +/// +public abstract class StateBase where T : ActivityObject where S : Enum +{ + /// + /// 当前状态对象对应的状态枚举类型 + /// + public S StateType { get; } + + /// + /// 当前状态对象挂载的角色对象 + /// + public T Master { get; set; } + + /// + /// 当前状态对象所处的状态机对象 + /// + public StateController StateController { get; set; } + + public StateBase(S state) + { + StateType = state; + } + + /// + /// 当从其他状态进入到当前状态时调用 + /// + /// 上一个状态类型 + /// 切换当前状态时附带的参数 + public virtual void Enter(S prev, params object[] args) + { + + } + + /// + /// 物理帧每帧更新 + /// + public virtual void PhysicsProcess(float delta) + { + + } + + /// + /// 是否允许切换至下一个状态 + /// + /// 下一个状态类型 + public virtual bool CanChangeState(S next) + { + return true; + } + + /// + /// 从当前状态退出时调用 + /// + /// 下一个状态类型 + public virtual void Exit(S next) + { + + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/StateController.cs b/DungeonShooting_Godot/src/game/role/StateController.cs index 18943c0..ad9557d 100644 --- a/DungeonShooting_Godot/src/game/role/StateController.cs +++ b/DungeonShooting_Godot/src/game/role/StateController.cs @@ -10,13 +10,13 @@ /// /// 当前活跃的状态 /// - public IState CurrState => _currState; - private IState _currState; + public StateBase CurrStateBase => _currStateBase; + private StateBase _currStateBase; /// /// 负责存放状态实例对象 /// - private readonly Dictionary> _states = new Dictionary>(); + private readonly Dictionary> _states = new Dictionary>(); /// /// 记录下当前帧是否有改变的状态 @@ -26,9 +26,9 @@ public override void PhysicsProcess(float delta) { _isChangeState = false; - if (CurrState != null) + if (CurrStateBase != null) { - CurrState.PhysicsProcess(delta); + CurrStateBase.PhysicsProcess(delta); //判断当前帧是否有改变的状态, 如果有, 则重新调用 PhysicsProcess() 方法 if (_isChangeState) { @@ -40,16 +40,16 @@ /// /// 往状态机力注册一个新的状态 /// - public void Register(IState state) + public void Register(StateBase stateBase) { - if (GetStateInstance(state.StateType) != null) + if (GetStateInstance(stateBase.StateType) != null) { - GD.PrintErr("当前状态已经在状态机中注册:", state); + GD.PrintErr("当前状态已经在状态机中注册:", stateBase); return; } - state.Master = ActivityObject as T; - state.StateController = this; - _states.Add(state.StateType, state); + stateBase.Master = ActivityObject as T; + stateBase.StateController = this; + _states.Add(stateBase.StateType, stateBase); } /// @@ -71,7 +71,7 @@ /// /// 根据状态类型获取相应的状态对象 /// - private IState GetStateInstance(S stateType) + private StateBase GetStateInstance(S stateType) { _states.TryGetValue(stateType, out var v); return v; @@ -82,7 +82,7 @@ /// private void _changeState(bool late, S next, params object[] arg) { - if (_currState != null && _currState.StateType.Equals(next)) + if (_currStateBase != null && _currStateBase.StateType.Equals(next)) { return; } @@ -92,19 +92,19 @@ GD.PrintErr("当前状态机未找到相应状态:" + next); return; } - if (_currState == null) + if (_currStateBase == null) { - _currState = newState; + _currStateBase = newState; newState.Enter(default, arg); } - else if (_currState.CanChangeState(next)) + else if (_currStateBase.CanChangeState(next)) { _isChangeState = !late; - var prev = _currState.StateType; - _currState.Exit(next); + var prev = _currStateBase.StateType; + _currStateBase.Exit(next); GD.Print("nextState => " + next); - _currState = newState; - _currState.Enter(prev, arg); + _currStateBase = newState; + _currStateBase.Enter(prev, arg); } } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs index 6e3eaa8..6d30caa 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs @@ -38,10 +38,16 @@ /// 视野检测射线, 朝玩家打射线, 检测是否碰到墙 /// public RayCast2D ViewRay { get; } + + /// + /// 导航代理 + /// + public NavigationAgent2D NavigationAgent2D { get; } - private Position2D _navigationPoint; - private NavigationAgent2D _navigationAgent2D; - private float _navigationUpdateTimer = 0; + /// + /// 导航代理中点 + /// + public Position2D NavigationPoint { get; } public Enemy() : base(ResourcePath.prefab_role_Enemy_tscn) { @@ -58,15 +64,15 @@ //视野射线 ViewRay = GetNode("ViewRay"); - _navigationPoint = GetNode("NavigationPoint"); - _navigationAgent2D = _navigationPoint.GetNode("NavigationAgent2D"); + NavigationPoint = GetNode("NavigationPoint"); + NavigationAgent2D = NavigationPoint.GetNode("NavigationAgent2D"); //PathSign = new PathSign(this, PathSignLength, GameApplication.Instance.Room.Player); //注册Ai状态机 - StateController.Register(new AINormalState()); - StateController.Register(new AIProbeState()); - StateController.Register(new AITailAfterState()); + StateController.Register(new AiNormalStateBase()); + StateController.Register(new AiProbeStateBase()); + StateController.Register(new AiTailAfterStateBase()); } public override void _Ready() @@ -75,7 +81,7 @@ //默认状态 StateController.ChangeState(AIStateEnum.AINormal); - _navigationAgent2D.SetTargetLocation(GameApplication.Instance.Room.Player.GlobalPosition); + NavigationAgent2D.SetTargetLocation(GameApplication.Instance.Room.Player.GlobalPosition); } /// diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AINormalState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AINormalState.cs deleted file mode 100644 index 45b13c2..0000000 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AINormalState.cs +++ /dev/null @@ -1,40 +0,0 @@ - -using Godot; - -/// -/// AI 正常状态 -/// -public class AINormalState : IState -{ - public AIStateEnum StateType { get; } = AIStateEnum.AINormal; - public Enemy Master { get; set; } - public StateController StateController { get; set; } - public void Enter(AIStateEnum prev, params object[] args) - { - - } - - public void PhysicsProcess(float delta) - { - //检测玩家 - var player = GameApplication.Instance.Room.Player; - //玩家中心点坐标 - var playerPos = player.MountPoint.GlobalPosition; - - if (Master.IsInViewRange(playerPos) && Master.TestViewRayCast(playerPos) == null) - { - //发现玩家 - StateController.ChangeStateLate(AIStateEnum.AITailAfter); - } - } - - public bool CanChangeState(AIStateEnum next) - { - return true; - } - - public void Exit(AIStateEnum next) - { - - } -} diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AIProbeState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AIProbeState.cs deleted file mode 100644 index 368947d..0000000 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AIProbeState.cs +++ /dev/null @@ -1,29 +0,0 @@ - -/// -/// Ai 不确定玩家位置 -/// -public class AIProbeState : IState -{ - public AIStateEnum StateType { get; } = AIStateEnum.AIProbe; - public Enemy Master { get; set; } - public StateController StateController { get; set; } - public void Enter(AIStateEnum prev, params object[] args) - { - - } - - public void PhysicsProcess(float delta) - { - - } - - public bool CanChangeState(AIStateEnum next) - { - return true; - } - - public void Exit(AIStateEnum next) - { - - } -} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AITailAfterState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AITailAfterState.cs deleted file mode 100644 index 0c1417e..0000000 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AITailAfterState.cs +++ /dev/null @@ -1,29 +0,0 @@ - -/// -/// AI 发现玩家 -/// -public class AITailAfterState : IState -{ - public AIStateEnum StateType { get; } = AIStateEnum.AITailAfter; - public Enemy Master { get; set; } - public StateController StateController { get; set; } - public void Enter(AIStateEnum prev, params object[] args) - { - - } - - public void PhysicsProcess(float delta) - { - - } - - public bool CanChangeState(AIStateEnum next) - { - return true; - } - - public void Exit(AIStateEnum next) - { - - } -} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalStateBase.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalStateBase.cs new file mode 100644 index 0000000..65a3148 --- /dev/null +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalStateBase.cs @@ -0,0 +1,26 @@ + +using Godot; + +/// +/// AI 正常状态 +/// +public class AiNormalStateBase : StateBase +{ + public AiNormalStateBase() : base(AIStateEnum.AINormal) + { + } + + public override void PhysicsProcess(float delta) + { + //检测玩家 + var player = GameApplication.Instance.Room.Player; + //玩家中心点坐标 + var playerPos = player.MountPoint.GlobalPosition; + + if (Master.IsInViewRange(playerPos) && Master.TestViewRayCast(playerPos) == null) + { + //发现玩家 + StateController.ChangeStateLate(AIStateEnum.AITailAfter); + } + } +} diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiProbeStateBase.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiProbeStateBase.cs new file mode 100644 index 0000000..be9854d --- /dev/null +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiProbeStateBase.cs @@ -0,0 +1,10 @@ + +/// +/// Ai 不确定玩家位置 +/// +public class AiProbeStateBase : StateBase +{ + public AiProbeStateBase() : base(AIStateEnum.AIProbe) + { + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterStateBase.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterStateBase.cs new file mode 100644 index 0000000..725bf8e --- /dev/null +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterStateBase.cs @@ -0,0 +1,43 @@ + +/// +/// AI 发现玩家 +/// +public class AiTailAfterStateBase : StateBase +{ + + private float _navigationUpdateTimer = 0; + + public AiTailAfterStateBase() : base(AIStateEnum.AITailAfter) + { + } + + public override void PhysicsProcess(float delta) + { + //跟随玩家 + var master = Master; + if (master.NavigationAgent2D.IsNavigationFinished()) + { + return; + } + var playerGlobalPosition = GameApplication.Instance.Room.Player.GlobalPosition; + //临时处理, 让敌人跟随玩家 + if (_navigationUpdateTimer <= 0) + { + _navigationUpdateTimer = 0.2f; + if (master.NavigationAgent2D.GetTargetLocation() != playerGlobalPosition) + { + master.NavigationAgent2D.SetTargetLocation(playerGlobalPosition); + } + } + else + { + _navigationUpdateTimer -= delta; + } + + var nextPos = master.NavigationAgent2D.GetNextLocation(); + master.LookTargetPosition(playerGlobalPosition); + master.AnimatedSprite.Animation = AnimatorNames.Run; + master.Velocity = (nextPos - master.GlobalPosition - master.NavigationPoint.Position).Normalized() * master.MoveSpeed; + master.CalcMove(delta); + } +} \ No newline at end of file