diff --git a/Characters/Character.cs b/Characters/Character.cs
index 585082f..247c921 100644
--- a/Characters/Character.cs
+++ b/Characters/Character.cs
@@ -1,5 +1,4 @@
using Godot;
-using System;
namespace SupaLidlGame.Characters
{
@@ -16,17 +15,35 @@ namespace SupaLidlGame.Characters
// Get the gravity from the project settings to be synced with RigidBody nodes.
public float Gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
- public Vector2 Direction { get; protected set; } = Vector2.Zero;
+ public Vector2 Direction { get; set; } = Vector2.Zero;
+
+ public Vector2 Target { get; set; } = Vector2.Zero;
+
+ [Export]
+ public State.Machine StateMachine { get; set; }
public override void _Process(double delta)
{
+ if (StateMachine != null)
+ {
+ StateMachine.Process(delta);
+ }
+ }
+
+ public override void _Input(InputEvent @event)
+ {
+ if (StateMachine != null)
+ {
+ StateMachine.Input(@event);
+ }
}
public override void _PhysicsProcess(double delta)
{
- // movement would be more crisp with no acceleration
- Velocity = Direction * Speed;
- MoveAndSlide();
+ if (StateMachine != null)
+ {
+ StateMachine.PhysicsProcess(delta);
+ }
}
}
}
diff --git a/Characters/ExampleEnemy.tscn b/Characters/ExampleEnemy.tscn
index f44728c..2563266 100644
--- a/Characters/ExampleEnemy.tscn
+++ b/Characters/ExampleEnemy.tscn
@@ -1,14 +1,18 @@
-[gd_scene load_steps=4 format=3 uid="uid://dymwd5ihpwyqm"]
+[gd_scene load_steps=7 format=3 uid="uid://dymwd5ihpwyqm"]
[ext_resource type="Script" path="res://Characters/NPC.cs" id="1_4x3dm"]
[ext_resource type="Texture2D" uid="uid://bw052v8ikfget" path="res://icon.svg" id="2_ujqd7"]
+[ext_resource type="Script" path="res://Characters/States/Machine.cs" id="3_k4ypw"]
+[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"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_uict5"]
size = Vector2(32, 16)
-[node name="ExampleEnemy" type="CharacterBody2D"]
+[node name="ExampleEnemy" type="CharacterBody2D" node_paths=PackedStringArray("StateMachine")]
script = ExtResource("1_4x3dm")
Speed = 32.0
+StateMachine = NodePath("StateMachine")
[node name="Icon" type="Sprite2D" parent="."]
scale = Vector2(0.25, 0.25)
@@ -17,3 +21,16 @@ texture = ExtResource("2_ujqd7")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2(0, 8)
shape = SubResource("RectangleShape2D_uict5")
+
+[node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")]
+script = ExtResource("3_k4ypw")
+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")
diff --git a/Characters/NPC.cs b/Characters/NPC.cs
index aa64ed0..282fee4 100644
--- a/Characters/NPC.cs
+++ b/Characters/NPC.cs
@@ -8,9 +8,8 @@ namespace SupaLidlGame.Characters
///
/// Time in seconds it takes for the NPC to think FeelsDankCube
///
- public const float ThinkTime = 0.125f;
+ public const float ThinkTime = 0.25f;
- public Character Target { get; protected set; }
public float[] Weights => _weights;
float[] _weights = new float[16];
@@ -30,6 +29,7 @@ namespace SupaLidlGame.Characters
}
}
+ /*
public override void _Process(double delta)
{
if ((_thinkTimeElapsed += delta) > ThinkTime)
@@ -42,6 +42,7 @@ namespace SupaLidlGame.Characters
//Direction = (Target.GlobalPosition - GlobalPosition).Normalized();
base._Process(delta);
}
+ */
public override void _Draw()
{
@@ -83,10 +84,21 @@ namespace SupaLidlGame.Characters
return bestChar;
}
+ public void ThinkProcess(double delta)
+ {
+ if ((_thinkTimeElapsed += delta) > ThinkTime)
+ {
+ _thinkTimeElapsed = 0;
+ Think();
+ }
+
+ Direction = _weightDirs[_bestWeightIdx];
+ }
+
private void Think()
{
Vector2 pos = FindBestTarget().GlobalPosition;
- Vector2 dir = GlobalPosition.DirectionTo(pos);
+ Vector2 dir = Target = GlobalPosition.DirectionTo(pos);
float dist = GlobalPosition.DistanceSquaredTo(pos);
for (int i = 0; i < 16; i++)
@@ -99,9 +111,9 @@ namespace SupaLidlGame.Characters
Vector2 rotatedDir = new Vector2(-dir.y, dir.x);
float horizDot = Math.Abs(_weightDirs[i].Dot(rotatedDir));
- // this is a smaller weight so they are more likely to
- // pick the direction they are currently heading when
- // choosing between two horizontal weights
+ // this is a smaller weight so they are more likely to pick the
+ // direction they are currently heading when choosing between two
+ // horizontal weights
float currDirDot = (_weightDirs[i].Dot(Direction) + 1) / 16;
// square so lower values are even lower
@@ -158,7 +170,7 @@ namespace SupaLidlGame.Characters
else
{
float dot = _weightDirs[i].Dot(_weightDirs[j]);
- _weights[j] -= (dot + 1) / 4;
+ _weights[j] -= (dot + 1) / 2;
}
}
}
diff --git a/Characters/Player.cs b/Characters/Player.cs
index c6111e7..457ee19 100644
--- a/Characters/Player.cs
+++ b/Characters/Player.cs
@@ -3,16 +3,11 @@ using System;
namespace SupaLidlGame.Characters
{
- public partial class Player : Character
- {
- public override void _Ready()
- {
+ public partial class Player : Character
+ {
+ public override void _Ready()
+ {
- }
-
- public override void _Process(double delta)
- {
- Direction = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
- }
- }
+ }
+ }
}
diff --git a/Characters/Player.tscn b/Characters/Player.tscn
index 18ee00d..0754ceb 100644
--- a/Characters/Player.tscn
+++ b/Characters/Player.tscn
@@ -1,13 +1,21 @@
-[gd_scene load_steps=4 format=3 uid="uid://bncaar8vp3b84"]
+[gd_scene load_steps=9 format=3 uid="uid://bncaar8vp3b84"]
[ext_resource type="Script" path="res://Characters/Player.cs" id="1_flygr"]
[ext_resource type="Texture2D" uid="uid://bw052v8ikfget" path="res://icon.svg" id="2_xmgd1"]
+[ext_resource type="Script" path="res://Characters/States/Machine.cs" id="3_npkjp"]
+[ext_resource type="Script" path="res://Characters/States/PlayerIdleState.cs" id="4_4k4mb"]
+[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"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_bfqew"]
size = Vector2(32, 16)
-[node name="Node2D" type="CharacterBody2D"]
+[sub_resource type="LabelSettings" id="LabelSettings_q5h1n"]
+font_size = 24
+
+[node name="Player" type="CharacterBody2D" node_paths=PackedStringArray("StateMachine")]
script = ExtResource("1_flygr")
+StateMachine = NodePath("StateMachine")
[node name="Sprite2D" type="Sprite2D" parent="."]
scale = Vector2(0.25, 0.25)
@@ -21,4 +29,36 @@ shape = SubResource("RectangleShape2D_bfqew")
current = true
zoom = Vector2(4, 4)
-[node name="StateManager" type="Node" parent="."]
+[node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")]
+script = ExtResource("3_npkjp")
+InitialState = NodePath("Idle")
+Character = NodePath("..")
+DebugLevel = 2
+
+[node name="Idle" type="Node" parent="StateMachine" node_paths=PackedStringArray("MoveState")]
+script = ExtResource("4_4k4mb")
+MoveState = NodePath("../Move")
+
+[node name="Move" type="Node" parent="StateMachine" node_paths=PackedStringArray("IdleState", "RollState")]
+script = ExtResource("5_tx5rw")
+IdleState = NodePath("../Idle")
+RollState = NodePath("../Roll")
+
+[node name="Roll" type="Node" parent="StateMachine" node_paths=PackedStringArray("IdleState")]
+script = ExtResource("6_6bgrj")
+IdleState = NodePath("../Idle")
+
+[node name="Debug" type="Control" parent="."]
+layout_mode = 3
+anchors_preset = 0
+
+[node name="State" type="Label" parent="Debug"]
+offset_left = -20.0
+offset_top = -60.0
+offset_right = 20.0
+offset_bottom = -34.0
+text = "lol"
+label_settings = SubResource("LabelSettings_q5h1n")
+horizontal_alignment = 1
+
+[node name="Node" type="Node" parent="."]
diff --git a/Characters/States/CharacterState.cs b/Characters/States/CharacterState.cs
new file mode 100644
index 0000000..2d64e41
--- /dev/null
+++ b/Characters/States/CharacterState.cs
@@ -0,0 +1,36 @@
+using Godot;
+
+namespace SupaLidlGame.Characters.State
+{
+ public partial class CharacterState : Node
+ {
+ public 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;
+
+ ///
+ /// Called when the Character exits this CharacterState.
+ ///
+ public virtual void Exit(CharacterState nextState) { }
+
+ public virtual CharacterState Process(double delta) => null;
+
+ public virtual CharacterState PhysicsProcess(double delta)
+ {
+ Character.Velocity = Character.Direction * Character.Speed;
+ Character.MoveAndSlide();
+ return null;
+ }
+
+ public virtual CharacterState Input(InputEvent @event) => null;
+ }
+}
diff --git a/Characters/States/EnemyMachine.cs b/Characters/States/EnemyMachine.cs
new file mode 100644
index 0000000..e69de29
diff --git a/Characters/States/Machine.cs b/Characters/States/Machine.cs
new file mode 100644
index 0000000..bba2291
--- /dev/null
+++ b/Characters/States/Machine.cs
@@ -0,0 +1,87 @@
+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 (DebugLevel >= 2)
+ {
+ if (State is not null)
+ {
+ string proxyNote = isProxied ? " (proxied)" : "";
+ GD.Print($"Transition{proxyNote} {State.Name} -> {nextState.Name}");
+ }
+ }
+
+ if (DebugLevel >= 1)
+ {
+ if (GetNode