@rbxts/roblox-state-machine
v1.0.1
Published
A simple finite state machine implementation for RobloxTS
Readme
RobloxStateMachine
A simple finite state machine library very much inspired by prooheckcp's RobloxStateMachine.
Was mainly done because I wanted to port RobloxStateMachine to RobloxTS.
StateMachine
constructor(InitialState, States, MustCallHeartbeatManually, Data)
Parameters
- InitialState - The initial state that the state machine will enter. Accepts the state name or constructor.
- States - An array of states that the state machine will be able to access and switch to.
- MustCallHeartbeatManually - If true, the OnHeartbeat method in every state will have to be called manually with the StateMachine.ExecuteHeartbeatEvent method.
- Data - Can be anything you wish. This data will be visible by all states and transitions. Usually an object.
import { StateMachine, State, StateConstructor } from "@rbxts/finite-state-machine";
function RequireStates(Path: Folder) {
return Path.GetChildren().map(Child => require(Child as ModuleScript) as StateConstructor);
}
const NPC = {
Instance: Workspace.NPC,
Health: 100,
}
const Machine = new StateMachine(
"Roaming",
RequireStates(ReplicatedStorage.States),
false,
NPC
);ChangeState(State)
Used to change the state of the machine.
Parameters
- State - Can be either a string, a constructor or one of the states you get by calling
StateMachine.GetStates()
GetCurrentState()
Returns
- State - The
Statethat is currently on.
GetPreviousState()
Returns
- State - The previous
Statethat used to be on.
GetStates()
Returns
- States - An array of states that the state machine has.
ExecuteHeartbeatEvent(Delta)
A method that when called, calls the OnHeartbeat method of both the current state and the current state's transitions.
Parameters
- Delta - The delta time of the heartbeat event.
Destroy()
Stops the machine from running.
StateChanged
An event that fires whenever the state machine changes states
StateMachine.StateChanged.Connect((NewState, OldState) => {
// ...
})StateMachine.ExecuteHeartbeatEvents(Delta, OnlyManualStateMachines)
A Static method that can be called to execute the OnHeartbeat methods on ALL the active StateMachines. Useful for when you want to call the OnHeartbeat events at different times/with different DeltaTime or use some other event like RenderStepped.
Parameters
Delta - The delta time
OnlyManualStateMachines - If true, will call the OnHeartbeat events only on State Machines that have MustCallHeartbeatManually to true. Otherwise only the ones who have it to false.
Creating States
Let's say we have a state named Roaming.
import { State } from "@rbxts/roblox-state-machine";
import { NPCType } from "./Types";
// The Data parameter is the 4th parameter of the state machine constructor, and the type of data can be specified in the State<Data> type.
export = class Roaming extends State<NPCType> {
// The transitions that this state will use. Is optional.
// Documentation about transitions is further below.
Transitions = []
OnEnter(Data: NPCType) {
// This is called when the state machine switches to this state.
// Data can also be accessed with this.Data
}
OnLeave(Data: NPCType) {
// Called when the state machine leaves this state
}
OnInit(Data: NPCType) {
// Called when the state machine is constructed.
}
OnHeartbeat(Data: NPCType, Delta: number) {
// Called every heartbeat or when StateMachine.ExecuteHeartbeatEvent is called while this state is currently on.
if (Data.Anger > 100) this.ChangeState("Angry");
}
OnDestroy(Data: NPCType) {
// Called when the state machine is destroyed.
}
// A method that is called everytime the state machine tries to change states. If it returns false, the state machine won't change the current state.
CanChangeState(Target: State<NPCType>) {
// In this scenario, if the machine wants to switch to the Running state while it's in the Roaming state (this state), don't allow it to switch.
return Target.Name !== "Running"
}
}And now we can create a state machine that uses this state.
import { StateMachine } from "@rbxts/roblox-state-machine";
import Roaming from "somewhere";
const NPC = ...
const Machine = new StateMachine("Roaming", [Roaming], false, NPC);Do note that the machine starts immediately after it's constructed.
Transitions
Transitions can help with transitioning states. They serve as a little bonus on top of states.
Again, let's say we have a file named RunningTransition.
import { Transition } from "@rbxts/roblox-state-machine";
import { NPCType } from "./Types";
export = class RunningTransition extends Transition<NPCType> {
// The target state that the transition will make the state machine transition to. Can be either a string, a State from StateMachine.GetStates() or a state constructor. See the OnHeartbeat method here.
TargetState = "Running";
OnEnter(Data: NPCType) {
// This is called when the state machine switches to the state that uses this transition.
// Data can also be accessed with this.Data
}
OnLeave(Data: NPCType) {
// Called when the state machine leaves the state that uses this transition.
}
OnInit(Data: NPCType) {
// Called when the state machine is constructed.
}
OnHeartbeat(Data: NPCType, Delta: number): boolean {
// Called every heartbeat or when StateMachine.ExecuteHeartbeatEvent is called while the state that uses this transition is currently on. If this method returns true, the state machine will switch to the TargetState
// In this case, if the npc anger is over 100, we will transition to the running state (Because the TargetState is set to Running).
return Data.Anger > 100
}
OnDestroy(Data: NPCType) {
// Called when the state machine is destroyed.
}
}We can apply this transition to a state like so:
import RunningTransition from "somewhere";
// RunningTransition can be used for other states, too.
class Roaming extends State<NPCType> {
Transitions = [RunningTransition]
}Final notes
States and transitions can inherit from other states/transitions.
States and Transitions have most of the methods that StateMachine has.
