192 lines
4.9 KiB
C#
192 lines
4.9 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.Threading.Tasks;
|
|
using Godot;
|
|
using Godot.Collections;
|
|
|
|
namespace KarrotStarterTemplate.Common.StateMachines;
|
|
|
|
public class StateInputsPackage
|
|
{
|
|
|
|
/*
|
|
* Potrzebujemy:
|
|
* 1. Informacji jakie klawisze trzyma teraz gracz (np czy trzyma Shift, żeby przejść z Walk na Run) - done
|
|
* 2. Globalnych flag, które sie nie resetują (np. czy gracz wcisnął C i jest w trybie kucania, wciśnięcie C ponownie wyłącza tę flagę) - done
|
|
* 3. Informacji o tym czy w trakcie eventu gracz wcisnął dany klawisz (jeśli gracz kliknie atak w trakcie uniku to musimy zakolejkować to działanie
|
|
* 4. Wartości w wektorach (np inputDirection z WASD) - done
|
|
* 5. Informacji o tym, że skończyła się animacja, albo event z animnotify
|
|
*
|
|
* Gdy OnCheckRelevance coś zwraca, to nie powinniśmy przechodzić w ten state, tylko kolejkować to i sprawdzać czy aktualny stejt ma flagę "canExit" na true,
|
|
* wtedy odpalać ostatni dostępny state?
|
|
*/
|
|
|
|
private readonly Dictionary<string, Variant> _inputs = new Dictionary<string, Variant>();
|
|
|
|
public void Add(string key, Variant value)
|
|
{
|
|
_inputs[key] = value;
|
|
}
|
|
public void Clear(string key)
|
|
{
|
|
_inputs.Remove(key);
|
|
}
|
|
|
|
public Variant Get(string key)
|
|
{
|
|
if (_inputs.TryGetValue(key, out Variant value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
return new Variant();
|
|
}
|
|
|
|
public Vector3 GetVector3(string key)
|
|
{
|
|
if (_inputs.TryGetValue(key, out var value))
|
|
{
|
|
return value.AsVector3();
|
|
}
|
|
|
|
return Vector3.Zero;
|
|
}
|
|
|
|
public string GetString(string key)
|
|
{
|
|
if (_inputs.TryGetValue(key, out var value))
|
|
{
|
|
return value.AsString();
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
public bool GetBool(string key)
|
|
{
|
|
if (_inputs.TryGetValue(key, out var value))
|
|
{
|
|
return value.AsBool();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
[GlobalClass]
|
|
public partial class StateMachine : Node
|
|
{
|
|
[Export] public Node3D owner { get; set; }
|
|
[Export] public State defaultState { get; set; }
|
|
|
|
[Export] public bool activateOnReady { get; set; }
|
|
|
|
public StateInputsPackage package { get; private set; } = new StateInputsPackage();
|
|
public State currentState { get; private set; }
|
|
public bool isTransitioning { get; private set; } = false;
|
|
public bool active => currentState != null;
|
|
|
|
public Dictionary<string, State> states { get; private set; } = new Dictionary<string, State>();
|
|
|
|
public CharacterBody3D ownerCharacterBody => (CharacterBody3D)owner;
|
|
|
|
|
|
// Called when the node enters the scene tree for the first time.
|
|
public override async void _Ready()
|
|
{
|
|
foreach (Node node in GetChildren())
|
|
{
|
|
if (node is State state)
|
|
{
|
|
states.Add(state.id, state);
|
|
}
|
|
}
|
|
|
|
if (activateOnReady)
|
|
{
|
|
await Activate();
|
|
}
|
|
}
|
|
|
|
public async Task Activate()
|
|
{
|
|
await TransitionToState(defaultState);
|
|
}
|
|
|
|
public async void Deactivate()
|
|
{
|
|
if (currentState != null)
|
|
{
|
|
await TransitionToState(null as State);
|
|
}
|
|
|
|
}
|
|
|
|
public override void _PhysicsProcess(double delta)
|
|
{
|
|
currentState?.StatePhysicsProcess(delta);
|
|
|
|
|
|
}
|
|
|
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
|
public override void _Process(double delta)
|
|
{
|
|
if (currentState != null)
|
|
{
|
|
currentState.StateProcess(delta);
|
|
|
|
if (!isTransitioning)
|
|
{
|
|
string nextState = currentState.CheckRelevance();
|
|
if (nextState != null)
|
|
{
|
|
TransitionToState(nextState);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
private Task<bool> TransitionToState(string stateId)
|
|
{
|
|
// if (currentState != null && currentState.id == stateId)
|
|
// {
|
|
// return Task.FromResult(false);
|
|
// }
|
|
|
|
return TransitionToState(states[stateId]);
|
|
}
|
|
|
|
private async Task<bool> TransitionToState(State state)
|
|
{
|
|
if (currentState == state && !state.canTransitionToSelf)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
GD.Print("--");
|
|
GD.Print($"------------------------------------------");
|
|
GD.Print($"------Transitioning from {currentState?.id} to {state?.id}");
|
|
|
|
isTransitioning = true;
|
|
if (currentState != null)
|
|
{
|
|
await currentState.Exit();
|
|
}
|
|
|
|
if (state != null)
|
|
{
|
|
await state.Enter();
|
|
}
|
|
|
|
currentState = state;
|
|
|
|
GD.Print($"------Transition finished to {state?.id}");
|
|
GD.Print($"------------------------------------------");
|
|
GD.Print("--");
|
|
|
|
isTransitioning = false;
|
|
return true;
|
|
}
|
|
} |