Skip to content

Instantly share code, notes, and snippets.

@millerbyte
Created December 11, 2024 02:26
Show Gist options
  • Select an option

  • Save millerbyte/8bfedc0fb7d42d6eeb96433072613be9 to your computer and use it in GitHub Desktop.

Select an option

Save millerbyte/8bfedc0fb7d42d6eeb96433072613be9 to your computer and use it in GitHub Desktop.
Objectless State Machine
using Godot;
using System;
public class AIStateMachine : Node
{
// Enum to represent states
private enum StateEnum
{
Idle,
Follow,
Attack,
Recover
}
// Struct to represent a state
private struct State
{
public StateEnum StateType;
public int NextStateIndex; // Index of the next state in the array
public Action<StateMachinePayload> UpdateAction;
public State(StateEnum stateType, int nextStateIndex, Action<StateMachinePayload> updateAction)
{
StateType = stateType;
NextStateIndex = nextStateIndex;
UpdateAction = updateAction;
}
}
// Payload shared across all states
private struct StateMachinePayload
{
public Vector3 Position;
public Vector3 TargetPosition;
public bool IsEnemyVisible;
public float AttackCooldown;
// Add other shared data here
}
private State[] states; // Array to hold all states
private StateMachinePayload payload; // Shared payload
private int currentStateIndex; // Current state index
public override void _Ready()
{
// Initialize states
states = new State[]
{
new State(StateEnum.Idle, 1, UpdateIdle),
new State(StateEnum.Follow, 2, UpdateFollow),
new State(StateEnum.Attack, 3, UpdateAttack),
new State(StateEnum.Recover, 0, UpdateRecover)
};
payload = new StateMachinePayload
{
Position = Vector3.Zero,
TargetPosition = new Vector3(10, 0, 10),
IsEnemyVisible = false,
AttackCooldown = 0
};
currentStateIndex = 0; // Start with Idle
}
public override void _Process(float delta)
{
// Call the update function of the current state
states[currentStateIndex].UpdateAction(payload);
}
private void UpdateIdle(StateMachinePayload payload)
{
GD.Print("Idle state: Looking for an enemy...");
if (payload.IsEnemyVisible)
{
TransitionToState(states[currentStateIndex].NextStateIndex);
}
}
private void UpdateFollow(StateMachinePayload payload)
{
GD.Print("Follow state: Moving towards the target...");
if ((payload.Position - payload.TargetPosition).Length() < 1f)
{
TransitionToState(states[currentStateIndex].NextStateIndex);
}
}
private void UpdateAttack(StateMachinePayload payload)
{
GD.Print("Attack state: Attacking the enemy...");
if (payload.AttackCooldown <= 0)
{
TransitionToState(states[currentStateIndex].NextStateIndex);
}
}
private void UpdateRecover(StateMachinePayload payload)
{
GD.Print("Recover state: Cooling down...");
payload.AttackCooldown -= GetProcessDeltaTime();
if (payload.AttackCooldown <= 0)
{
TransitionToState(states[currentStateIndex].NextStateIndex);
}
}
private void TransitionToState(int nextStateIndex)
{
GD.Print($"Transitioning from {states[currentStateIndex].StateType} to {states[nextStateIndex].StateType}");
currentStateIndex = nextStateIndex;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment