diff --git a/DungeonShooting_Godot/src/framework/ExternalForce.cs b/DungeonShooting_Godot/src/framework/ExternalForce.cs
new file mode 100644
index 0000000..a3c3225
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/ExternalForce.cs
@@ -0,0 +1,35 @@
+
+using Godot;
+
+///
+/// 物体所受到的外力的描述对象
+///
+public class ExternalForce
+{
+ ///
+ /// 当前力的名称
+ ///
+ public string Name { get; }
+
+ ///
+ /// 是否启用这个力对象, 如果禁用, 则该力对象则不会参与到运动计算当中, 而且不会调用 PhysicsProcess 方法
+ ///
+ public bool Enable { get; set; } = true;
+
+ ///
+ /// 当前力的速率
+ ///
+ public Vector2 Velocity { get; set; } = Vector2.Zero;
+
+ ///
+ /// 物理帧更新
+ ///
+ public virtual void PhysicsProcess(float delta)
+ {
+ }
+
+ public ExternalForce(string name)
+ {
+ Name = name;
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/MoveController.cs b/DungeonShooting_Godot/src/framework/MoveController.cs
new file mode 100644
index 0000000..e7f28aa
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/MoveController.cs
@@ -0,0 +1,147 @@
+
+using System.Collections.Generic;
+using Godot;
+
+public class MoveController : Component
+{
+ ///
+ /// 玩家受到的外力的集合
+ ///
+ private readonly Dictionary _forceData = new Dictionary();
+
+ ///
+ /// 这个速度就是玩家当前物理帧移动的真实速率
+ /// 该速度就是 BasisVelocity + ForceVelocity
+ ///
+ public Vector2 Velocity => _velocity;
+ private Vector2 _velocity = Vector2.Zero;
+
+ ///
+ /// 玩家的基础移动速率, 包括左右移动, 下坠, 上升等
+ ///
+ public Vector2 BasisVelocity { get => _basisVelocity; set => _basisVelocity = value; }
+ private Vector2 _basisVelocity = Vector2.Zero;
+
+ ///
+ /// 玩家向上的方向
+ ///
+ private readonly Vector2 _upDir = Vector2.Up;
+
+ ///
+ /// 根据名称添加一个外力, 并返回创建的外力的对象, 如果存在这个名称的外力, 移除之前的外力
+ ///
+ public ExternalForce AddForce(string name)
+ {
+ var f = new ExternalForce(name);
+ AddForce(f);
+ return f;
+ }
+
+ ///
+ /// 根据对象添加一个外力力, 如果存在这个名称的外力, 移除之前的外力
+ ///
+ public T AddForce(T force) where T : ExternalForce
+ {
+ _forceData.Remove(force.Name);
+ _forceData.Add(force.Name, force);
+ return force;
+ }
+
+ ///
+ /// 根据名称移除一个外力
+ ///
+ public void RemoveForce(string name)
+ {
+ if (!_forceData.Remove(name))
+ {
+ GD.PrintErr($"力:{name}不存在!!!");
+ }
+ }
+
+ ///
+ /// 根据名称获取一个外力
+ ///
+ public ExternalForce GetForce(string name)
+ {
+ _forceData.TryGetValue(name, out var f);
+ return f;
+ }
+
+ ///
+ /// 检车是否有当前名称的外力对象
+ ///
+ public bool ContainsForce(string name)
+ {
+ return _forceData.ContainsKey(name);
+ }
+
+ ///
+ /// 根据对象移除一个外力
+ ///
+ public void RemoveForce(ExternalForce force)
+ {
+ RemoveForce(force.Name);
+ }
+
+ ///
+ /// 移除所有外力
+ ///
+ public void ClearForce()
+ {
+ _forceData.Clear();
+ }
+
+ ///
+ /// 移除所有外力和基础力, 使物体静止
+ ///
+ public void Halt()
+ {
+ ClearForce();
+ _basisVelocity = Vector2.Zero;
+ }
+
+ public override void PhysicsProcess(float delta)
+ {
+ //先调用更新
+ var ks = new List(_forceData.Keys);
+ foreach (var k in ks)
+ {
+ var fore = _forceData[k];
+ if (fore.Enable)
+ fore.PhysicsProcess(delta);
+ }
+
+ //外力总和
+ var finallyEf = Vector2.Zero;
+ foreach (var item in _forceData)
+ {
+ if (item.Value.Enable)
+ finallyEf += item.Value.Velocity;
+ }
+ //最终速率
+ var finallyVelocity = _basisVelocity + finallyEf;
+
+ //计算移动
+ _velocity = ActivityObject.MoveAndSlide(finallyVelocity, _upDir);
+
+ //调整外力速率
+ if (_forceData.Count > 0)
+ {
+ var scale = new Vector2();
+ //x轴外力
+ var efx = _velocity.x - _basisVelocity.x;
+ scale.x = finallyEf.x == 0f ? 0 : Mathf.Abs(efx / finallyEf.x);
+ //y轴外力
+ var efy = _velocity.y - _basisVelocity.y;
+ scale.y = finallyEf.y == 0f ? 0 : Mathf.Abs(efy / finallyEf.y);
+ foreach (var item in _forceData)
+ {
+ if (item.Value.Enable)
+ {
+ var velocity = item.Value.Velocity;
+ item.Value.Velocity = new Vector2(velocity.x * scale.x, velocity.y * scale.y);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/role/PathSign.cs b/DungeonShooting_Godot/src/game/role/PathSign.cs
deleted file mode 100644
index 3b1d943..0000000
--- a/DungeonShooting_Godot/src/game/role/PathSign.cs
+++ /dev/null
@@ -1,286 +0,0 @@
-
-using Godot;
-
-///
-/// 寻路标记, 记录下Role移动过的位置, 用于Ai寻路
-///
-public class PathSign : Node2D, IDestroy
-{
- public bool IsDestroyed { get; private set; }
-
- ///
- /// 当前标记在整个链上的索引
- ///
- public int Index { get; }
-
- ///
- /// 监视的对象
- ///
- public Role Target { get; }
-
- ///
- /// 视野半径
- ///
- public float ViewRadius { get; }
-
- ///
- /// 射线对象
- ///
- public RayCast2D RayCast { get; }
-
- ///
- /// 连接的下一个 PathSign
- ///
- public PathSign Next { get; set; }
-
- ///
- /// 是否启用标记路径, 如果禁用, 将会清空所有标记
- ///
- public bool Enable
- {
- get => _enable;
- set
- {
- if (_enable && !value && Next != null)
- {
- Next.Destroy();
- Next = null;
- }
-
- if (!value)
- {
- _isInRange = false;
- _isCollision = false;
- _targetPos = Vector2.Zero;
- _isDiscoverTarget = false;
- }
- _enable = value;
- }
- }
-
- ///
- /// 目标出现过的位置
- ///
- public Vector2 TargetPosition
- {
- get => _targetPos;
- set => _targetPos = value;
- }
-
- //是否发现过目标
- private bool _isDiscoverTarget = false;
- //目标在视野范围内出现过的位置
- private Vector2 _targetPos;
- //射线是否碰撞到目标
- private bool _isCollision;
- //目标是否在范围内
- private bool _isInRange;
- //是否启用
- private bool _enable = false;
-
- ///
- /// 创建标记
- ///
- /// 挂载节点
- /// 视野半径
- /// 监视对象
- public PathSign(Node2D root, float viewRadius, Role target) : this(root, Vector2.Zero, viewRadius, target, 0)
- {
- }
-
- private PathSign(Node2D root, Vector2 pos, float viewRadius, Role target, int index)
- {
- Index = index;
- Target = target;
- ViewRadius = viewRadius;
- root.AddChild(this);
- Position = pos;
-
- //目前只检测墙壁碰撞
- RayCast = new RayCast2D();
- RayCast.CollisionMask = PhysicsLayer.Wall;
- AddChild(RayCast);
-
- //绘制箭头
- if (GameApplication.Instance.Debug)
- {
- var sprite = new Sprite();
- sprite.Texture = ResourceManager.Load(ResourcePath.resource_effects_debug_arrows_png);
- sprite.Position = new Vector2(0, -sprite.Texture.GetHeight() * 0.5f);
- AddChild(sprite);
- }
- }
-
- public override void _Process(float delta)
- {
- base._Process(delta);
- if (GameApplication.Instance.Debug)
- {
- Update();
- }
- }
-
- public override void _PhysicsProcess(float delta)
- {
- if (!_enable)
- {
- return;
- }
- //监视目标
- var nowTargetPos = Target.GlobalPosition;
- var distanceSquared = GlobalPosition.DistanceSquaredTo(nowTargetPos);
- var nowIsInRange = distanceSquared <= ViewRadius * ViewRadius;
-
- if (nowIsInRange) //在视野范围内
- {
- var isCollision = Detect(nowTargetPos);
-
- if (isCollision) //碰到墙
- {
- if (_isInRange && !_isCollision && Next == null) //如果上一帧就在视野内, 才能创建新的折点
- {
- var distance = Mathf.Sqrt(distanceSquared);
- Next = new PathSign(GameApplication.Instance.Room.GetRoot(), _targetPos, ViewRadius - distance, Target, Index + 1);
- Next._targetPos = nowTargetPos;
- Next.Enable = true;
- }
- }
- else //没有碰到墙
- {
- if (Next != null)
- {
- Next.Destroy();
- Next = null;
- }
- _targetPos = nowTargetPos;
- _isDiscoverTarget = true;
- }
-
- _isCollision = isCollision;
- }
- else
- {
- _isCollision = false;
- }
-
- _isInRange = nowIsInRange;
- }
-
- ///
- /// 检测射线是否碰到墙壁
- ///
- ///
- private bool Detect(Vector2 pos)
- {
- RayCast.Enabled = true;
- RayCast.CastTo = RayCast.ToLocal(pos);
- RayCast.ForceRaycastUpdate();
-
- var flag = RayCast.IsColliding();
- RayCast.Enabled = false;
- return flag;
- }
-
- public void Destroy()
- {
- if (IsDestroyed)
- {
- return;
- }
-
- IsDestroyed = true;
-
- if (Next != null)
- {
- Next.Destroy();
- }
-
- QueueFree();
- }
-
- public override void _Draw()
- {
- if (GameApplication.Instance.Debug && _isDiscoverTarget)
- {
- if (Next != null)
- {
- DrawLine(Vector2.Zero, ToLocal(Next.GlobalPosition), Colors.Blue);
- }
- else if (_isInRange && !_isCollision)
- {
- var pos = ToLocal(_targetPos);
- DrawString(ResourceManager.Load(ResourcePath.resource_font_cn_font_12_tres), new Vector2(-6, 12), (ViewRadius - GlobalPosition.DistanceTo(_targetPos)).ToString());
- DrawLine(Vector2.Zero, pos, Colors.Red);
- }
- else
- {
- var pos = ToLocal(_targetPos);
- DrawString(ResourceManager.Load(ResourcePath.resource_font_cn_font_12_tres), new Vector2(-6, 12), "0");
- DrawLine(Vector2.Zero, pos, Colors.Yellow);
- }
- }
- }
-}
-
-#region 备份代码
-/*
-第一个绑定在Role身上的点, 需要每一帧更新
-if (Master.PathSign.Enable)
-{
- var targetSign = master.PathSign;
- var enemyPos = master.GlobalPosition;
- if (targetSign.Next == null)
- {
- var targetPosition = targetSign.TargetPosition;
-
- if (enemyPos.DistanceSquaredTo(targetPosition) <=
- master.Velocity.LengthSquared() * delta) //移动到下一个节点了, 还是没有找到目标, 变为第二状态
- {
- StateController.ChangeStateLate(AIStateEnum.AINormal);
- }
- else //继续移动
- {
- master.LookTargetPosition(targetPosition);
- master.AnimatedSprite.Animation = AnimatorNames.Run;
- master.Velocity = (targetPosition - enemyPos).Normalized() * master.MoveSpeed;
- master.CalcMove(delta);
- }
- }
- else
- {
- var nextPos = targetSign.Next.GlobalPosition;
-
- if (enemyPos.DistanceSquaredTo(nextPos) <=
- master.Velocity.LengthSquared() * delta) //已经移动到下一个节点了, 删除下一个节点, 后面的接上
- {
- var nextNext = targetSign.Next.Next;
- var tempPos = targetSign.Next.TargetPosition;
- targetSign.Next.Next = null;
- targetSign.Next.Destroy();
- targetSign.Next = nextNext;
-
- if (nextNext != null) //下一个点继续移动
- {
- nextPos = nextNext.GlobalPosition;
- master.LookTargetPosition(nextPos);
- master.AnimatedSprite.Animation = AnimatorNames.Run;
- master.Velocity = (nextPos - enemyPos).Normalized() * master.MoveSpeed;
- master.CalcMove(delta);
- }
- else
- {
- targetSign.TargetPosition = tempPos;
- }
- }
- else //继续移动
- {
- master.LookTargetPosition(nextPos);
- master.AnimatedSprite.Animation = AnimatorNames.Run;
- master.Velocity = (nextPos - enemyPos).Normalized() * master.MoveSpeed;
- master.CalcMove(delta);
- }
- }
-}
-
-*/
-#endregion