Skip to main content

Multiplayer

It's time to see Easel's most powerful feature - automatic multiplayer.

First, let's get your game ready for more than one player. Then we will show you how can turn on multiplayer with a flick of a switch!

Displaying player names

People want to know who they are playing with. Let's display the player names below their ships.

Select your ship.easel file. Insert the following highlighted code into your Ship function:

pub fn unit.Ship([owner]) {
use body=unit
let radius=1

Body(pos=@(0,0))
PolygonCollider(shape=Circle(radius=), category=Category:Ship)

BoundaryWrappingSystem(radius=)
HealthSystem(maxHealth=100)

TextSprite(PlayerName, screenOffset=@(0, 2*radius), radius=0.5, vAlign=VAlign:Top, opacity=0.5, noRotation=true, stroke=0.25, strobe=false)

with Health {
let proportion = Health / MaxHealth
ImageSprite(@hero.png, color=proportion.Mix(#f88, #fff), radius=1.5*radius, angleOffset=0.25rev, shadow=0.5)
}

// ...
}

Click Preview. You should see a label underneath your character with your name on it.

Aiming line

When multiple players are in the game, sometimes players can get confused about which character is theirs. One simple trick to help with this is to draw a Strand from their character to their cursor. This points not just where they are aiming, but also back at their character so the player can always find themselves.

Select your ship.easel file. Insert the highlighted code into your Ship function just after the TextSprite you just added:

pub fn unit.Ship([owner]) {
use body=unit
let radius=1

Body(pos=@(0,0))
PolygonCollider(shape=Circle(radius=), category=Category:Ship)

BoundaryWrappingSystem(radius=)
HealthSystem(maxHealth=100)

TextSprite(PlayerName, screenOffset=@(0, 2*radius), radius=0.5, vAlign=VAlign:Top, opacity=0.5, noRotation=true, stroke=0.25, strobe=false)
Strand(body=unit, to=Position:AtPointer, color=#00000000, fade=#000000ff, opacity=0.1, radius=0.2, audience=owner, strobe=false, layer=-50)

with Health {
let proportion = Health / MaxHealth
ImageSprite(@hero.png, color=proportion.Mix(#f88, #fff), radius=1.5*radius, angleOffset=0.25rev, shadow=0.5)
}

// ...
}

Click Preview. You should see a very subtle black line pointing from your ship to your cursor. We are keeping it subtle so that it doesn't get in the way of the game, but if you like you could change its opacity to make it stand out more.

info

The audience=owner parameter for Strand means that only the player who owns this ship will see the aiming line.

Wait for players to join

Let's make the game wait until all players have joined before sending them into the dangerous asteroid field. To do this, we will create a simple dialog that appears waits for a player to click a "Start Game" button before proceeding.

Creating the dialog

Select main.easel, and insert the following code just after your Main function:

pub game fn World.Main() {
// ...

SpawnEachPlayer owner {
// ...
}

AsteroidFieldSystem
}

fn dialog.WaitingForPlayersDialog {
BottomContent {
Panel {
VStack {
H3 { "Waiting for players..." }
HStack {
RaisedButton(PressIntent<startGame>, backgroundColor=Color:Primary, expand=true) { "Start Game" }
}
}
}
}
once Pressed<startGame> { CommenceGame }
once GameCommenced { Expire }
}

fn dialog.GameOverDialog([owner]) {
// ...
}

We are not done yet. Preview your game just to make sure you have not made any syntax errors before moving on.

info

The PressIntent intent and the Pressed signal together let you respond to a button press in a dialog.

Showing the dialog

Now we need to make the dialog appear before the game starts.

Select main.easel again, and insert the following highlighted code into your Main function just before the AsteroidFieldSystem:

pub game fn World.Main() {
// ...

SpawnEachPlayer owner {
// ...
}

await Spawn dialog {
WaitingForPlayersDialog
}

AsteroidFieldSystem
}

Click Preview. You should see a dialog with a "Start Game" button. Notice the asteroids will not start coming until you click the button.

Fire lasers to start

Now, the game waits for someone to click "Start Game". We want to reduce the friction for new players to get straight into the action if that is what they want, so let's make it so just shooting your laser will start the game, without needing to click the button.

Select ship.easel. Insert the following highlighted code into your on ButtonDown(Click) handler:

pub fn unit.Ship([owner]) {
// ...

on ButtonDown(Click) {
CommenceGame

while IsButtonDown(Click) {
Spawn projectile {
Laser
}
await Tick(0.5s)
}
}

// ...
}

Click Preview. Now you can just start firing your lasers, and asteroids will start coming without needing to click the "Start Game" button.

info

CommenceGame is a special signal that marks the official start of the game. If you call it more than once, it will have no effect after the first time. By default CommenceGame locks the game so no more players can join.

Enabling multiplayer

Now it is time to make your game multiplayer!

Select main.easel. Insert a maxHumanPlayers=5 parameter into your Main function's parameter list so that it looks like this:

pub game fn World.Main(maxHumanPlayers=5) {
// ...
}

Click Preview, then once you have entered preview mode, click the Invite button in the top right. This will create a temporary multiplayer session at a unique URL that you can share your friends. If you don't have a friend available right now, you can open the URL in a new tab and join the game from both tabs. You should see two ships, one for each player.

You have now achieved something which would normally take years of programming experience: you have created a multiplayer game!

How multiplayer works

In Easel, making a multiplayer game is as easy as making a singleplayer game. You can just act as if all your players are in one shared world, and Easel takes care of the rest. You don't need to worry about network connections or synchronizing game state between players.

Multiplayer is unusually easy with Easel because of one reason. Multiplayer is not just built in, it is baked in to the very fabric of Easel's programming language. Every single line of code you write in Easel is automatically imbued with multiplayer support, without you having to do any extra work. Easel takes care of all the hard parts for you.

info

Easel's multiplayer uses a clever technique called rollback netcode, which allows the game to run smoothly even when the players on other sides of the world! You can read more about how it works in the Multiplayer section of the documentation.