Skip to main content

Asteroids

In this section, we are going to add asteroids to the game. If the ship crashes into the asteroids, the ship will be destroyed and the game will be over.

Spawning asteroids

All physical objects need a category, so select your categories.easel file and insert the highlighted code to add a category for asteroids.

pub tangible category Category:Ship
pub tangible category Category:Asteroid

Next, let's spawn an asteroid every 2 seconds, until we have reached our limit of 25 asteroids at any one time. We will use a with block to make our code for this repeat every 2 seconds.

Select main.easel and insert the highlighted code into your Main function at the bottom.

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

SpawnEachPlayer owner {
// ...
}

with Tick(2s) {
const MaxAsteroids = 25
if QueryCount(filter=Category:Asteroid) < MaxAsteroids {
Spawn asteroid {
Asteroid
}
}
}
}

This calls an Asteroid function, which we will define next.

tip

If during this tutorial you are finding the game too hard, try changing the MaxAsteroids value to a smaller value (like const MaxAsteroids = 3, for example).

Defining asteroid behavior

Let's create our Asteroid function to define the behavior of an asteroid. Similar to Ship, we want to give it a Body and Collider, but this time we are going to create its graphics by emitting a Spark on every tick.

Create a new asteroid.easel file. You can do this by clicking the New File button in the bottom left of the editor. Insert the following code into your new asteroid.easel file.

pub fn asteroid.Asteroid() {
use body=asteroid
use radius=4, speed=8, dissipate=0.75s, color=#ff0000

Body(
pos = (BoundaryRadius + radius + 5) * RandomVector,
velocity = speed * RandomVector,
)
PolygonCollider(shape=Circle, category=Category:Asteroid)
RecoverSpeed

WrapOutsideBoundary

on Paint {
Spark(color=, fade=color.Darken(0.5), splatter=0.5, layer=-10, shadow=0.5)
}
}
note

(BoundaryRadius + radius + 5) * RandomVector is a simple way of generating a random position outside the boundary as the asteroid's initial location. The 5 is a term that coders would colloquially call a 'fudge factor' - it is a number we have made up to make sure the asteroid really does get spawned outside the boundary. There are certainly smarter ways to do this, but this is good enough for now.

info

Sometimes when an entity collides with another, it either loses or gains speed in the collision. RecoverSpeed is a built-in function that makes an entity gradually return to its original speed after colliding with another entity.

Click Preview, then enter your game and wait a few seconds. You should see some asteroids spawn and move around the screen. Try flying around and crashing into them. You'll notice that the ship can collide with asteroids but nothing else happens yet.

Asteroids destroying the ship

Crashing into an asteroid should destroy the ship. This will involve creating some sparks and then removing the ship from the game.

In ship.easel, insert the highlighted code at the bottom of your Ship function, before the closing brace }.

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

with Pointer {
// ...
}

on BeforeCollide that {
if that.Category.Overlaps(Category:Asteroid) {
repeat 5 {
Spark(color=#ffffff, luminous=1, bloom=3, feather=1, dissipate=0.75s, splatter=1, speed=10)
}

Expire
break
}
}
}
info

The repeat block is a simple way to run a block of code multiple times.

Click Preview and try flying your ship into an asteroid. You should see the ship explode. If you're having trouble flying the ship into an asteroid, one tip is to fly the ship to where the asteroid is going to be, not where it is. You might also find you have better control over your ship if you only thrust your engine in short bursts, rather than holding down the button continuously.

Game over message

Let's make the game display a "Game Over" message when the ship is destroyed. Even though we only have one player for now, we are going to do this in a way which will make it easy to support multiple players later.

First, if a player loses their ship, we mark them as eliminated from the game by calling the Eliminate function.

Select your main.easel file and insert the highlighted code into your Main function:

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

SpawnEachPlayer owner {
Subspawn ship {
Ship

once BeforeDespawn {
Eliminate
}
}
}

// ...
}
warning

Be careful to place this block inside the right set of curly braces {}, there are quite a few here! Coding is a highly delicate and precise operation, and even a small mistake can stop your code from working as expected.

In main.easel, insert the highlighted code at the bottom of your Main function to display the "Game Over" message once all players are eliminated.

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

on PlayerEliminated {
if !QueryAnyPlayer(isEliminated=false) {
ConcludeGame
break
}
}

once AfterGameConcluded {
OverlayContent {
VStack(align=Align:Center) {
Standout { FlyInWords("Game Over") }

P(animate=Animate:FlyFromBottom) {
RaisedButton(Main, backgroundColor=Color:Primary) { "Play Again" }
}
}
}
}
}
info

Menus, buttons, heads up displays, etc are collectively called the User Interface or UI in computer programming terms. Easel has a powerful yet simple system to create reactive user interfaces. See User Interfaces to learn more.

Click Preview and try crashing your ship into an asteroid. You should see a "Game Over" message appear, and you can click "Play Again" to start a new game.