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