Jump to content

Documentation for the KerbalFSM state machine (help needed)


Recommended Posts

The api available here:

https://kerbalspaceprogram.com/api/class_kerbal_f_s_m.html

simply documents the methods, but there is no real explanation for this.

I've searched the forums, but while it is referenced in many places, it is not explained.  Apparently it was written back for 0.16 by @HarvesteR, the earliest mention of this was found in a Devnote Tuesday back in 2014 here:  

 

Besides the fact that I may be able to use this for new mods, I'm now trying to understand how it works in a reverse-engineering way on a mod I'm maintaining (NASA Countdown Clock).

So, I'm going to attempt to write my own documentation here.  I will incorporate any corrections or additions into the OP to try to keep a single point for reference.  This will by necessity also include documentation on  KFSMState and KFSMEvent.  Some will be redundant since I will be copying portions of the public API

=================

The state machine available for KSP is called KerbalFSM.  Two classes which it needs are KFSMState and KFSMEvent

  • KerbalFSM is the main class for the state machine, essentially this is the controller
  • KFSMState contains information related to a specific state of the machine, such as what events will activate the state
  • KFSMEvent contains information related to a specific event.

The flow of events appears to be:

  1. Create state machine (KerbalFSM)
  2. Create various states which will be added to the state machine
  3. For each state, create events which are added to the state
  4. Finally, add the states to the state machine.

I've included some code in the spoiler which summarize the above points:

Spoiler



// Create the state machine
private KerbalFsmEx _machine = new KerbalFsmEx();

// Create States (the classes derive from KFSMState)

InitialState initial = new InitialState("Init", _machine);
SettingState settings = new SettingState("Settings", _machine);
SequenceState sequence = new SequenceState("Sequence", _machine);
KFSMState finish = new KFSMState("Finish");

// Define some local events and add to the states 

var go2Settings = new KFSMEvent("Settings") { GoToStateOnEvent = settings, updateMode = KFSMUpdateMode.MANUAL_TRIGGER };
initial.AddEvent(go2Settings);

var go2Init = new KFSMEvent("Init") { GoToStateOnEvent = initial, updateMode = KFSMUpdateMode.MANUAL_TRIGGER };
settings.AddEvent(go2Init);
sequence.AddEvent(go2Init);
finish.AddEvent(go2Init);

// Add states to the state machine

_machine.AddState(initial);
_machine.AddState(settings);
_machine.AddState(sequence);
_machine.AddState(launch);
_machine.AddState(finish);


 

 

Now it gets a bit hazy...

The state machine needs to be started using the StartFSM method.  There are two ways of calling:

public void StartFSM(KFSMState initialState);
public void StartFSM(string initialStateName);

The first starts the machine by passing in the method of the initial state.

The second starts the machine by passing in the name of the initial state.

While the state machine defines methods for FixedUpdateFSM, LateUpdateFSM, and UpdateFSM, it doesn't call those itself, so you need to write the code for that, such as the following:

Spoiler

public void FixedUpdate()
{
	if (_machine.Started)
		_machine.FixedUpdateFSM();
}

public void Update()
{
	if (_machine.Started)
		_machine.UpdateFSM();
}

public void LateUpdate()
{
	if (_machine.Started)
		_machine.LateUpdateFSM();
}

 

 

Link to comment
Share on other sites

A good example of how an FSM works in game is the docking ports. They set their behavior using a KerbalFSM and different KFSMStates and KFSMEvents, with each state identifiable by a string.

I spent a long time digging into this while making my flexible docking ports mod, though it basically just works on top of the FSM: https://github.com/DMagic1/KSP_Flexible_Docking/blob/master/Source/FlexoTube.cs#L304-L351

 

Each state for the docking port would just define its behavior in the FixedUpdateFSM method. So when it's in the "ready" state then it cycles through all of the docking ports in the scene checking if they are within distance, the right size, etc...

Then when it finds a suitable matching port it changes (using a KFSMEvent, I assume) to the "acquiring" state (the dominant port is chosen based on vessel mass; I'm not sure how it chooses if they are the same mass), where it applies a force to bring the vessels together and makes another distance check (and other checks for rotation, alignment, etc...). Then it can switch to the "docked" state.

It also uses the state to set the part's menu UI. Whenever it changes state it resets the part menu, so that you get the different buttons like undock, or decouple (or whatever it says) when the port is in the "PreAttached" state, when a port is connected to something in the editor.

 

So rather than having a horribly convoluted FixedUpdate method, the docking ports just set up the FSM when it starts. Then each behavior is its own contained component, which makes it easier to manage, and change or add new behaviors.

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...