How to: Top-down Joystick movement
One of the most first things you'll want to do when creating a top-down game is to allow the player to move their character around. This guide will show you how to set up a simple top-down character movement system that works when using the WASD keys, a gamepad and an on-screen joystick for touchscreen players.
The simplest way to support all the various control schemes is to use the Joystick as your primary input method, and then use KeyboardVirtualJoystick and VirtualJoystick to simulate a joystick when a Gamepad is not connected.
Joystick control
In this example, the joystick is the canonical input method for moving the character. All other forms of input will map to the joystick. So let's first get the joystick to move the character:
let speed = 20
on BeforePhysics {
if Joystick == @(0, 0) {
await Joystick
continue
}
ForcefulStep(Joystick * speed / TicksPerSecond)
Heading = Angle(Joystick) // Make the character look where they are going
}
We use a on block to run this code before the physics simulation step every tick.
ForcefulStep is used to move the character in the direction of the Joystick.
The step is forceful, meaning if our character collides with anything during its step, it will push it out of the way.
We also adjust the Heading of the character to face the direction it is moving.
When the Joystick is a zero Vector @(0,0), we use await Joystick to put the behavior to sleep.
This is useful because some players might never use the Joystick, for example if they are using the mouse instead.
You will also notice that we specify speed as a variable at the top of the code.
This could instead come from a function parameter, for example.
We then divide the speed by TicksPerSecond to calculate how much of the speed
we should apply each frame.
Keyboard
Now your joystick is working, you can now use KeyboardVirtualJoystick to map the keyboard arrow keys to the joystick.
KeyboardVirtualJoystick(up=ArrowUp, left=ArrowLeft, down=ArrowDown, right=ArrowRight)
You can also support WASD keys by using ButtonRemap to remap the WASD keys to the arrow keys:
ButtonRemap(KeyW, ArrowUp)
ButtonRemap(KeyA, ArrowLeft)
ButtonRemap(KeyS, ArrowDown)
ButtonRemap(KeyD, ArrowRight)
We recommend you put all of these remappings at the top of your Main function.
Set this once at the top of Main then you can forget about the keyboard everywhere else.
The rest of your codebase will only use the Joystick, as that is the canonical input method.
Touchscreens
The VirtualJoystick function will display a virtual joystick which players can use to control their character. In most cases, this is only useful for touchscreen users, so you can use the TouchscreenOnly to limit this to touchscreen users.
As VirtualJoystick inserts a user interface element,
it must be placed inside a Section of the user interface.
The most reliable way to do this is to place it inside a BottomLeftCommand
which places it in an overlay on top of the game screen.
BottomLeftCommand<joystick>(offset=@(6,6)) {
TouchscreenOnly {
VirtualJoystick(radius=5)
}
}
Since BottomLeftCommand is an overlay, it requires an offset.
Make sure to give it an offset that is bigger than the radius of the VirtualJoystick
so that the joystick is clear of the corner of the screen.
Full code listing
This is one example of a complete game that supports top-down movement with a Joystick, WASD keys
and a virtual joystick for touchscreen users.
If you would like to try it, click the Launch Editor button in the top right of this page,
create a new project and use copy-and-paste to overwrite the contents of the main.easel file with the code below.
pub game fn World.Main() {
KeyboardVirtualJoystick(up=ArrowUp, left=ArrowLeft, down=ArrowDown, right=ArrowRight)
ButtonRemap(KeyW, ArrowUp)
ButtonRemap(KeyA, ArrowLeft)
ButtonRemap(KeyS, ArrowDown)
ButtonRemap(KeyD, ArrowRight)
SpawnEachPlayer owner {
BottomLeftCommand<joystick>(offset=@(6,6)) {
TouchscreenOnly {
VirtualJoystick(radius=5)
}
}
Subspawn unit {
Ship
}
}
}
pub fn unit.Ship([owner]) {
use body=unit
use speed=30
use radius=1, shape=Equilateral(numPoints=3), color=#00ff00
Body(pos=@(0,0))
PolygonSprite
on BeforePhysics {
if Joystick == @(0, 0) {
await Joystick
continue
}
Heading = Angle(Joystick)
ForcefulStep(Joystick * speed / TicksPerSecond)
}
}