===== StateMachines v2 =====
* https://github.com/THBA-HTX/fsm_demo
Princip:
using Godot;
using System;
public partial class AIController : CharacterBody2D
{
[Export] public float SpeedWalk = 1.5f;
[Export] public float SpeedRun = 10f;
private float CurrentSpeed;
public Boolean IsRunning = false;
public Boolean IsStopped = false;
public CharacterBody2D Player; // reference til vores Spiller
public float PlayerDistance;
Vector2 NextDest; // Hvad er næste destination..gemmes her
Vector2 MoveDirection;
[Signal] public delegate void TargetReachedEventHandler();
public override void _Ready()
{
// Vi opretter reference til vores Spiller
Player = (CharacterBody2D) GetTree().GetNodesInGroup("Player")[0];
}
public override void _Process(double delta)
{
if (Player != null)
{
PlayerDistance = GlobalPosition.DistanceTo(Player.GlobalPosition);
}
// GD.Print("Player Distance er: " + PlayerDistance.ToString());
}
public override void _PhysicsProcess(double delta)
{
MoveDirection = GlobalPosition.DirectionTo(NextDest).Normalized();
if ( GlobalPosition.DistanceTo(NextDest) <= 0.8f)
{ // Sender signal om at vi har nået vores destination til abonnenter
EmitSignal(nameof(TargetReached));
}
// Hvis AI er stopped, så sæt retning til 0.
if (IsStopped == true) {
MoveDirection = Vector2.Zero;
}
if (IsRunning == true)
{
CurrentSpeed = SpeedRun;
}
else {
CurrentSpeed = SpeedWalk;
}
Velocity = MoveDirection * CurrentSpeed;
MoveAndSlide();
}
public void MoveToPosition(Vector2 pos)
{
IsStopped = false;
NextDest = pos;
}
}
==== StateMachine ====
using Godot;
using Godot.Collections;
using System;
public partial class StateMachine : Node
{
// Den state vi starter i
[Export] public State initialState;
private State _currentState; // Nuværende tilstand
// I denne ordbog kan jeg gemme alle mine states.
private Dictionary _states;
public override void _Ready()
{
// Opretter ordbog, som jeg erklærede tidligere.
_states = new Dictionary();
if (initialState == null) {
GD.Print("Thomas du har glemt at sætte InitialState .. stram op.");
}
// Jeg kigger hvem jeg har som børn :D mmkay.
foreach (Node child in this.GetChildren()) {
if (child is State s) {
_states[s.Name] = s;
s.Initialize();
}
}
ChangeState(initialState.Name); // Skift til start tilstand
GD.Print("StateMachine is ready.");
}
public void ChangeState(string stateName) {
if (!_states.ContainsKey(stateName) || _currentState == _states[stateName] )
{
return; // HOP UD !!! gør ikke mere
}
if (_currentState != null)
_currentState.Exit(); // Vi hopper af nuværende tilstand (oprydning)
_currentState = _states[stateName]; // sætter nuværende tilstand til nye tilstand.
_currentState.Enter(); // Initialisere
GD.Print("Changed state to: " + stateName);
}
override public void _Process(double delta) {
_currentState.Update(delta);
}
public override void _PhysicsProcess(double delta)
{
_currentState.PhysicsUpdate(delta);
}
public void OnTargetReached() {
if (_currentState != null) {
_currentState.NavigationComplete();
}
}
}
==== PatrolState ====
using Godot;
using System;
public partial class PatrolState : State
{
[Export] public Node2D[] waypoints;
[Export] public float ChaseRange = 120f;
private int _currentWaypointIndex = 0;
override public void Enter() {
base.Enter(); // Kalder den klasse State som vi nedarver fra.
Controller.MoveToPosition( waypoints[_currentWaypointIndex].GlobalPosition );
}
override public void Update(double delta) {
if (Controller.PlayerDistance < ChaseRange) {
fsm.ChangeState("Chase");
}
}
override public void NavigationComplete() {
_currentWaypointIndex++;
// Hvis vi er nået til sidste waypoint, så sæt næste waypoint til index 0.
if (_currentWaypointIndex >= waypoints.Length) {
_currentWaypointIndex = 0;
}
Controller.MoveToPosition( waypoints[_currentWaypointIndex].GlobalPosition );
}
}