From 1cf47d18a9b233712b7a73422dfe4a6f378438ef Mon Sep 17 00:00:00 2001 From: HumanoidSandvichDispenser Date: Thu, 25 May 2023 15:28:33 -0700 Subject: [PATCH] state machine refactoring --- Characters/Character.cs | 10 +-- Characters/ExampleEnemy.tscn | 34 +++---- Characters/Player.cs | 3 +- Characters/Player.tscn | 50 ++++++----- Characters/States/EnemyMachine.cs | 0 Characters/States/MoveState.cs | 10 --- Characters/States/PlayerAttackState.cs | 59 ------------ .../Character}/CharacterState.cs | 26 ++---- State/Character/CharacterStateMachine.cs | 40 +++++++++ .../Character}/NPCIdleState.cs | 4 +- .../Character}/NPCMoveState.cs | 6 +- .../States => State/Character}/NPCState.cs | 6 +- .../Character}/PlayerIdleState.cs | 4 +- .../Character}/PlayerMoveState.cs | 4 +- .../Character}/PlayerRollState.cs | 15 +--- .../States => State/Character}/PlayerState.cs | 25 ++---- State/IState.cs | 7 +- State/Machine.cs | 90 ------------------- State/Sword/SwordState.cs | 2 +- 19 files changed, 120 insertions(+), 275 deletions(-) delete mode 100644 Characters/States/EnemyMachine.cs delete mode 100644 Characters/States/MoveState.cs delete mode 100644 Characters/States/PlayerAttackState.cs rename {Characters/States => State/Character}/CharacterState.cs (54%) create mode 100644 State/Character/CharacterStateMachine.cs rename {Characters/States => State/Character}/NPCIdleState.cs (79%) rename {Characters/States => State/Character}/NPCMoveState.cs (74%) rename {Characters/States => State/Character}/NPCState.cs (51%) rename {Characters/States => State/Character}/PlayerIdleState.cs (89%) rename {Characters/States => State/Character}/PlayerMoveState.cs (89%) rename {Characters/States => State/Character}/PlayerRollState.cs (69%) rename {Characters/States => State/Character}/PlayerState.cs (65%) delete mode 100644 State/Machine.cs diff --git a/Characters/Character.cs b/Characters/Character.cs index c7e4d86..b87c941 100644 --- a/Characters/Character.cs +++ b/Characters/Character.cs @@ -2,6 +2,7 @@ using Godot; using SupaLidlGame.Extensions; using SupaLidlGame.Items; using SupaLidlGame.Utils; +using SupaLidlGame.State.Character; namespace SupaLidlGame.Characters { @@ -26,12 +27,6 @@ namespace SupaLidlGame.Characters protected float _mass = 1.0f; - public float JumpVelocity { get; protected set; } = -400.0f; - - public float AccelerationMagnitude { get; protected set; } = 256.0f; - - public Vector2 Acceleration => Direction * AccelerationMagnitude; - public Vector2 NetImpulse { get; set; } = Vector2.Zero; public Vector2 Direction { get; set; } = Vector2.Zero; @@ -50,7 +45,6 @@ namespace SupaLidlGame.Characters } _health = value; - GD.Print(_health); if (_health <= 0) { Die(); @@ -71,7 +65,7 @@ namespace SupaLidlGame.Characters public Inventory Inventory { get; set; } [Export] - public State.Machine StateMachine { get; set; } + public CharacterStateMachine StateMachine { get; set; } [Export] public ushort Faction { get; set; } diff --git a/Characters/ExampleEnemy.tscn b/Characters/ExampleEnemy.tscn index 767b726..0bc0347 100644 --- a/Characters/ExampleEnemy.tscn +++ b/Characters/ExampleEnemy.tscn @@ -3,9 +3,9 @@ [ext_resource type="Script" path="res://Characters/Enemy.cs" id="1_2yopk"] [ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="1_fx1w5"] [ext_resource type="Texture2D" uid="uid://bej8thq7ruyty" path="res://Assets/Sprites/Characters/forsen2.png" id="3_ocaae"] -[ext_resource type="Script" path="res://State/Machine.cs" id="4_4nwgr"] -[ext_resource type="Script" path="res://Characters/States/NPCIdleState.cs" id="4_8r2qn"] -[ext_resource type="Script" path="res://Characters/States/NPCMoveState.cs" id="5_utogm"] +[ext_resource type="Script" path="res://State/Character/CharacterStateMachine.cs" id="4_424ux"] +[ext_resource type="Script" path="res://State/Character/NPCIdleState.cs" id="5_tn4cf"] +[ext_resource type="Script" path="res://State/Character/NPCMoveState.cs" id="6_73mr6"] [ext_resource type="PackedScene" uid="uid://cjgxyhgcyvsv7" path="res://BoundingBoxes/Hurtbox.tscn" id="6_jo0cg"] [ext_resource type="Script" path="res://Items/Inventory.cs" id="7_43gq8"] [ext_resource type="PackedScene" uid="uid://d72ehtv1ks0e" path="res://Items/Weapons/Sword.tscn" id="8_s3c8r"] @@ -141,6 +141,21 @@ Inventory = NodePath("Inventory") StateMachine = NodePath("StateMachine") Faction = 2 +[node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")] +script = ExtResource("4_424ux") +InitialState = NodePath("Idle") +Character = NodePath("..") + +[node name="Idle" type="Node" parent="StateMachine" node_paths=PackedStringArray("MoveState", "Character")] +script = ExtResource("5_tn4cf") +MoveState = NodePath("../Move") +Character = NodePath("../..") + +[node name="Move" type="Node" parent="StateMachine" node_paths=PackedStringArray("IdleState", "Character")] +script = ExtResource("6_73mr6") +IdleState = NodePath("../Idle") +Character = NodePath("../..") + [node name="Sprite" type="AnimatedSprite2D" parent="."] use_parent_material = true position = Vector2(0, -4) @@ -151,19 +166,6 @@ animation = &"move" position = Vector2(0, 4) shape = SubResource("RectangleShape2D_uict5") -[node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")] -script = ExtResource("4_4nwgr") -InitialState = NodePath("Idle") -Character = NodePath("..") - -[node name="Idle" type="Node" parent="StateMachine" node_paths=PackedStringArray("MoveState")] -script = ExtResource("4_8r2qn") -MoveState = NodePath("../Move") - -[node name="Move" type="Node" parent="StateMachine" node_paths=PackedStringArray("IdleState")] -script = ExtResource("5_utogm") -IdleState = NodePath("../Idle") - [node name="Hurtbox" parent="." instance=ExtResource("6_jo0cg")] position = Vector2(0, -4) Faction = 2 diff --git a/Characters/Player.cs b/Characters/Player.cs index 81ecdb3..4623ecb 100644 --- a/Characters/Player.cs +++ b/Characters/Player.cs @@ -1,5 +1,6 @@ using Godot; using SupaLidlGame.Utils; +using SupaLidlGame.State; namespace SupaLidlGame.Characters { @@ -41,7 +42,7 @@ namespace SupaLidlGame.Characters public override void ModifyVelocity() { - if (StateMachine.State is State.PlayerRollState) + if (StateMachine.CurrentState is SupaLidlGame.State.Character.PlayerRollState) { Velocity *= 2; } diff --git a/Characters/Player.tscn b/Characters/Player.tscn index 82d093c..1b6441a 100644 --- a/Characters/Player.tscn +++ b/Characters/Player.tscn @@ -2,14 +2,14 @@ [ext_resource type="Script" path="res://Characters/Player.cs" id="1_flygr"] [ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="2_ngsgt"] -[ext_resource type="Script" path="res://Characters/States/PlayerIdleState.cs" id="4_4k4mb"] [ext_resource type="Texture2D" uid="uid://bej8thq7ruyty" path="res://Assets/Sprites/Characters/forsen2.png" id="4_5vird"] [ext_resource type="PackedScene" uid="uid://cl56eadpklnbo" path="res://Utils/PlayerCamera.tscn" id="4_ym125"] -[ext_resource type="Script" path="res://State/Machine.cs" id="5_glslt"] -[ext_resource type="Script" path="res://Characters/States/PlayerMoveState.cs" id="5_tx5rw"] -[ext_resource type="Script" path="res://Characters/States/PlayerRollState.cs" id="6_6bgrj"] +[ext_resource type="Script" path="res://State/Character/CharacterStateMachine.cs" id="5_rgckv"] +[ext_resource type="Script" path="res://State/Character/PlayerIdleState.cs" id="6_wkfdm"] [ext_resource type="PackedScene" uid="uid://d72ehtv1ks0e" path="res://Items/Weapons/Sword.tscn" id="7_4rxuv"] +[ext_resource type="Script" path="res://State/Character/PlayerMoveState.cs" id="7_dfqd8"] [ext_resource type="Script" path="res://Items/Inventory.cs" id="7_xyenu"] +[ext_resource type="Script" path="res://State/Character/PlayerRollState.cs" id="8_fy0v5"] [ext_resource type="PackedScene" uid="uid://cjgxyhgcyvsv7" path="res://BoundingBoxes/Hurtbox.tscn" id="9_avyu4"] [ext_resource type="AudioStream" uid="uid://njun3e6v4854" path="res://Assets/Sounds/hurt.wav" id="12_h0x0g"] @@ -148,6 +148,28 @@ Inventory = NodePath("Inventory") StateMachine = NodePath("StateMachine") Faction = 1 +[node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")] +script = ExtResource("5_rgckv") +InitialState = NodePath("Idle") +Character = NodePath("..") + +[node name="Idle" type="Node" parent="StateMachine" node_paths=PackedStringArray("MoveState", "IdleState", "Character")] +script = ExtResource("6_wkfdm") +MoveState = NodePath("../Move") +IdleState = NodePath(".") +Character = NodePath("../..") + +[node name="Move" type="Node" parent="StateMachine" node_paths=PackedStringArray("RollState", "IdleState", "Character")] +script = ExtResource("7_dfqd8") +RollState = NodePath("../Roll") +IdleState = NodePath("../Idle") +Character = NodePath("../..") + +[node name="Roll" type="Node" parent="StateMachine" node_paths=PackedStringArray("IdleState", "Character")] +script = ExtResource("8_fy0v5") +IdleState = NodePath("../Idle") +Character = NodePath("../..") + [node name="Camera2D" parent="." instance=ExtResource("4_ym125")] position_smoothing_speed = 8.0 @@ -160,26 +182,6 @@ animation = &"idle" position = Vector2(0, 8) shape = SubResource("RectangleShape2D_bfqew") -[node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")] -script = ExtResource("5_glslt") -InitialState = NodePath("Idle") -Character = NodePath("..") -DebugLevel = 2 - -[node name="Idle" type="Node" parent="StateMachine" node_paths=PackedStringArray("MoveState", "IdleState")] -script = ExtResource("4_4k4mb") -MoveState = NodePath("../Move") -IdleState = NodePath(".") - -[node name="Move" type="Node" parent="StateMachine" node_paths=PackedStringArray("RollState", "IdleState")] -script = ExtResource("5_tx5rw") -RollState = NodePath("../Roll") -IdleState = NodePath("../Idle") - -[node name="Roll" type="Node" parent="StateMachine" node_paths=PackedStringArray("IdleState")] -script = ExtResource("6_6bgrj") -IdleState = NodePath("../Idle") - [node name="Debug" type="Control" parent="."] visible = false layout_mode = 3 diff --git a/Characters/States/EnemyMachine.cs b/Characters/States/EnemyMachine.cs deleted file mode 100644 index e69de29..0000000 diff --git a/Characters/States/MoveState.cs b/Characters/States/MoveState.cs deleted file mode 100644 index be972be..0000000 --- a/Characters/States/MoveState.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SupaLidlGame.Characters.State -{ - public partial class MoveState : CharacterState - { - public override CharacterState PhysicsProcess(double delta) - { - return base.PhysicsProcess(delta); - } - } -} diff --git a/Characters/States/PlayerAttackState.cs b/Characters/States/PlayerAttackState.cs deleted file mode 100644 index 730eafc..0000000 --- a/Characters/States/PlayerAttackState.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Godot; -using SupaLidlGame.Items; - -namespace SupaLidlGame.Characters.State -{ - public partial class PlayerAttackState : PlayerState - { - private double _attackTime = 0; - - public override CharacterState Enter(CharacterState previousState) - { - if (Character.Inventory.SelectedItem is Weapon weapon) - { - _attackTime = weapon.UseTime; - weapon.Visible = true; - Character.Inventory.SelectedItem.Use(); - } - else - { - return IdleState; - } - return base.Enter(previousState); - } - - public override void Exit(CharacterState nextState) - { - if (Character.Inventory.SelectedItem is null) - { - - } - Character.Inventory.SelectedItem.Deuse(); - if (Character.Inventory.SelectedItem is Weapon weapon) - { - //weapon.Visible = false; - } - base.Exit(nextState); - } - - public override CharacterState Input(InputEvent @event) - { - return base.Input(@event); - } - - public override CharacterState PhysicsProcess(double delta) - { - Character.Velocity *= 0.5f; - return base.PhysicsProcess(delta); - } - - public override CharacterState Process(double delta) - { - if ((_attackTime -= delta) <= 0) - { - return IdleState; - } - return base.Process(delta); - } - } -} diff --git a/Characters/States/CharacterState.cs b/State/Character/CharacterState.cs similarity index 54% rename from Characters/States/CharacterState.cs rename to State/Character/CharacterState.cs index 5f9234e..2b0cc3a 100644 --- a/Characters/States/CharacterState.cs +++ b/State/Character/CharacterState.cs @@ -1,26 +1,18 @@ using Godot; -namespace SupaLidlGame.Characters.State +namespace SupaLidlGame.State.Character { - public partial class CharacterState : Node + public abstract partial class CharacterState : Node, IState { - public Character Character { get; set; } + [Export] + public Characters.Character Character { get; set; } - /// - /// Called when the Character enters this CharacterState. - /// - /// - /// This returns a CharacterState in case a state is being - /// transitioned to but wants to transition to another state. For - /// example, an attack state can return to an idle state, but that idle - /// state can override it to the move state immediately when necessary. - /// - public virtual CharacterState Enter(CharacterState previousState) => null; + public virtual IState Enter(IState prev) => null; - /// - /// Called when the Character exits this CharacterState. - /// - public virtual void Exit(CharacterState nextState) { } + public virtual void Exit(IState next) + { + + } public virtual CharacterState Process(double delta) { diff --git a/State/Character/CharacterStateMachine.cs b/State/Character/CharacterStateMachine.cs new file mode 100644 index 0000000..5386c9a --- /dev/null +++ b/State/Character/CharacterStateMachine.cs @@ -0,0 +1,40 @@ +using Godot; + +namespace SupaLidlGame.State.Character +{ + public partial class CharacterStateMachine : StateMachine + { + [Export] + public override CharacterState InitialState { get; set; } + + [Export] + public Characters.Character Character { get; set; } + + public void Process(double delta) + { + var state = CurrentState.Process(delta); + if (state is CharacterState) + { + ChangeState(state); + } + } + + public void PhysicsProcess(double delta) + { + var state = CurrentState.PhysicsProcess(delta); + if (state is CharacterState) + { + ChangeState(state); + } + } + + public void Input(InputEvent @event) + { + var state = CurrentState.Input(@event); + if (state is not null) + { + ChangeState(state); + } + } + } +} diff --git a/Characters/States/NPCIdleState.cs b/State/Character/NPCIdleState.cs similarity index 79% rename from Characters/States/NPCIdleState.cs rename to State/Character/NPCIdleState.cs index 3fd4b95..3024443 100644 --- a/Characters/States/NPCIdleState.cs +++ b/State/Character/NPCIdleState.cs @@ -1,6 +1,6 @@ using Godot; -namespace SupaLidlGame.Characters.State +namespace SupaLidlGame.State.Character { public partial class NPCIdleState : NPCState { @@ -17,7 +17,7 @@ namespace SupaLidlGame.Characters.State return null; } - public override CharacterState Enter(CharacterState previousState) + public override IState Enter(IState previousState) { Character.Sprite.Play("idle"); return base.Enter(previousState); diff --git a/Characters/States/NPCMoveState.cs b/State/Character/NPCMoveState.cs similarity index 74% rename from Characters/States/NPCMoveState.cs rename to State/Character/NPCMoveState.cs index 4350009..59653fe 100644 --- a/Characters/States/NPCMoveState.cs +++ b/State/Character/NPCMoveState.cs @@ -1,6 +1,6 @@ using Godot; -namespace SupaLidlGame.Characters.State +namespace SupaLidlGame.State.Character { public partial class NPCMoveState : NPCState { @@ -17,10 +17,10 @@ namespace SupaLidlGame.Characters.State return null; } - public override CharacterState Enter(CharacterState previousState) + public override IState Enter(IState prev) { Character.Sprite.Play("move"); - return base.Enter(previousState); + return base.Enter(prev); } } } diff --git a/Characters/States/NPCState.cs b/State/Character/NPCState.cs similarity index 51% rename from Characters/States/NPCState.cs rename to State/Character/NPCState.cs index 2655aa9..c91397a 100644 --- a/Characters/States/NPCState.cs +++ b/State/Character/NPCState.cs @@ -1,8 +1,8 @@ -namespace SupaLidlGame.Characters.State +namespace SupaLidlGame.State.Character { - public partial class NPCState : CharacterState + public abstract partial class NPCState : CharacterState { - protected NPC _npc => Character as NPC; + protected Characters.NPC _npc => Character as Characters.NPC; public override CharacterState Process(double delta) { diff --git a/Characters/States/PlayerIdleState.cs b/State/Character/PlayerIdleState.cs similarity index 89% rename from Characters/States/PlayerIdleState.cs rename to State/Character/PlayerIdleState.cs index aa84471..998c6a3 100644 --- a/Characters/States/PlayerIdleState.cs +++ b/State/Character/PlayerIdleState.cs @@ -1,13 +1,13 @@ using Godot; -namespace SupaLidlGame.Characters.State +namespace SupaLidlGame.State.Character { public partial class PlayerIdleState : PlayerState { [Export] public CharacterState MoveState { get; set; } - public override CharacterState Enter(CharacterState previousState) + public override IState Enter(IState previousState) { GD.Print("Entered idle state"); if (previousState is not PlayerMoveState) diff --git a/Characters/States/PlayerMoveState.cs b/State/Character/PlayerMoveState.cs similarity index 89% rename from Characters/States/PlayerMoveState.cs rename to State/Character/PlayerMoveState.cs index 9f27a20..1b26508 100644 --- a/Characters/States/PlayerMoveState.cs +++ b/State/Character/PlayerMoveState.cs @@ -1,14 +1,14 @@ using Godot; using SupaLidlGame.Items; -namespace SupaLidlGame.Characters.State +namespace SupaLidlGame.State.Character { public partial class PlayerMoveState : PlayerState { [Export] public PlayerRollState RollState { get; set; } - public override CharacterState Enter(CharacterState previousState) + public override IState Enter(IState previousState) { Godot.GD.Print("Started moving"); _player.Animation = "move"; diff --git a/Characters/States/PlayerRollState.cs b/State/Character/PlayerRollState.cs similarity index 69% rename from Characters/States/PlayerRollState.cs rename to State/Character/PlayerRollState.cs index 14ee519..efafbd6 100644 --- a/Characters/States/PlayerRollState.cs +++ b/State/Character/PlayerRollState.cs @@ -1,6 +1,6 @@ using Godot; -namespace SupaLidlGame.Characters.State +namespace SupaLidlGame.State.Character { public partial class PlayerRollState : PlayerState { @@ -8,7 +8,7 @@ namespace SupaLidlGame.Characters.State private Vector2 _rollDirection = Vector2.Zero; - public override CharacterState Enter(CharacterState previousState) + public override IState Enter(IState previousState) { _timeLeftToRoll = 0.5; // roll the direction we were previously moving in @@ -17,7 +17,7 @@ namespace SupaLidlGame.Characters.State return base.Enter(previousState); } - public override void Exit(CharacterState nextState) + public override void Exit(IState nextState) { // we want to reset our state variables in case we are forced out of // this state (e.g. from death) @@ -35,14 +35,5 @@ namespace SupaLidlGame.Characters.State } return null; } - - /* - public override CharacterState PhysicsProcess(double delta) - { - Character.Velocity = Character.Direction * Character.Speed * 1.5f; - Character.MoveAndSlide(); - return null; - } - */ } } diff --git a/Characters/States/PlayerState.cs b/State/Character/PlayerState.cs similarity index 65% rename from Characters/States/PlayerState.cs rename to State/Character/PlayerState.cs index e9e7207..7ba5557 100644 --- a/Characters/States/PlayerState.cs +++ b/State/Character/PlayerState.cs @@ -1,11 +1,11 @@ using Godot; using SupaLidlGame.Items; +using SupaLidlGame.Characters; -namespace SupaLidlGame.Characters.State +namespace SupaLidlGame.State.Character { - public partial class PlayerState : CharacterState + public abstract partial class PlayerState : CharacterState { - //public PlayerMachine PlayerMachine => Machine as PlayerMachine; protected Player _player => Character as Player; [Export] @@ -15,28 +15,13 @@ namespace SupaLidlGame.Characters.State { var inventory = Character.Inventory; - #if DEBUG - //if (@event.IsActionPressed("equip")) - //{ - // inventory.SelectedItem = inventory.GetNode("Sword"); - //} - #endif - - if (this is PlayerIdleState or PlayerMoveState - && !_player.Inventory.IsUsingItem) + if (this is PlayerIdleState or PlayerMoveState && + !_player.Inventory.IsUsingItem) { if (@event.IsActionPressed("equip_1")) { inventory.SelectedItem = inventory.GetItemByMap("equip_1"); } - else if (@event.IsActionPressed("equip_2")) - { - inventory.SelectedItem = inventory.GetItemByMap("equip_2"); - } - else if (@event.IsActionPressed("equip_3")) - { - inventory.SelectedItem = inventory.GetItemByMap("equip_3"); - } } return base.Input(@event); diff --git a/State/IState.cs b/State/IState.cs index c916b1f..2d920c3 100644 --- a/State/IState.cs +++ b/State/IState.cs @@ -11,15 +11,12 @@ namespace SupaLidlGame.State /// example, an attack state can return to an idle state, but that idle /// state can override it to the move state immediately when necessary. /// - public IState Enter(IState previousState) => null; + public IState Enter(IState previousState); /// /// Called when the Character exits this CharacterState. /// - public void Exit(IState nextState) - { - - } + public void Exit(IState nextState); public IState Process(double delta) => null; } diff --git a/State/Machine.cs b/State/Machine.cs deleted file mode 100644 index 07f284e..0000000 --- a/State/Machine.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Godot; - -namespace SupaLidlGame.Characters.State -{ - public partial class Machine : Node - { - protected Character _character; - - public CharacterState State { get; protected set; } - - [Export] - public CharacterState InitialState { get; set; } - - [Export] - public Character Character { get; set; } - - [Export] - public int DebugLevel { get; set; } - - public override void _Ready() - { - ChangeState(InitialState); - } - - public void ChangeState(CharacterState nextState, bool isProxied = false) - { - if (nextState is null) - return; - - if (DebugLevel >= 2) - { - if (State is not null) - { - string proxyNote = isProxied ? " (proxied)" : ""; - GD.Print($"Transition{proxyNote} {State.Name} -> {nextState.Name}"); - } - } - - if (DebugLevel >= 1) - { - if (GetNode