2
\$\begingroup\$

I have a JavaScript canvas game setup using Phaser framework. When I first made my character able to run, jump, and crouch, I used lots of if/else conditions and ended up with an ugly, buggy mess. I have read some articles about finite state machines and that seems to be the solution I need.

I am using javascript-state-machine and can't work out what kind of logic belongs where. My old logic looked something like this:

function update() { // ticks every frame
  if (cursors.right.isDown && !jumping && !crouching) {
    runRight(); // set character velocity and animation
  } else if( // more absurd complexity
}

What should be my first thought when trying to add a movement to the character? Do I think about the input first or define a state on the state machine first?

What exactly is a state? You can be moving right and jumping, but also moving to the right and not jumping. How are those movements described in terms of states?

What checks the user input for keys that are pressed or released and how does that interact with the changing of states?

A very helpful answer would include a psuedo code example of running and jumping that uses javascript-state-machine. I've read this interesting article that addresses character movement with an FSM, but I'm unable to apply those principles in JavaScript, with Phaser, and with this FSM library.

Here's some code from my most recent refactoring attempt in case it helps. I doubt it's even on the right track.

hero.sm = StateMachine.create({
  initial: 'standing',
  events: [
    {name: 'move', from: ['standing', 'jumping'], to: 'running'},
    {name: 'jump', from: ['standing', 'running'], to: 'jumping'},
    {name: 'stop', from: ['running', 'jumping'], to: 'standing'},
  ],
  callbacks: {
    // onEVENT
    onmove: (event, to, from, direction) => {

    },
    // onSTATE
    onrunning: () => {

    },
    // onSTATEleave
    onrunningleave: () => {

    }
  }
})

hero.update = () => {
  if (cursors.right.isDown || cursors.left.isDown) {
    hero.sm.move(cursors.right.isDown ? 'right' : 'left');
  } else {
    hero.sm.stop();
  }
}
\$\endgroup\$
3
\$\begingroup\$

I prefer not to use FSMs for movement, but it can work in certain cases. One of the things FSMs are great for is animations, which is what the linked example would easily tie into. The key thing to notice about the code you are working from is that lateral motion is considered in every one of their states using their function agent:getMovement(). Even in the Falling and Jumping states, the x position is updated based on if the player has one of the movement buttons pressed.

If you want to implement the system in that article, you will have to implement their update function and at the end when they call self:updateState(dt).

function update(currentState, dt) {
    if (currentState == "running") {
        var xv, yv = agent.getVelocity();
        var xm, ym = agent.getMovement();
        agent.setVelocity(xv + 4000 * xm * dt, yv);
    } elseif (currentState == "jumping") {
        ...
    }
}

Interestingly enough, you shouldn't have to implement any of the callback functions for the state machine (unless you want them to start playing an animation or something). The state machine in this case is a simple structure that your update loop uses to keep track of what it should be doing.

hero.sm = StateMachine.create({
  initial: 'standing',
  events: [
    {name: 'move', from: 'standing', to: 'running'},
    {name: 'jump', from: ['standing', 'running'], to: 'jumping'},
    {name: 'stop', from: 'running', to: 'standing'},
    {name: 'fall', from: ['standing', 'running'], to: 'falling'},
    {name: 'hitroof', from: 'jumping', to: 'falling'},
    {name: 'hitground', from: 'falling', to: 'standing'},
  ]});

Again, you should only need callbacks if you need something specific to happen when the state is first entered (such as playing a sound or animation).

\$\endgroup\$
  • \$\begingroup\$ Ah! I have this: github.com/m59peacemaker/game/blob/… So, where I went wrong is including the update functions in that part of the application. That should be a separate concern that just checks with the state machine so that it knows how to update. That makes sense! \$\endgroup\$ – m59 Jan 13 '16 at 21:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.