diff --git a/DungeonShooting_Godot/project.godot b/DungeonShooting_Godot/project.godot index 869f9bd..c5ef034 100644 --- a/DungeonShooting_Godot/project.godot +++ b/DungeonShooting_Godot/project.godot @@ -27,7 +27,6 @@ window/size/width=1920 window/size/height=1080 window/size/resizable=false -window/size/always_on_top=true window/dpi/allow_hidpi=true window/vsync/use_vsync=false window/stretch/mode="2d" diff --git a/DungeonShooting_Godot/src/framework/ActivityObject.cs b/DungeonShooting_Godot/src/framework/ActivityObject.cs index ef1a1cd..1d448bd 100644 --- a/DungeonShooting_Godot/src/framework/ActivityObject.cs +++ b/DungeonShooting_Godot/src/framework/ActivityObject.cs @@ -65,6 +65,21 @@ /// public MoveController MoveController { get; } + /// + /// 物体移动基础速率 + /// + public Vector2 BasisVelocity + { + get => MoveController.BasisVelocity; + set => MoveController.BasisVelocity = value; + } + + /// + /// 这个速度就是玩家当前物理帧移动的真实速率, 该速度由物理帧循环更新, 并不会马上更新 + /// 该速度就是 BasisVelocity + 外力总和 + /// + public Vector2 Velocity => MoveController.Velocity; + //组件集合 private List> _components = new List>(); //是否初始化阴影 @@ -120,7 +135,6 @@ case "ShadowSprite": ShadowSprite = (Sprite)body; ShadowSprite.Visible = false; - //ShadowSprite.ZIndex = -5; break; case "Collision": Collision = (CollisionShape2D)body; @@ -264,6 +278,20 @@ } /// + /// 每帧调用一次, 物体的 Process() 会在组件的 Process() 之前调用 + /// + protected virtual void Process(float delta) + { + } + + /// + /// 每物理帧调用一次, 物体的 PhysicsProcess() 会在组件的 PhysicsProcess() 之前调用 + /// + protected virtual void PhysicsProcess(float delta) + { + } + + /// /// 如果开启 debug, 则每帧调用该函数, 可用于绘制文字线段等 /// protected virtual void DebugDraw() @@ -357,7 +385,9 @@ _throwData.StartXSpeed = xSpeed; _throwData.StartYSpeed = ySpeed; _throwData.RotateSpeed = rotate; - _throwData.LinearVelocity = new Vector2(_throwData.XSpeed, 0).Rotated(_throwData.Direction * Mathf.Pi / 180); + _throwData.ThrowForce = MoveController.AddForce("ThrowForce"); + _throwData.ThrowForce.Velocity = + new Vector2(_throwData.XSpeed, 0).Rotated(_throwData.Direction * Mathf.Pi / 180); _throwData.Y = startHeight; _throwData.Bounce = bounce; _throwData.BounceStrength = bounceStrength; @@ -436,8 +466,13 @@ return (TC)component; } - public override void _Process(float delta) + /// + /// 每帧调用一次, 为了防止子类覆盖 _Process(), 给 _Process() 加上了 sealed, 子类需要帧循环函数请重写 Process() 函数 + /// + public sealed override void _Process(float delta) { + Process(delta); + //更新组件 if (_components.Count > 0) { @@ -462,7 +497,6 @@ //投抛计算 if (_throwData != null && !_throwData.IsOver) { - _throwData.LinearVelocity = MoveAndSlide(_throwData.LinearVelocity); GlobalRotationDegrees = GlobalRotationDegrees + _throwData.RotateSpeed * delta; CalcThrowAnimatedPosition(); @@ -508,7 +542,7 @@ _throwData.XSpeed = _throwData.StartXSpeed = _throwData.StartXSpeed * _throwData.BounceSpeed; _throwData.YSpeed = _throwData.StartYSpeed = _throwData.StartYSpeed * _throwData.BounceStrength; _throwData.RotateSpeed = _throwData.RotateSpeed * _throwData.BounceStrength; - _throwData.LinearVelocity *= _throwData.BounceSpeed; + _throwData.ThrowForce.Velocity *= _throwData.BounceSpeed; _throwData.FirstOver = false; _throwData.IsOver = false; @@ -571,8 +605,13 @@ } } - public override void _PhysicsProcess(float delta) + /// + /// 每物理帧调用一次, 为了防止子类覆盖 _PhysicsProcess(), 给 _PhysicsProcess() 加上了 sealed, 子类需要帧循环函数请重写 PhysicsProcess() 函数 + /// + public sealed override void _PhysicsProcess(float delta) { + PhysicsProcess(delta); + //更新组件 if (_components.Count > 0) { @@ -595,7 +634,10 @@ } } - public override void _Draw() + /// + /// 绘制函数, 子类不允许重写, 需要绘制函数请重写 DebugDraw() + /// + public sealed override void _Draw() { if (IsDebug) { @@ -785,6 +827,9 @@ /// private void ThrowOver() { + //移除投抛的力 + MoveController.RemoveForce(_throwData.ThrowForce); + GetParent().RemoveChild(this); GameApplication.Instance.Room.GetRoot(UseYSort).AddChild(this); RestoreCollision(); diff --git a/DungeonShooting_Godot/src/framework/MoveController.cs b/DungeonShooting_Godot/src/framework/MoveController.cs index 5f79c96..ccedee0 100644 --- a/DungeonShooting_Godot/src/framework/MoveController.cs +++ b/DungeonShooting_Godot/src/framework/MoveController.cs @@ -13,11 +13,33 @@ private readonly List _forceList = new List(); /// - /// 这个速度就是玩家当前物理帧移动的真实速率 + /// 这个速度就是玩家当前物理帧移动的真实速率, 该速度由物理帧循环更新, 并不会马上更新 + /// 该速度就是 BasisVelocity + 外力总和 /// public Vector2 Velocity => _velocity; + private Vector2 _velocity = Vector2.Zero; + + /// + /// 玩家的基础移动速率 + /// + public Vector2 BasisVelocity + { + get => _basisVelocity; + set => _basisVelocity = value; + } + + private Vector2 _basisVelocity = Vector2.Zero; + + /// + /// 获取所有外力对象 + /// + public ExternalForce[] GetAllForce() + { + return _forceList.ToArray(); + } + /// /// 根据名称添加一个外力, 并返回创建的外力的对象, 如果存在这个名称的外力, 移除之前的外力 /// @@ -103,17 +125,13 @@ _forceList.Clear(); } - /// - /// 移除所有外力和基础力, 使物体静止 - /// - public void Halt() - { - ClearForce(); - _velocity = Vector2.Zero; - } - public override void PhysicsProcess(float delta) { + if (_basisVelocity == Vector2.Zero && _forceList.Count == 0) + { + return; + } + //先调用更新 var externalForces = _forceList.ToArray(); foreach (var fore in externalForces) @@ -123,7 +141,7 @@ } //外力总和 - var finallyEf = Vector2.Zero; + var finallyEf = new Vector2(); foreach (var fore in externalForces) { if (fore.Enable) @@ -131,29 +149,33 @@ } //最终速率 - var finallyVelocity = _velocity + finallyEf; + var finallyVelocity = _basisVelocity + finallyEf; - //计算移动 - _velocity = ActivityObject.MoveAndSlide(finallyVelocity); - - //调整外力速率 - if (externalForces.Length > 0) + if (finallyVelocity != Vector2.Zero) { - var scale = new Vector2(); - //x轴外力 - var efx = _velocity.x - _velocity.x; - scale.x = finallyEf.x == 0f ? 0 : Mathf.Abs(efx / finallyEf.x); - //y轴外力 - var efy = _velocity.y - _velocity.y; - scale.y = finallyEf.y == 0f ? 0 : Mathf.Abs(efy / finallyEf.y); - foreach (var force in externalForces) + //计算移动 + _velocity = ActivityObject.MoveAndSlide(finallyVelocity); + + //调整外力速率 + if (externalForces.Length > 0) { - if (force.Enable) + //x轴外力 + var scaleX = finallyEf.x == 0f ? 0 : Mathf.Abs((_velocity.x - _basisVelocity.x) / finallyEf.x); + //y轴外力 + var scaleY = finallyEf.y == 0f ? 0 : Mathf.Abs((_velocity.y - _basisVelocity.y) / finallyEf.y); + foreach (var force in _forceList) { - var velocity = force.Velocity; - force.Velocity = new Vector2(velocity.x * scale.x, velocity.y * scale.y); + if (force.Enable) + { + var velocity = force.Velocity; + force.Velocity = new Vector2(velocity.x * scaleX, velocity.y * scaleY); + } } } } + else + { + _velocity = finallyEf; + } } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/framework/ObjectThrowData.cs b/DungeonShooting_Godot/src/framework/ObjectThrowData.cs index ec4ffd3..2986397 100644 --- a/DungeonShooting_Godot/src/framework/ObjectThrowData.cs +++ b/DungeonShooting_Godot/src/framework/ObjectThrowData.cs @@ -74,8 +74,8 @@ public Vector2 CurrPosition; public float Y; - public Vector2 LinearVelocity; - + public ExternalForce ThrowForce; + //----------- 用于记录原始信息 -------------- public bool UseOrigin = true; public Shape2D OriginShape; diff --git a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs index c4879b3..96eff98 100644 --- a/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs +++ b/DungeonShooting_Godot/src/game/item/weapon/Weapon.cs @@ -302,9 +302,8 @@ UnclaimedWeapons.Remove(this); } - public override void _Process(float delta) + protected override void Process(float delta) { - base._Process(delta); //这把武器被扔在地上, 或者当前武器没有被使用 if (Master == null || Master.Holster.ActiveWeapon != this) { diff --git a/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs b/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs index 60f7c2c..71a35a0 100644 --- a/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs +++ b/DungeonShooting_Godot/src/game/item/weapon/bullet/Bullet.cs @@ -40,9 +40,8 @@ ShowShadowSprite(); } - public override void _PhysicsProcess(float delta) + protected override void PhysicsProcess(float delta) { - base._PhysicsProcess(delta); //移动 var kinematicCollision = MoveAndCollide(new Vector2(FlySpeed * delta, 0).Rotated(Rotation)); if (kinematicCollision != null) diff --git a/DungeonShooting_Godot/src/game/item/weapon/knife/Knife.cs b/DungeonShooting_Godot/src/game/item/weapon/knife/Knife.cs index ebc604f..e3e1da1 100644 --- a/DungeonShooting_Godot/src/game/item/weapon/knife/Knife.cs +++ b/DungeonShooting_Godot/src/game/item/weapon/knife/Knife.cs @@ -44,10 +44,9 @@ _hitArea.Connect("body_entered", this, nameof(OnBodyEntered)); } - public override void _Process(float delta) + protected override void Process(float delta) { - base._Process(delta); - + base.Process(delta); if (IsActive) { //让碰撞节点与武器挂载节点位置保持一致, 而不跟着武器走 @@ -55,10 +54,9 @@ } } - public override void _PhysicsProcess(float delta) + protected override void PhysicsProcess(float delta) { - base._PhysicsProcess(delta); - + base.PhysicsProcess(delta); //过去两个物理帧后就能关闭碰撞了 if (++_attackIndex >= 2) { diff --git a/DungeonShooting_Godot/src/game/role/Player.cs b/DungeonShooting_Godot/src/game/role/Player.cs index a009b19..f805bc9 100644 --- a/DungeonShooting_Godot/src/game/role/Player.cs +++ b/DungeonShooting_Godot/src/game/role/Player.cs @@ -49,10 +49,9 @@ Shield = 30; } - public override void _Process(float delta) + protected override void Process(float delta) { - base._Process(delta); - + base.Process(delta); //脸的朝向 var gPos = GlobalPosition; if (LookTarget == null) @@ -110,9 +109,9 @@ } } - public override void _PhysicsProcess(float delta) + protected override void PhysicsProcess(float delta) { - base._PhysicsProcess(delta); + base.PhysicsProcess(delta); HandleMoveInput(delta); //播放动画 PlayAnim(); @@ -193,35 +192,35 @@ // 如果 有输入 就以当前速度,用acceleration 插值到 对应方向 * 最大速度 if (Mathf.IsZeroApprox(dir.x)) { - Velocity = new Vector2(Mathf.MoveToward(Velocity.x, 0, Friction * delta), Velocity.y); + BasisVelocity = new Vector2(Mathf.MoveToward(BasisVelocity.x, 0, Friction * delta), BasisVelocity.y); } else { - Velocity = new Vector2(Mathf.MoveToward(Velocity.x, dir.x * MoveSpeed, Acceleration * delta), Velocity.y); + BasisVelocity = new Vector2(Mathf.MoveToward(BasisVelocity.x, dir.x * MoveSpeed, Acceleration * delta), + BasisVelocity.y); } if (Mathf.IsZeroApprox(dir.y)) { - Velocity = new Vector2(Velocity.x, Mathf.MoveToward(Velocity.y, 0, Friction * delta)); + BasisVelocity = new Vector2(BasisVelocity.x, Mathf.MoveToward(BasisVelocity.y, 0, Friction * delta)); } else { - Velocity = new Vector2(Velocity.x, Mathf.MoveToward(Velocity.y, dir.y * MoveSpeed, Acceleration * delta)); + BasisVelocity = new Vector2(BasisVelocity.x, + Mathf.MoveToward(BasisVelocity.y, dir.y * MoveSpeed, Acceleration * delta)); } - - CalcMove(delta); } - + // 播放动画 private void PlayAnim() { - if (Velocity != Vector2.Zero) + if (BasisVelocity != Vector2.Zero) { - if ((Face == FaceDirection.Right && Velocity.x >= 0) || Face == FaceDirection.Left && Velocity.x <= 0) //向前走 + if ((Face == FaceDirection.Right && BasisVelocity.x >= 0) || Face == FaceDirection.Left && BasisVelocity.x <= 0) //向前走 { AnimatedSprite.Animation = AnimatorNames.Run; } - else if ((Face == FaceDirection.Right && Velocity.x < 0) || Face == FaceDirection.Left && Velocity.x > 0) //向后走 + else if ((Face == FaceDirection.Right && BasisVelocity.x < 0) || Face == FaceDirection.Left && BasisVelocity.x > 0) //向后走 { AnimatedSprite.Animation = AnimatorNames.ReverseRun; } diff --git a/DungeonShooting_Godot/src/game/role/Role.cs b/DungeonShooting_Godot/src/game/role/Role.cs index 6d8e632..90c1cfc 100644 --- a/DungeonShooting_Godot/src/game/role/Role.cs +++ b/DungeonShooting_Godot/src/game/role/Role.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using Godot; @@ -63,16 +62,6 @@ private FaceDirection _face; /// - /// 是否启用角色移动, 如果禁用, 那么调用 CalcMove() 将不再有任何效果 - /// - public bool EnableMove { get; set; } = true; - - /// - /// 移动速度, 通过调用 CalcMove() 函数来移动 - /// - public Vector2 Velocity { get; set; } = Vector2.Zero; - - /// /// 是否死亡 /// public bool IsDie { get; private set; } @@ -245,10 +234,8 @@ InteractiveArea.Connect("area_exited", this, nameof(_OnPropsExit)); } - public override void _Process(float delta) + protected override void Process(float delta) { - base._Process(delta); - //看向目标 if (LookTarget != null) { @@ -348,17 +335,6 @@ } /// - /// 计算角色移动 - /// - public virtual void CalcMove(float delta) - { - if (EnableMove && Velocity != Vector2.Zero) - { - Velocity = MoveAndSlide(Velocity); - } - } - - /// /// 返回所有武器是否弹药都打光了 /// public bool IsAllWeaponTotalAmmoEmpty() diff --git a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs index e2e3143..2e547fc 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/Enemy.cs @@ -136,10 +136,9 @@ Destroy(); } - public override void _PhysicsProcess(float delta) + protected override void PhysicsProcess(float delta) { - base._PhysicsProcess(delta); - + base.PhysicsProcess(delta); _enemyAttackTimer -= delta; EnemyPickUpWeapon(); diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiFindAmmoState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiFindAmmoState.cs index 9199909..4d507e6 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AiFindAmmoState.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiFindAmmoState.cs @@ -108,10 +108,9 @@ //计算移动 var nextPos = Master.NavigationAgent2D.GetNextLocation(); Master.AnimatedSprite.Animation = AnimatorNames.Run; - Master.Velocity = + Master.BasisVelocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * Master.MoveSpeed; - Master.CalcMove(delta); } } } diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiFollowUpState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiFollowUpState.cs index f4a8371..bb6e82f 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AiFollowUpState.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiFollowUpState.cs @@ -75,9 +75,8 @@ //计算移动 var nextPos = Master.NavigationAgent2D.GetNextLocation(); Master.AnimatedSprite.Animation = AnimatorNames.Run; - Master.Velocity = (nextPos - masterPosition - Master.NavigationPoint.Position).Normalized() * + Master.BasisVelocity = (nextPos - masterPosition - Master.NavigationPoint.Position).Normalized() * Master.MoveSpeed; - Master.CalcMove(delta); } //检测玩家是否在视野内 diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiLeaveForState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiLeaveForState.cs index 96babc1..d0901ab 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AiLeaveForState.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiLeaveForState.cs @@ -67,9 +67,8 @@ var nextPos = Master.NavigationAgent2D.GetNextLocation(); Master.LookTargetPosition(Enemy.FindTargetPosition); Master.AnimatedSprite.Animation = AnimatorNames.Run; - Master.Velocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * + Master.BasisVelocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * Master.MoveSpeed; - Master.CalcMove(delta); } var playerPos = GameApplication.Instance.Room.Player.GetCenterPosition(); diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs index 8260a7e..0683d8d 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiNormalState.cs @@ -78,9 +78,8 @@ //计算移动 var nextPos = Master.NavigationAgent2D.GetNextLocation(); Master.AnimatedSprite.Animation = AnimatorNames.Run; - Master.Velocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * + Master.BasisVelocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * Master.MoveSpeed; - Master.CalcMove(delta); if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点 { diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiSurroundState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiSurroundState.cs index 36c6594..5995c35 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AiSurroundState.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiSurroundState.cs @@ -80,9 +80,8 @@ //计算移动 var nextPos = Master.NavigationAgent2D.GetNextLocation(); Master.AnimatedSprite.Animation = AnimatorNames.Run; - Master.Velocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * + Master.BasisVelocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * Master.MoveSpeed; - Master.CalcMove(delta); if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点 { diff --git a/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs b/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs index 7a094e7..d766cd2 100644 --- a/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs +++ b/DungeonShooting_Godot/src/game/role/enemy/state/AiTailAfterState.cs @@ -68,9 +68,8 @@ //计算移动 var nextPos = Master.NavigationAgent2D.GetNextLocation(); Master.AnimatedSprite.Animation = AnimatorNames.Run; - Master.Velocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * + Master.BasisVelocity = (nextPos - Master.GlobalPosition - Master.NavigationPoint.Position).Normalized() * Master.MoveSpeed; - Master.CalcMove(delta); } //检测玩家是否在视野内, 如果在, 则切换到 AiTargetInView 状态 if (Master.IsInTailAfterViewRange(playerPos))