State Machine

jme-state-machine Github

jMonkeyEngine state machine helps to define complex behaviour using states. This way, one can avoid cluttered code with lots of if-else branches checking if the model is walking or running, then do x or y… This way complicated physics/input handling that are tight to some states, need only there be implemented.

Implementation and architecture are based on the book “Game Programming Patterns”, chapter State.

In the examples directory on Github, a fully working example is shown using the Sinbad model. States used in that example are found here: example states.

The State Machine consists of four entities: ModelStateMachine, Layer, State, StateChange.
One creates a state machine in jMonkeyEngine using the following:

ModelStateMachine modelStateMachine = new ModelStateMachine();
spatial.addControl(modelStateMachine);

To add a layer, one needs to set the default state.

Layer modelBaseLayer = new Layer();
modelStateMachine.addLayer(modelBaseLayer);
modelBaseLayer.setInitialState(new IdleState());

The idea is to go to a new state by one of the following methods:

  • handleActionInput
  • handleAnalogInput
  • controlUpdate

There are multiple ways to change state. return on one of the above methods a StateChange instance by using either:

  • StateChange.to(new SomeOtherState())
  • StateChange.push(new TemporaryState())
  • StateChange.pop()
  • null if the state does not need to be changed.

The state machine is a pushdown automaton, meaning it is possible to push a state temporarily on the stack (and afterwards pop it from that state).
In the video this happens, when Sinbad runs into the sword, it picks it up, and then returns back to the state it was before.

Create a class that extends the abstract State class, and you are good to go.
When a state is entered, automatically the onEnter method is called by the state machine. Likewise, on leaving a state, the onExit method is called.
In these methods, attach (and detach) bindings to physics tick listeners and start (and stop) animations.

Layers are used when different states need to be defined for different parts of the model, for instance equipment versus torso behavior. In the example in this project, a layer is created for the “look at sword behavior”. It uses states from the other layer to know when the sword is attached to the model using:

if (getLayer()
       .getLayers()
       .stream()
       .anyMatch(l -> l.hasState(PickUpSwordState.class))
) {
    return StateChange.to(new IdleState());
}

If anyone is interested in improving this functionality, please let me know. @sgold if this a shot of making a pull request into the engine, that would be awesome :unicorn:.
Thanks for reading. :slight_smile:

10 Likes

a) I don’t think it’s really appropriate for the engine. There are about 5,424,623 different ways to do a state machine and they will all have various tradeoffs that are somewhat game or context specific.
b) including it in the engine means you only get to update it every 4 years or so… which is also probably not what you want.

Presumably, you want this to evolve and harden as folks use it. Tying it to JME’s glacial release cycle is the opposite of that… and it’s why we work to move some things OUT of the engine.

5 Likes

Like Paul, I’d want to see broad acceptance of jme-state-machine before integrating it into the Engine.

Looking at the timeline of JME3 feature releases, the process does seem glacial:

  • 3.0.0: 2013
  • 3.1.0-stable: 12 February 2017
  • 3.2.0-stable: 5 January 2018
  • 3.3.0-stable: 30 March 2020
  • 3.4.0-stable: June[??] 2021

The 4-year gap between 3.0 and 3.1 was exceptional, not typical. Going forward, I expect annual feature releases, provided we can come up with compelling, new features (such as state machines).

The thing is, if it turns out that jme-state-machine is lacking some vital feature, 12-15 months would be an awfully long wait for resolution. Important libraries like Lemur and Minie are separate from the Engine so they can follow their own release cycles.

Furthermore, software requires ongoing maintenance. In the past, lots of code was contributed to JMonkeyEngine by people who then left the project. The maintenance burden has fallen on a relatively small group of volunteers, and lots of code hasn’t been seriously maintained for many years. As long as jme-state-machine lives in your repo, nobody will expect the core team to maintain it.

7 Likes

That makes sense, thanks for your answers. I’ll make sure that the project gets broad acceptance then :crazy_face: I’ll keep the development mostly to myself and will answer to feature requests and maintenance.

1 Like

I’d also add that

c) core library must ensure the compatibility with the jme ecosystem. Does it work well with, say, LWJGL3? Minie? and so on…

As @pspeed said, this library does not introduce a general purpouse feature (i.e. rendering related), but is quite context and game specific. Which isn’t bad per se; it becomes bad when somebody attempt to shoehorn it into use cases which aren’t fitting at all.

Also: with these projects the examples and documentation (best practices etc) are possibly even more important than the actual code.

2 Likes

jme-state-machine has no implementations related to rendering and physics. It is as you say - general purpose, it only enables a developer of writing code (such as physics and input) better and more bug-free in the jme eco system.

You are right that documentation and best practices are required, I’ll add a bunch.

1 Like

I remember having to build one of these for a game I was building in Godot. After that, this library is looking pretty neat! If you put it on Maven or something as a library, I may implement it in one of my own games…

2 Likes

Hi guys,

I have improved the documentation. I have added more explanations and examples.

Also I have tried to release the project using mvn on GitHub, but it seemed a lot more involved than I thought. I’m not used to release my mvn projects this way. So it isn’t working from command line yet.
I added the jar for now manually, but I don’t know if that will work out for you. Will look into it later

I use libgdx-ai and that features a general-purpose behavior tree implementation which helps a lot to reduce code complexity and avoids as well greatly the if then else hell. I personally like behavior trees better than state machine even tho state machines are more used as it is kind of easier to get. Took me a while to understand behavior trees. Anyway a library I would say not in the game engine itself. This is the really really great part of java that you easily can mix and match any lib out there which is available in the maven repo.
I usually use lemur, zay-es, libgdx-ai, feather and many more beside jme.

Interesting, behaviour trees are not generally used for controlling a character. A lot of complexity and functionality that come with behaviour trees will be unused since the user is defining the behaviour himself. Both unreal and unity use state machines for that. This State Machine project is targeted specifically towards creating a complex Character Controller made simple.