diff --git a/DungeonShooting_Godot/src/framework/ActivityObject.cs b/DungeonShooting_Godot/src/framework/ActivityObject.cs index 6416fd1..4de5ab2 100644 --- a/DungeonShooting_Godot/src/framework/ActivityObject.cs +++ b/DungeonShooting_Godot/src/framework/ActivityObject.cs @@ -339,10 +339,15 @@ public void RemoveComponent(Component component) { - if (ContainsComponent(component)) + for (int i = 0; i < _components.Count; i++) { - component.OnUnMount(); - component._SetActivityObject(null); + if (_components[i].Value == component) + { + _components.RemoveAt(i); + component.OnUnMount(); + component._SetActivityObject(null); + return; + } } } @@ -381,10 +386,10 @@ { if (!temp.IsStart) { - temp.Start(); + temp.Ready(); } - temp.Update(delta); + temp.Process(delta); } } } @@ -509,10 +514,10 @@ { if (!temp.IsStart) { - temp.Start(); + temp.Ready(); } - temp.PhysicsUpdate(delta); + temp.PhysicsProcess(delta); } } } @@ -547,6 +552,7 @@ CallDeferred(nameof(Destroy)); } + //返回该组件是否被挂载到当前物体上 private bool ContainsComponent(Component component) { for (int i = 0; i < _components.Count; i++) diff --git a/DungeonShooting_Godot/src/framework/Component.cs b/DungeonShooting_Godot/src/framework/Component.cs index 3e1d828..16c81e0 100644 --- a/DungeonShooting_Godot/src/framework/Component.cs +++ b/DungeonShooting_Godot/src/framework/Component.cs @@ -30,6 +30,69 @@ } /// <summary> + /// 当前组件所挂载物体的缩放 + /// </summary> + public Vector2 Scale + { + get => ActivityObject.Scale; + set => ActivityObject.Scale = value; + } + + /// <summary> + /// 当前组件所挂载物体的全局缩放 + /// </summary> + public Vector2 GlobalScale + { + get => ActivityObject.GlobalScale; + set => ActivityObject.GlobalScale = value; + } + + /// <summary> + /// 当前组件所挂载物体的旋转角度 + /// </summary> + public float Rotation + { + get => ActivityObject.Rotation; + set => ActivityObject.Rotation = value; + } + + /// <summary> + /// 当前组件所挂载物体的全局旋转角度 + /// </summary> + public float GlobalRotation + { + get => ActivityObject.GlobalRotation; + set => ActivityObject.GlobalRotation = value; + } + + /// <summary> + /// 当前组件所挂载物体的角度制旋转角度 + /// </summary> + public float RotationDegrees + { + get => ActivityObject.RotationDegrees; + set => ActivityObject.RotationDegrees = value; + } + + /// <summary> + /// 当前组件所挂载物体的全局角度制旋转角度 + /// </summary> + public float GlobalRotationDegrees + { + get => ActivityObject.GlobalRotationDegrees; + set => ActivityObject.GlobalRotationDegrees = value; + } + + /// <summary> + /// 当前组件所挂载物体的ZIndex + /// </summary> + public int ZIndex + { + get => ActivityObject.ZIndex; + set => ActivityObject.ZIndex = value; + } + + /// <summary> /// 当前组件是否显示 /// </summary> public bool Visible @@ -52,9 +115,27 @@ public CollisionShape2D Collision => ActivityObject.Collision; /// <summary> - /// 是否启用当前组件 + /// 是否启用当前组件, 如果禁用, 则不会调用 Process 和 PhysicsProcess /// </summary> - public bool Enable { get; set; } = true; + public bool Enable + { + get => _enable; + set + { + if (!_enable && value) + { + OnEnable(); + } + else if (_enable && !value) + { + OnDisable(); + } + + _enable = value; + } + } + + private bool _enable = true; /// <summary> /// 是否被销毁 @@ -65,25 +146,25 @@ internal bool IsStart = false; /// <summary> - /// 第一次调用 Update 或 PhysicsUpdate 之前调用 + /// 第一次调用 Process 或 PhysicsProcess 之前调用 /// </summary> - public virtual void Start() + public virtual void Ready() { } /// <summary> - /// 如果启用了组件, 则每帧会调用一次 Update + /// 如果启用了组件, 则每帧会调用一次 Process /// </summary> /// <param name="delta"></param> - public virtual void Update(float delta) + public virtual void Process(float delta) { } /// <summary> - /// 如果启用了组件, 则每物理帧会调用一次 PhysicsUpdate + /// 如果启用了组件, 则每物理帧会调用一次 PhysicsProcess /// </summary> /// <param name="delta"></param> - public virtual void PhysicsUpdate(float delta) + public virtual void PhysicsProcess(float delta) { } @@ -109,6 +190,20 @@ } /// <summary> + /// 当组件启用时调用 + /// </summary> + public virtual void OnEnable() + { + } + + /// <summary> + /// 当组件禁用时调用 + /// </summary> + public virtual void OnDisable() + { + } + + /// <summary> /// 当组件销毁 /// </summary> public void Destroy() diff --git a/DungeonShooting_Godot/src/framework/IProcess.cs b/DungeonShooting_Godot/src/framework/IProcess.cs index bc64da8..3e5777f 100644 --- a/DungeonShooting_Godot/src/framework/IProcess.cs +++ b/DungeonShooting_Godot/src/framework/IProcess.cs @@ -7,10 +7,10 @@ /// <summary> /// 普通帧每帧调用 /// </summary> - void Update(float delta); + void Process(float delta); /// <summary> /// 物理帧每帧调用 /// </summary> - void PhysicsUpdate(float delta); + void PhysicsProcess(float delta); } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/Role.cs b/DungeonShooting_Godot/src/game/role/Role.cs index 875ff37..1387d2b 100644 --- a/DungeonShooting_Godot/src/game/role/Role.cs +++ b/DungeonShooting_Godot/src/game/role/Role.cs @@ -143,12 +143,17 @@ /// </summary> public ActivityObject LookTarget { get; set; } + /// <summary> + /// + /// </summary> + public StateCtr StateCtr { get; } + //初始缩放 - private Vector2 StartScele; + private Vector2 _startScale; //所有角色碰撞的道具 - private readonly List<ActivityObject> InteractiveItemList = new List<ActivityObject>(); + private readonly List<ActivityObject> _interactiveItemList = new List<ActivityObject>(); - private CheckInteractiveResult TempResultData; + private CheckInteractiveResult _tempResultData; /// <summary> /// 可以互动的道具 @@ -219,7 +224,7 @@ { base._Ready(); AnimationPlayer = GetNode<AnimationPlayer>("AnimationPlayer"); - StartScele = Scale; + _startScale = Scale; MountPoint = GetNode<MountRotation>("MountPoint"); MountPoint.Master = this; BackMountPoint = GetNode<Position2D>("BackMountPoint"); @@ -263,12 +268,12 @@ //检查可互动的道具 bool findFlag = false; - for (int i = 0; i < InteractiveItemList.Count; i++) + for (int i = 0; i < _interactiveItemList.Count; i++) { - var item = InteractiveItemList[i]; + var item = _interactiveItemList[i]; if (item == null) { - InteractiveItemList.RemoveAt(i--); + _interactiveItemList.RemoveAt(i--); } else { @@ -284,12 +289,12 @@ InteractiveItem = item; ChangeInteractiveItem(result); } - else if (result.ShowIcon != TempResultData.ShowIcon) //切换状态 + else if (result.ShowIcon != _tempResultData.ShowIcon) //切换状态 { ChangeInteractiveItem(result); } } - TempResultData = result; + _tempResultData = result; } } } @@ -321,7 +326,7 @@ if (Holster.PickupWeapon(weapon) != -1) { //从可互动队列中移除 - InteractiveItemList.Remove(weapon); + _interactiveItemList.Remove(weapon); } } @@ -421,12 +426,12 @@ if (face == FaceDirection.Right) { RotationDegrees = 0; - Scale = StartScele; + Scale = _startScale; } else { RotationDegrees = 180; - Scale = new Vector2(StartScele.x, -StartScele.y); + Scale = new Vector2(_startScale.x, -_startScale.y); } } } @@ -458,9 +463,9 @@ ActivityObject propObject = other.AsActivityObject(); if (propObject != null) { - if (!InteractiveItemList.Contains(propObject)) + if (!_interactiveItemList.Contains(propObject)) { - InteractiveItemList.Add(propObject); + _interactiveItemList.Add(propObject); } } } @@ -474,9 +479,9 @@ ActivityObject propObject = other.AsActivityObject(); if (propObject != null) { - if (InteractiveItemList.Contains(propObject)) + if (_interactiveItemList.Contains(propObject)) { - InteractiveItemList.Remove(propObject); + _interactiveItemList.Remove(propObject); } if (InteractiveItem == propObject) { diff --git a/DungeonShooting_Godot/src/game/role/state/IState.cs b/DungeonShooting_Godot/src/game/role/state/IState.cs new file mode 100644 index 0000000..8276dd0 --- /dev/null +++ b/DungeonShooting_Godot/src/game/role/state/IState.cs @@ -0,0 +1,44 @@ +/// <summary> +/// 状态接口 +/// </summary> +public interface IState +{ + /// <summary> + /// 当前状态对象对应的状态枚举类型 + /// </summary> + StateEnum StateType { get; } + + /// <summary> + /// 当前状态对象挂载的角色对象 + /// </summary> + Role Role { get; set; } + + /// <summary> + /// 当前状态对象所处的状态机对象 + /// </summary> + StateCtr StateController { get; set; } + + /// <summary> + /// 当从其他状态进入到当前状态时调用 + /// </summary> + /// <param name="prev">上一个状态类型</param> + /// <param name="args">切换当前状态时附带的参数</param> + void Enter(StateEnum prev, params object[] args); + + /// <summary> + /// 物理帧每帧更新 + /// </summary> + void PhysicsProcess(float delta); + + /// <summary> + /// 是否允许切换至下一个状态 + /// </summary> + /// <param name="next">下一个状态类型</param> + bool CanChangeState(StateEnum next); + + /// <summary> + /// 从当前状态退出时调用 + /// </summary> + /// <param name="next">下一个状态类型</param> + void Exit(StateEnum next); +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/state/StateCtr.cs b/DungeonShooting_Godot/src/game/role/state/StateCtr.cs new file mode 100644 index 0000000..d0305ce --- /dev/null +++ b/DungeonShooting_Godot/src/game/role/state/StateCtr.cs @@ -0,0 +1,109 @@ +using Godot; +using System.Collections.Generic; + +/// <summary> +/// 角色状态机控制器 +/// </summary> +public class StateCtr : Component +{ + /// <summary> + /// 当前活跃的状态 + /// </summary> + public IState CurrState => _currState; + private IState _currState; + + /// <summary> + /// 负责存放状态实例对象 + /// </summary> + private readonly Dictionary<StateEnum, IState> _states = new Dictionary<StateEnum, IState>(); + + /// <summary> + /// 记录下当前帧是否有改变的状态 + /// </summary> + private bool _isChangeState; + + public override void PhysicsProcess(float delta) + { + _isChangeState = false; + if (CurrState != null) + { + CurrState.PhysicsProcess(delta); + //判断当前帧是否有改变的状态, 如果有, 则重新调用 PhysicsProcess() 方法 + if (_isChangeState) + { + PhysicsProcess(delta); + } + } + } + + /// <summary> + /// 往状态机力注册一个新的状态 + /// </summary> + public void Register(IState state) + { + if (GetStateInstance(state.StateType) != null) + { + GD.PrintErr("当前状态已经在状态机中注册:", state); + return; + } + state.Role = ActivityObject as Role; + state.StateController = this; + _states.Add(state.StateType, state); + } + + /// <summary> + /// 立即切换到下一个指定状态, 并且这一帧会被调用 PhysicsProcess + /// </summary> + public void ChangeState(StateEnum next, params object[] arg) + { + _changeState(false, next, arg); + } + + /// <summary> + /// 切换到下一个指定状态, 下一帧才会调用 PhysicsProcess + /// </summary> + public void ChangeStateLate(StateEnum next, params object[] arg) + { + _changeState(true, next, arg); + } + + /// <summary> + /// 根据状态类型获取相应的状态对象 + /// </summary> + private IState GetStateInstance(StateEnum stateType) + { + _states.TryGetValue(stateType, out var v); + return v; + } + + /// <summary> + /// 切换状态 + /// </summary> + private void _changeState(bool late, StateEnum next, params object[] arg) + { + if (_currState != null && _currState.StateType == next) + { + return; + } + var newState = GetStateInstance(next); + if (newState == null) + { + GD.PrintErr("当前状态机未找到相应状态:" + next); + return; + } + if (_currState == null) + { + _currState = newState; + newState.Enter(StateEnum.None, arg); + } + else if (_currState.CanChangeState(next)) + { + _isChangeState = !late; + var prev = _currState.StateType; + _currState.Exit(next); + GD.Print("nextState => " + next); + _currState = newState; + _currState.Enter(prev, arg); + } + } +} \ No newline at end of file diff --git a/DungeonShooting_Godot/src/game/role/state/StateEnum.cs b/DungeonShooting_Godot/src/game/role/state/StateEnum.cs new file mode 100644 index 0000000..151b523 --- /dev/null +++ b/DungeonShooting_Godot/src/game/role/state/StateEnum.cs @@ -0,0 +1,8 @@ + +public enum StateEnum +{ + None = 0, + Idle = 1, + Run = 2, + Move = 3, +} \ No newline at end of file