Created
August 4, 2025 15:36
-
-
Save CarsonMcKinstry/19a7c3a94ee080fae9812074177433b4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using Godot; | |
| using Godot.Collections; | |
| using ProjectTome.Utils; | |
| namespace ProjectTome.Core.StateMachine; | |
| [GlobalClass] | |
| public partial class StateMachine : Node | |
| { | |
| [Signal] | |
| public delegate void StateChangedEventHandler(string stateName); | |
| private Dictionary<string, StateNode> _nodes = new(); | |
| [Export] | |
| private StateNode? _currentState; | |
| private bool _isTransitioning; | |
| public string? CurrentState | |
| { | |
| get => _currentState?.Name; | |
| } | |
| public bool HasState(string stateName) | |
| { | |
| return _nodes.ContainsKey(stateName); | |
| } | |
| // Called when the node enters the scene tree for the first time. | |
| public override void _Ready() | |
| { | |
| RegisterStateNodes(); | |
| GetParent().Ready += OnParentReady; | |
| } | |
| private void OnParentReady() | |
| { | |
| if (_currentState == null) | |
| return; | |
| _currentState.Transition += HandleTransition; | |
| _currentState.OnEnter(new TransitionData()); | |
| _currentState.ProcessMode = ProcessModeEnum.Always; | |
| GetParent().Ready -= OnParentReady; | |
| } | |
| public void TransitionTo(string stateName, TransitionData? transitionData = null) | |
| { | |
| if (!HasState(stateName)) | |
| { | |
| throw GdException.Error( | |
| $"Attempted to transition to state '{stateName}', which isn't registered to this " | |
| ); | |
| } | |
| var nextState = _nodes[stateName]; | |
| HandleTransition(nextState, transitionData ?? new TransitionData()); | |
| } | |
| private void HandleTransition(StateNode nextState, TransitionData transitionData) | |
| { | |
| if (_isTransitioning) | |
| { | |
| GD.PushWarning( | |
| $"[{GetParent().Name}]: Attempted to transition from {CurrentState} to {nextState.Name} in the middle of a transition." | |
| ); | |
| return; | |
| } | |
| if (!HasState(nextState.Name)) | |
| { | |
| throw GdException.Error( | |
| $"Attempted to transition to state '{nextState.Name}', which isn't registered to this state machine" | |
| ); | |
| } | |
| _isTransitioning = true; | |
| if (_currentState != null) | |
| { | |
| _currentState.ProcessMode = ProcessModeEnum.Disabled; | |
| _currentState.Transition -= HandleTransition; | |
| _currentState.OnExit(); | |
| } | |
| _currentState = nextState; | |
| _currentState.Transition += HandleTransition; | |
| _currentState.OnEnter(transitionData); | |
| _currentState.ProcessMode = ProcessModeEnum.Always; | |
| EmitSignalStateChanged(CurrentState); | |
| _isTransitioning = false; | |
| } | |
| private void RegisterStateNodes() | |
| { | |
| foreach (var child in GetChildren()) | |
| { | |
| if (child is not StateNode node) | |
| continue; | |
| AddNode(node); | |
| } | |
| } | |
| private void AddNode(StateNode node) | |
| { | |
| _nodes[node.Name] = node; | |
| node.ProcessMode = ProcessModeEnum.Disabled; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using Godot; | |
| using Godot.Collections; | |
| namespace ProjectTome.Core.StateMachine; | |
| [GlobalClass] | |
| public partial class StateNode : Node | |
| { | |
| [Signal] | |
| public delegate void TransitionEventHandler(StateNode nextNode, TransitionData transitionData); | |
| public virtual void OnEnter(ITransitionData transitionData) { } | |
| public virtual void OnExit() { } | |
| protected void TransitionTo(StateNode nextNode, TransitionData? maybeTransitionData = null) | |
| { | |
| var transitionData = maybeTransitionData ?? new TransitionData(); | |
| Callable.From(() => EmitSignalTransition(nextNode, transitionData)).CallDeferred(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment