To keep the state logic minimal, I added the activation/deactivation of PhysicsProcess into my StateMachine, like so:
public void SwitchState<T>() where T : Node
{
var state = states.OfType<T>().FirstOrDefault();
if (state == null)
return;
currentState?.SetPhysicsProcess(false);
currentState = state;
currentState.SetPhysicsProcess(true);
currentState.Notification(5001);
}
This way you don’t need another notification and to worry about this, when creating new states.
One issue I see is sometimes a state may not even have a PhysicsProcess function; So what would happen if you tried to set PhysicsProcess to true when one doesn’t exist?
Note: so far it doesn’t seem to generate errors if one or both of them do not exist.
Any class that inherits from Node will have a _PhysicsProcess method that can be stopped or started, even if you don’t define it. You’ll learn more about inheritance in the upcoming “Refactoring with Inheritance” and “Using Abstract Classes” lectures, but anytime you use a “built-in” method from Godot like _Input, _Ready, or _PhysicsProcess, you’re overriding the functionality in the parent class, hence the method signature public **override** void _PhysicsProcess(double delta).
In @AncientGrief’s example, they use the where generic type constraint to tell the computer that any object passed in as Tmust be of the type Node or inherit from the type Node. So, since we know that state and currentState will always be of type Node, we know that it will always have a _PhysicsProcess method. The only thing we’re concerned about is that currentState is not null when we try to turn off the _PhysicsProcess, which would generate a NullReferenceException.
Justin_Abbott: Yes, we will learn inheritance and unlike C++, C# does not support multiple inheritance; but that’s usually not an issue because you can use multiple interfaces to (for the most part) make up for this short coming. Using interface might also be a better way to handle state machines than using nodes but so far node seems to work fine.
AncientGrief: I don’t know if I would use a null conditional operator in this particular situation ; I would use it when a value has the possibility of being null without actually being an error; but in the situation (dealing with states) it seems that you should only get a null if something is wrong; so I would rather let the exception handling part of the runtime(CLR) to flag the error to make it easier to find the bug so I correct it. You may eventually want to write code that allows you to handle the exception yourself.
We don’t see the rest of AncientGrief’s state machine, but the implementation used in the course has currentState as an exported variable, which could be null if it wasn’t set in the editor. In this specific implementation, perhaps it doesn’t matter if current state is set in the inspector, since AncientGrief is getting ready to update it anyway. Maybe they have some kind of logging set up in the _Ready method to shoot out a warning if it isn’t? Maybe instead of using an array that’s updated through the editor, they just loop through the children of the StateMachine node and set the first child as the default state and want to avoid an error during the first frame? Whatever may be happening elsewhere, they don’t care in this method if there is a state set, they just want to make sure to do something if it is. So, doing some kind of null check is reasonable.
There are a lot of valid ways to set up a state machine. I was just answering your question about trying to call a possibly non-existent SetPhysicsProcess(). If you want to do something different, go for it. It’s your code.
You are right I don’t know how AcientGrief’s state Machine is being implemented; I just took what he was saying based on the current lecture. I also shouldn’t have assumed he was saying to just use a null conditional operator to avoid worrying about or dealing with something being null since he seems to have a good grasp of the language and would already be well aware of the proper ways to deal with them.
The null-conditional was added, because the currentState is null, when I call SwitchState() the first time. I also use this method to set the initial state.
I changed some things in my code, so that all the state stuff happens in code, and not in the editor. The code sets up itself, without the need of me dragging nodes inside the editor. This way I can’t forget it.
If you are new to C#, I would recommend to follow the tutorial as is. I am skipping through the tutorial and add remarks on how to make one’s life easier, but it’s by no means a must-use Just some ideas.
I am in the process of bulding a StateMachine totally decoupled from Godot, since Nodes and Signals are not necessary and overkill imho. As you mentioned you can and should use interfaces in such a case, like this:
public class IdleState : IState
{
public void Enter()
{
//..
}
public void Execute()
{
//...
}
public void Exit()
{
//...
}
}
public class StateMachine
{
private IState currentState;
public void ChangeState(IState newState)
{
currentState?.Exit();
currentState = newState;
currentState.Enter();
}
public void Update()
{
currentState?.Execute();
}
}
This way you don’t need to clutter your Node-Tree. All the states are in your Scripts folder. Each NPC and Player gets a StateMachine Property and you can switch states as you wish.
If you want to take it further you can do something like this:
public void ChangeState<T>() where T : IState, new()
this way the state machine can instantiate your states for you and keep them active as needed.
Or you can declare multiple interfaces:
interface IState ...
interface IPlayerState : IState
interface INpcState : IState
...
//In your NPC Class add this property
public StateMachine<INpcState> StateMachine {get; private set}
This way you can structure your states a little and don’t use a “TalkToPlayerState” meant for an NPC in your Player-Class and then you can even go further and load and instantiate alle the States on Startup (using reflections) like so:
var stateTypes = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => typeof(IState).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
.Where(t => t.GetConstructor(Type.EmptyTypes) != null);
//...
foreach (var type in stateTypes)
{
IState stateInstance = (IState)Activator.CreateInstance(type);
stateInstances[type] = stateInstance; //Save it in a List inside the StateMachine
}
Personally, I went with an approach that just avoid allowing any state or even the StateMachine from overriding the _PhysicsProcess() method since we’re already running it from the Player.cs script.
In my case, I just changed from looking up the actual state to just using state basic integer IDs. It’s faster, involves less data type comparison and fool-proof with the right confirmations implemented.
A misconception of Godot “Node” is that the _PhysicsProcess() run on every Node, but that’s actually false. With a few exceptions, only Canvas Items and Node3D derivatives nodes have the _PhysicsProcess() enabled by default. Other nodes doesn’t pass through the _PhysicsProcess() unless you override it. When a Godot scene becomes active, it generate a list of nodes that should be covered by the _PhysicsProcess(). The exceptions are the nodes that render/read active data (like Viewport types) or the Root node of each scenes (even if they are (white) Nodes).
So by enabling and overriding the _PhysicsProcess() method from within a state node’s script, it’s enabling the node recognition from the Physics Process and, in the case shown in this course, means an upcoming waste of memory and resources.