melonJs – Input & Physics

melonJs

Following on from our pervious tutorial in the series using melonJs this tutorial gives a short introduction to keyboard input and basic body physcis to add character movement.

Body & physics

MelonJs makes it simple to detect keyboard input and then react. You can set the position (x, y) of the sprite, but you also have the option of using the physics engine to apply forces. This allows the game engine to determine the movement required for you based on the forces applied. For this to work we need to give our dude a virtual body. A body is required to apply forces.

Add the following lines just after where we call super inside the constructor (before we call the methods to setup the animations):

this.body = new Body(this, (new Rect(16, 16, 16, 16)).toIso());
this.body.collisionType = collision.types.PLAYER_OBJECT;
this.alwaysUpdate = true;
this.body.setMaxVelocity(2, 2);
this.body.setFriction(0.4, 0.4);

Let’s run through line by line to explain what is being setup. The first line is adding a body to our Entity. We need to specify a shape to use for collisions, the easiest to use is a rectangle, we are creating one which is 16 x 16.

The collision type allows us to specify the type of body we need to use; we can simply use player object for this.

Always update means that the object will still be updated when not visible. When we stop the dude leaving the screen in the future, we may not need this but as he can currently walk off we need to make sure he can walk back.

The max velocity is used to define the max velocity and therefore the max speed our character can move. When applying a force this will cap it. Set friction allows for a speeding up and slowing down effect, without this then there won’t be a gradual speeding up and slowing down.

We now have the body setup and ready to start receiving input and applying forces.

Keyboard input

Firstly, we need to tell melonJs which keys we are interested in to allow them to be hooked up. To do this we need to edit our /src/index.ts to setup the bindings. Within the loader.preload function definition add the following to the top of the method (above the state.set lines of code):

input.bindKey(input.KEY.UP, "Up");
input.bindKey(input.KEY.W, "Up");
input.bindKey(input.KEY.DOWN, "Down");
input.bindKey(input.KEY.S, "Down");
input.bindKey(input.KEY.LEFT, "Left");
input.bindKey(input.KEY.A, "Left");
input.bindKey(input.KEY.RIGHT, "Right");
input.bindKey(input.KEY.D, "Right");

The above is telling MelonJs when the specific key is pressed then it will be known as Up, Down, Left, Right. As you can see it is possible to bind multiple keys to the same action, this can be useful it you want multiple keys to carry out the same action. Once the bindings are in place head back to /src/renderables/player.ts. To listen for input, we need to listen to the game loop. There is an update loop which we can use within the Entity. This is called frequently (per frame) and we can use it to handle input. To determine if a key is pressed we can use the built in input.isKeyPressed method. Inside the update loop add the following:

if(input.isKeyPressed("Down")) {
     console.log("Down");
}

If you now run your game and take a loop at the browser console [insert links here] when you press the down key on your keyboard it should print log “Down”. This means we know the input is working.

Let’s now add in our force on the body and add in the other bound key actions, replace the above with:

if(input.isKeyPressed("Down")) {
    console.log("Down");
    this.body.force.y = this.body.maxVel.y
}

if(input.isKeyPressed("Up")) {
    console.log("Up");
    this.body.force.y = -this.body.maxVel.y
}

if(input.isKeyPressed("Left")) {
    console.log("Left");
    this.body.force.x = -this.body.maxVel.x
}

if(input.isKeyPressed("Right")) {
    console.log("Right");
    this.body.force.x = this.body.maxVel.x
}

If you spin up the game again you should find our character now moves up, down, left and right for the relevant keys. The next step will be to add the animations, so he walks and stops in the correct location.

Movement animations

We have already setup our animations ready, so now we need to hook them up to the input. It is important to remember that we have animations in the 4 directions, but we want the character to walk and then be idle in the last direction we were moving in. To allow for this we need to record the idle position we want to use when we stop pressing the key, we also need to know when the key is pressed and then no longer pressed so we can swap the animations.

Firstly, a little preparation, in the player.ts class under where we created the ANIMATION_SPEED we want to create a class variable, add the following line of code:

IdlePosition = "idle_down";

We will use this to record the idle animation we want to show when we let go of the key. We are setting it to idle_down so we can use this as the first animation when we start the game.

Next, replace everything inside your update method with the below:

let keyPressed = false;

if(input.isKeyPressed("Down")) {
    console.log("Down");
    this.body.force.y = this.body.maxVel.y
    this.setAnimation((this.renderable as Sprite), 'walk_down');
    this.IdlePosition = 'idle_down';
    keyPressed = true;
}

if(input.isKeyPressed("Up")) {
    console.log("Up");
    this.body.force.y = -this.body.maxVel.y
    this.setAnimation((this.renderable as Sprite), 'walk_up');
    this.IdlePosition = 'idle_up';
    keyPressed = true;
}

if(input.isKeyPressed("Left")) {
    console.log("Left");
    this.body.force.x = -this.body.maxVel.x
    this.setAnimation((this.renderable as Sprite), 'walk_left');
    this.IdlePosition = 'idle_left';
    keyPressed = true;
}

if(input.isKeyPressed("Right")) {
    console.log("Right");
    this.body.force.x = this.body.maxVel.x
    this.setAnimation((this.renderable as Sprite), 'walk_right');
    this.IdlePosition = 'idle_right';
    keyPressed = true;
}

if(!keyPressed) {
    this.setAnimation((this.renderable as Sprite), this.IdlePosition);
}
        
return super.update(dt);

You’ll notice for each input we have added 3 more lines (after we set the force). The first is to set the animation based on the button being pressed to use the correct direction. We can reuse our setAnimation method we created earlier to do this and pass in the name of the animation. The second change we made it to update the IdlePosition variable to the idle position to use once we stop pressing the key.

This is the name of the idle animation to use. The third change was to record the fact a key is being pressed. At the start of the method, we set this to false and then if any key is being pressed it will be set to true. By examining this at the end it determines if we should switch to the idle animation. If it is required, then the IdlePosition is passed in so it can be set correctly.

If you spin up the game again now and try all directions, you should find that our dude walks around and plays the animation in the correct direction. When you release the key he should stop, the animation stops playing and his idle position is facing the last walking direction.

Well done, you have now grasped the basics to create a top-down character and move him round the screen.

Leave a Reply

Your email address will not be published. Required fields are marked *

WordPress Cookie Plugin by Real Cookie Banner