Guides
Controlling player movement

Using platfomer to control player movement

To control the player the movement, the scripting API has a builtin Platformer controls which provides a WASD type of navigation. WASD is named after the keys W, A, S, D, it's a popular scheme for controlling character movement in 3D game

KeyAction
W or ArrowUpMoves the character forwards
A or ArrowLeftMoves the character to the left
S or ArrowRightMoves the character to the right
D or ArrowDownMoves the character backwards
SpacePerform a jump on the character
Shift + Direction KeySpeeds up character movement in that direction

Creating Platformer controls

Here's an example on how to create the controls

import { Camera, Player, Controls } from "@oo/scripting";
 
 
export default class GameManager {
 
    controls = null;
 
    // ...
 
    onReady() {
 
        this.controls = this.controls = Controls.get({
            type: "platformer",
            object: Player.avatar,
            target: Camera,
        })
    }
 
}

The example uses ControlsFactory.get() method to instantiate the controls. Let's look at the options passed to the method:

  • type: we used "platformer" to create the WASD type navigation controls
  • object: the character moved by the controls. We set it to the main player avatar
  • target: is used to determine the forward direction of the movement. We want the player to move in the direction where the camera is looking.

When specifying "platformer" as type, the factory returns an instance of PlatformerControlsWrapper.

Customizing Platformer's parameters

We can also provide additional parameters to control some aspects of the controls like character speed, jump height ...

import { Camera, Player, Controls } from "@oo/scripting";
 
 
export default class GameManager {
 
    //...
 
    this.controls = Controls.get({
        type: "platformer",
        object: Player.avatar,
        target: Camera,
        params: {
            run : {
                maxSpeed : 15, // Default is 10
                boost: 2, // Default is 1.5
            },
            jump: {  
                height: 10, // Default is 3
            },
            autoAnimate: false // Default is true
        }
    })
}

We pass the parameters in the params field. The shape of the params depend on the type of controls we create. For Platfomer controls, the possible params are:

  • run.maxSpeed : this is the walking speed of the charcater
  • run.boost : how much to accelrate the charcater when Shift is pressed
  • jump.height : max height the character will reach when Space is pressed
  • autoAnimate: tells wether the controls should set the appropriate animation on the charcater depending on its current state (running, jumping, idle ...). This only applies when the attached object is an AvatarComponent

Below is a table of animations applied to the character, depending on its state

StateAnimation nameNotes
Not moving"idle"
Walking"walk"
Running"run"
Jumping"jump"
Flying"fly"the flying state happens if the character stays on the air for a long duration

You can customize the animation behavior in 2 ways

  • If you want to change how the standard animations ("idle", "walk", ...) look all toghether in the game. You can upload custom animations using the VRM animation component on the studio. See Customizing avatar animations for more info.

  • If you want to control the logic of picking which animation to choose, set autoAnimate to false. You can then use the assign the desired animation to the attached object manually (e.g. Player.avatar.animation = "...").

Activating / Deactivating

At some moment in the game you may want to deactivate the controls (e.g when the game is paused). You can use the controls active property to activate or deactivate the controls, for example:

    controls.active = false

Note that deactivating the controls will stop the attached characted movement at the moment of invocation. So if the character is in the middle of jump, for example, the character will stop in mid air (Stopping the character movement refer only to changes in position or rotations of the character, the last assigned animation will keep playing).

If you want the controls to keep processing past actions (like keep falling from the current jump), but prevent further user input from affecting its state, you should write instead

    controls.controller.active = false

This will keep the controls responsive to the game update events, but ignore future user inputs.

The Platformer controller is the object responsible of translating user input (keyboard, joystick) into meaninful actions for the controls (forward, left ...). It does so by listening to the relevent user events (keyDown, keyUp ...) then toggling the appropriate field on the actions property of the controller.

This separation gives you a greater control over the platformer's behavior, and make it possible to implement some custom character control without having to write a new custom controls from scratch.

For example, suppose you're making a Statues like game (opens in a new tab). Where the only action possible is moving forward. Furthermore the user doesn't have to keep pressing the ArrowUp key to keep moving. When the players are allowed to move, the user can just press the key once and the character should keep moving until the next key press.

Let's see how to implement this behavior using the platformer

import { Camera, Player, Controls,  } from "@oo/scripting";
 
 
export default class GameManager {
 
    controls = null;
 
    // ...
 
    onReady() {
 
        this.controls = Controls.get({
            type: "platformer",
            object: Player.avatar,
            target: Camera,
        })
 
        this.controls.controller.active = false
 
        Emitter.on(Events.KEY_DOWN, this.onKeyDown)
    }
 
    onKeyDown = (e) => {
 
        if (e.repeat || e.key !== "ArrowUp") return 
        
        this.controls.actions.forward = !this.controls.actions.forward
 
    }
 
    onStop() {
 
        Emitter.off(Events.KEY_DOWN, this.onKeyDown)
    }
 
}

In the above example:

  • We disabled the platformer's controller to deactivate the default input binding
  • Then we attached our own event listener that will set the actions.forward property directly as appropriate

🚧 Applying kinematic forces

TBD