Tutorial 1: Quickstart
Welcome to the beginning of your journey with Easel! This tutorial is designed to get you up-to-speed as quickly as possible, so you can start making your own games in Easel.
This tutorial takes about 15 minutes to complete, but may take shorter or longer depending on how much time you spend experimenting.
What you will make
In this tutorial, you will make a multiplayer game based on the childhood playground game Tag.
In Tag, one player is the tagger, and they attempt to catch other players while the other players try to avoid being caught. Once the tagger catches another player, that player becomes the tagger, and now they must be the one to chase other players.
Who this is for
This tutorial is designed for anyone who is new to Easel, whether you are new to programming or have many years of experience.
Throughout the tutorial, you will find many infoboxes just like this one. Infoboxes provide additional explanation that you might find interesting, but are not essential to completing the tutorial. If you are new to programming, feel free to skip over the infoboxes as they may be a bit more technical and might not make sense to you yet.
If you get stuck at any point during this tutorial, a great place to ask questions is our Official Discord Community!
Create a project
So you can continue to follow along with this tutorial, you will need to open the Easel game editor in a new tab:
- Right-click the Launch Editor button in the top-right corner of this page.
- Select Open link in new tab.
- When the editor loads, click New Project.
Give your project a name
- Select
easel.tomlfrom the left-hand panel of the editor. - Find the
namefield in the[project]section. It will already have a randomly generated name, likeGenerous Pink Biscuit. - Change the
nametoTag, you're it!, replacing what was there before.
It should look like this, once you are done:
[project]
name = "Tag, you're it!"
# ...
In this tutorial, sometimes we will omit some lines for brevity.
This will be indicated by three dots: ....
Previewing your game
Click the Preview button in the top-right hand corner of the editor. Type in a username (don't use your real name), and then click Play. You'll see a message greeting you to your empty game world. Click Exit Preview in the top-left hand corner to return to the editor.
Setting up the map
Our game of tag is going to take place within the boundaries of a square map. This lets the tagger trap the other players in a corner, making the game more fun. We will now set up the view so that the players can see the entire map on their screen.
- Select the
main.easelfile in the left-hand panel of the editor. - Delete everything that is there and replace it with the following:
pub const BoundaryRadius=25
pub game fn World.Main() {
SolidBackground(#222233)
Camera(body=@(0, 0), radius=BoundaryRadius)
Viewport(aspectRatio=1)
}
Click Preview. You should see a solid dark blue color filling a square on screen. This is the arena in which the players will play tag.
The Main function is the entrypoint of your game,
where you set into motion all the entities and behaviors that make up your game.
It is declared as fn World.Main as it operates on the World entity.
Inside Main, we are adding 3 components to the World - SolidBackground,
Camera, and Viewport.
Creating a hero
Each player will control a character that we will call their hero. We will now spawn heroes, one for each player in the game.
Spawning a hero for each player
Insert the following code snippet inside the Main function, at the bottom:
pub const BoundaryRadius=25
pub game fn World.Main() {
SolidBackground(#222233)
Camera(body=@(0, 0), radius=BoundaryRadius)
Viewport(aspectRatio=1)
SpawnEachPlayer owner {
Subspawn hero {
OverlayContent { "Hello world, I'm " + PlayerName + "'s hero!" }
}
}
}
Click Preview to enter your game. You've successfully spawned a hero yourself! It doesn't do much yet, just displays a greeting, but we will fix that soon.
In this code snippet, we use SpawnEachPlayer to spawn an entity
for each player in the game. In turn, each player entity spawns its hero entity.
We used Subspawn instead of Spawn so
when a player leaves the game, their hero entity will be automatically removed too.
Defining a hero
Now it's time to define what a hero is.
Because the hero will become more complicated as we add more features to it,
we will define it in its own file hero.easel instead of in main.easel.
- Select
main.easel. - Find the
Subspawn heroblock. - Replace its contents with the following highlighted code snippet:
pub const BoundaryRadius=25
pub game fn World.Main() {
// ...
SpawnEachPlayer owner {
Subspawn hero {
Hero
}
}
}
If you click Preview now, you will get an unknown identifier: Hero error because we haven't yet declared the Hero function.
We will do that next.
- Create a new file called
hero.easel. You can do this by clicking the New File button in the bottom left corner of the editor, look for button that has a+sign on it. - Copy-and-paste the following code into your new
hero.easelfile:
pub fn hero.Hero() {
use body=hero
let radius=1
Body(pos=@(SignedRandom*BoundaryRadius, SignedRandom*BoundaryRadius))
PolygonSprite(shape=Equilateral(radius=, numPoints=5), color=#00ff77, crater=0.5, shading=0.25, shadow=0.5)
PolygonCollider(shape=Circle(radius=), category=Category:Default)
}
Click Preview.
You should see a green pentagon representing your hero.
We've put a hole in it (crater=0.5) to make it look stylish, as well as given some shading and a shadow to make it stand out.
We can't yet control it, but we will be doing that next.
A standard entity in Easel has three components:
- A Body, which gives it a position in space.
- A PolygonSprite or ImageSprite, which gives it a visual representation on screen.
- A PolygonCollider, which gives it a physical shape that can collide with other entities and push them around.
Because our hero entity has their own Body, the function begins with a use body=hero statement.
This makes the sprite, collider and any future components (e.g. sounds, cameras, particle effects) automatically follow the hero's position and rotation.
See Context to learn more.
Making your hero move
Now we will make the hero continuously move towards the mouse pointer.
Owner parameter
Because there will be multiple players in the game, each with their own mouse pointer,
we first need to know which player is controlling this hero.
To do this, we will make the Hero take an owner parameter that tells us which player owns this hero.
Select hero.easel. Insert a new parameter [owner] into the Hero function signature, so it looks like this:
pub fn hero.Hero([owner]) {
use body=hero
let radius=1
Body(pos=@(SignedRandom*BoundaryRadius, SignedRandom*BoundaryRadius))
PolygonSprite(shape=Equilateral(radius=, numPoints=5), color=#00ff77, crater=0.5, shading=0.25, shadow=0.5)
PolygonCollider(shape=Circle(radius=), category=Category:Default)
}
This won't do anything new yet, but click Preview to make sure there are no new errors in your code.
Now the Hero knows who its player is. We will make use of this next.
The square brackets [ ] around owner makes it a context parameter.
This means the owner parameter is found implicitly from surrounding context.
Here is an annotated snippet of your main.easel code to illustrate how this works:
SpawnEachPlayer owner { // adds `owner` to context
Subspawn hero {
Hero // uses `owner` from context, no need for `Hero(owner=)`
}
}
See Context to learn more.
Moving towards the pointer
Now we know who owns this hero, we can make it move towards that player's mouse pointer.
Select hero.easel. Insert the following highlighted code snippet inside the Hero function, at the bottom:
pub fn hero.Hero([owner]) {
use body=hero
let radius=1
Body(pos=BoundaryRadius*Random*RandomVector)
PolygonSprite(shape=Equilateral(radius=, numPoints=5), color=#00ff77, crater=0.5, shading=0.25, shadow=0.5)
PolygonCollider(shape=Circle(radius=), category=Category:Default)
on Tick {
let target = @(Pointer.X.Truncate(BoundaryRadius), Pointer.Y.Truncate(BoundaryRadius))
let step = (target - Pos).Truncate(0.25)
if step == @(0, 0) { continue }
Pos += step
Heading = step.Angle
}
}
Click Preview. You should now be able to move your hero around the screen by moving your mouse pointer.
We can simply write Pos and Heading instead of hero.Pos and hero.Heading
because of the use body=hero statement at the top of the function.
See Context to learn more.
Multiplayer
Let's make this game multiplayer so that you can play with your friends!
Displaying the player's name
Players want to know who they are playing with. Before we enable multiplayer, let's add a label underneath each hero with the player's name on it.
Select hero.easel. Insert the following highlighted code snippet inside the Hero function:
pub fn hero.Hero([owner]) {
use body=hero
let radius=1
Body(pos=BoundaryRadius*Random*RandomVector)
PolygonSprite(shape=Equilateral(radius=, numPoints=5), color=#00ff77, crater=0.5, shading=0.25, shadow=0.5)
TextSprite(PlayerName, fontSize=1, color=#fff, noRotation=true, screenOffset=@(0, 1.5), opacity=0.5, vAlign=VAlign:Top)
PolygonCollider(shape=Circle(radius=), category=Category:Default)
on Tick {
// ...
}
}
Click Preview. You should see your name displayed underneath your hero.
We just added a TextSprite component to hero to display the player's name.
Enabling multiplayer
Now it's time to make your game multiplayer!
- Switch back to
main.easel. - Insert the parameter
maxHumanPlayers=10into the parameter list forMain, so it looks like this:
pub game fn World.Main(maxHumanPlayers=10) {
// ...
}
Your game is now multiplayer! To test this out, you can open the game in multiple browser tabs, like this:
- Click Preview.
- Click the Invite button in the top-right corner of the preview window.
- Copy the link and open it in a new browser window.
- Now enter the game in both windows. You should see two heroes on the screen, one for each player.
The players cannot interact with each other yet. We still need to implement the "tag" part of the game!
This automatically makes our game multiplayer, and allows up to 10 players to join at once. See Multiplayer to learn more.
Player colors
Now that we have multiple players, it would be helpful to have each hero be a different color so that players can easily tell them apart.
- Select
hero.easel. - Find the
PolygonSpritestatement - Replace the
color=#00ff77parameter withownerColor=true, so it looks like this:
pub fn hero.Hero([owner]) {
use body=hero
let radius=1
Body(pos=BoundaryRadius*Random*RandomVector)
PolygonSprite(shape=Equilateral(radius=, numPoints=5), ownerColor=true, crater=0.5, shading=0.25, shadow=0.5)
TextSprite(PlayerName, fontSize=1, color=#fff, noRotation=true, screenOffset=@(0, 1.5), opacity=0.5, vAlign=VAlign:Top)
PolygonCollider(shape=Circle(radius=), category=Category:Default)
// ...
}
Click Preview. You should see your hero in a blue color now. If you still have your other tab open, exit and rejoin the game in that tab. You should now see your hero in a different color than the other player.
Easel automatically assigns a unique color to every player in the game. By default, the player always sees themselves as blue so they can easily identify themselves. See Player Colors to learn about how to use and customize the player colors in your game.
Tagging
Now let's implement the tagging mechanic, where one player is the tagger and tries to catch the other players.
Storing the tagged player
To begin, we will create a property called TaggedHero to store which hero is currently tagged.
Select main.easel. Insert the following code snippet at the top of the file, before your Main function:
pub const BoundaryRadius=25
pub prop World.TaggedHero
pub game fn World.Main(maxHumanPlayers=10) {
SolidBackground(#222233)
Camera(body=@(0, 0), radius=BoundaryRadius)
Viewport(aspectRatio=1)
SpawnEachPlayer owner {
// ...
}
}
We've made our TaggedHero a property because a property sends signals every time it changes,
allowing other parts of your code to react to it changing. This will become useful later.
Tagging the first hero
The game begins with no one tagged. We need to tag a hero to start the game!
Select main.easel. Insert the following code snippet inside the Main function, at the bottom:
pub const BoundaryRadius=25
pub prop World.TaggedHero
pub game fn World.Main(maxHumanPlayers=10) {
SolidBackground(#222233)
Camera(body=@(0, 0), radius=BoundaryRadius)
Viewport(aspectRatio=1)
SpawnEachPlayer owner {
// ...
}
on Tick {
if !TaggedHero.Exists {
TaggedHero = QueryAny(filter=Category:Default)
}
}
}
We won't be able to see any difference yet, but click Preview just to make sure there are no errors in your code.
If a player leaves while they are tagged, it leaves TaggedHero pointing to an entity that no longer Exists.
This code makes sure to tag a player not just at first, but also if the tagged player leaves the game.
Fire trail
Let's make it look like the tagged hero is on fire, so it is very clear to the other players who they need to run away from.
Switch to hero.easel. Insert the following code snippet inside the Hero function, at the bottom:
pub fn hero.Hero([owner]) {
use body=hero
let radius=1
Body(pos=BoundaryRadius*Random*RandomVector)
PolygonSprite(shape=Equilateral(radius=, numPoints=5), ownerColor=true, crater=0.5, shading=0.25, shadow=0.5)
TextSprite(PlayerName, fontSize=1, color=#fff, noRotation=true, screenOffset=@(0, 1.5), opacity=0.5, vAlign=VAlign:Top)
PolygonCollider(shape=Circle(radius=), category=Category:Default)
on Tick {
let target = @(Pointer.X.Truncate(BoundaryRadius), Pointer.Y.Truncate(BoundaryRadius))
let step = (target - Pos).Truncate(0.25)
Pos += step
Heading = step.Angle
}
on Tick {
if TaggedHero == hero {
Spark(radius=0.5*radius, color=#f80, dissipate=0.5s, luminous=1, glare=1, bloom=1, bloomAlpha=1, speed=5, layer=-5)
}
}
}
Click Preview. As soon as you enter the game, you should see yourself on fire, because you are now the tagged hero.
Spark is one of the ways you can create a particle system in Easel.
Tagging another player on collision
Now we will make it so when the tagged hero collides with another hero, that hero becomes tagged instead. The tagger will be teleported to a random location on the map so they can't be instantly tagged back by the other player.
Select hero.easel. Insert the following code snippet inside the Hero function, at the bottom:
pub fn hero.Hero([owner]) {
use body=hero
let radius=1
Body(pos=BoundaryRadius*Random*RandomVector)
PolygonSprite(shape=Equilateral(radius=, numPoints=5), ownerColor=true, crater=0.5, shading=0.25, shadow=0.5)
TextSprite(PlayerName, fontSize=1, color=#fff, noRotation=true, screenOffset=@(0, 1.5), opacity=0.5, vAlign=VAlign:Top)
PolygonCollider(shape=Circle(radius=), category=Category:Default)
on Tick {
// ...
}
on Tick {
if TaggedHero == hero {
Spark(radius=0.5*radius, color=#f80, dissipate=0.5s, luminous=1, glare=1, bloom=1, bloomAlpha=1, speed=5, layer=-5)
}
}
on BeforeCollide that {
if TaggedHero == hero {
await AfterPhysics
TaggedHero = that
Pos = BoundaryRadius * Random * RandomVector
}
}
}
Click Preview to test your game.
You will need a second player to test this all works. If you still have your other browser window open, you can just exit the game, then re-enter it. This will get your second player up-to-date with the latest code changes.
You should now be able to tag other players by colliding with them. When you tag another player, you will be teleported to a random location on the map.
When two heroes touch, both of them will receive a BeforeCollide signal, first one then the other.
That means if we are the first hero,
the tag will be returned back to us immediately when the collision is processed for the second hero.
To stop this, we await AfterPhysics so all collision signals are processed before we perform the transfer.
Displaying instructions
To finish off, let's display some instructions so the players know what to do.
Select main.easel. Insert the following code snippet inside the Main function, at the bottom:
pub const BoundaryRadius=25
pub prop World.TaggedHero
pub game fn World.Main(maxHumanPlayers=10) {
SolidBackground(#222233)
Camera(body=@(0, 0), radius=BoundaryRadius)
Viewport(aspectRatio=1)
SpawnEachPlayer owner {
// ...
}
on Tick {
// ...
}
BottomContent {
with TaggedHero {
InsetPanel {
P {
PlayerNameDisplay(TaggedHero.Owner)
" is "
Span(bold=true, color=#ff8800) { "tagged" }
", run away from them!"
}
}
}
}
}
Click Preview. You should now see a message at the bottom of the screen telling you who is tagged. Tag another player and you should see the message update.
The with block updates the instructions every time the TaggedHero changes.
It can do this because TaggedHero is a property and so sends signals whenever it changes.
This is an example of Reactive Programming, a programming style which is fundamental to how you make Easel games.
See User Interfaces to learn more about how to create user interfaces in Easel.
Tutorial complete!
Congratulations, you have made a multiplayer game of tag in Easel!
This would be a great time to try your newly-made game with your friends. You can invite them to your game the same way you did before, by clicking the Invite button in the top-right corner of the preview window, and sending them the link.
See Publishing to learn how to publish your game in a more permanent way.
Recap
In this tutorial, you learned these concepts:
- Project: Creating a project, giving it a name and previewing it in the editor.
- Entities: How to spawn entities and subentities, and how to attach components to entities.
- Physics: How to create bodies and colliders, and how to respond to collision signals.
- Graphics: How to create sprites and sparks, and how to color your sprites based on the player that owns them.
- User Interface: How to display information to the player and have it update automatically when the game state changes.
- Programming Language: How to run code every tick, and how to use context parameters.
- Multiplayer: How to make your game multiplayer with the flick of a switch.
Next steps
There is so much more to learn about Easel! Here are some suggestions for what to do next.
- The Astroblast Tutorial is the next tutorial in the series. It will show you a much more complex game, and teach you many more features of Easel.
Alternatively, the best way to learn is by doing. Consider adding even more features to this game. See how far you can take it! Here are some ideas you can try:
- Add a power-up that makes the tagged player move faster for a short amount of time, making it easier for them to tag other players.
- Add a timer to the game, and make it so the player who is tagged the least amount of time wins.
- Add a scoreboard to the game, so players can see how long they have been tagged for.
Thanks for following along with this tutorial! We hope you had fun and this is just the beginning of many games you will make with Easel.
We would love to hear from you! Join our Official Discord Community and share your game with us, ask questions, and give feedback.
Further reading
- The Key Concepts page covers the most important features of Easel on one page.
- The Language section covers the Easel programming language in full detail.