Skip to main content

Spawning Asteroids

Time for some danger! Let's add some asteroids to the game. If the ship crashes into the asteroids, the ship will be destroyed and the game will be over.

Spawning asteroids

Adding a category

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

Spawning an asteroid every 2 seconds

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 constant to a smaller value (like const MaxAsteroids = 3, for example).

info

The QueryCount function counts the number of entities that match particular criteria.

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 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

When entities collides with each other, they normally change speed due to the collision. That's physics for you! RecoverSpeed is a built-in function that makes an entity gradually return to its original speed after colliding with another entity. Without it, our asteroids will keep hitting each other until they all eventually come to a stop. That would be boring!

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 in a big explosion! Let's create some sparks, then remove 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. The Overlaps function can be used to check if two sets of categories have anything in common. The Expire function marks an entity for deletion from the game. The entity will be despawned during the reap phase near the end of the current tick.

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.

Displaying game over message

In main.easel, insert the highlighted code inside the SpawnEachPlayer block inside your Main function to display a "Game Over" message.

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

SpawnEachPlayer owner {
Subspawn ship {
Ship
}

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

HStack(animate=Animate:FlyFromBottom, align=Align:Center) {
RaisedButton(Main, backgroundColor=Color:Primary) { "Play Again" }
}
}
}
}

// ...
}
warning

Be careful to place this block inside the right set of curly braces {}, there are quite a few here! This snippet should go inside the SpawnEachPlayer block so it only applies to one player. When one player has had their ship destroyed, only they will see the game over message. Other players should not be affected.

info

You can create menus, buttons, heads up displays, etc using Easel's powerful but simple user interface system. Easel uses a reactive programming model to make it easy to dynamically update the user interface based on the state of the game. See User Interfaces to learn more.

Click Preview and you will see something is wrong. The game over message displays straight away, even when the ship is still alive! We need to wait for the player's ship to be destroyed.

Wait for the ship to be destroyed

We simply need to await for the ship to be destroyed before continuing to show the game over message.

Select your main.easel file, find the line Subspawn ship and change it to await Subspawn ship:

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

SpawnEachPlayer owner {
await Subspawn ship {
Ship
}

OverlayContent {
// ...
}
}

// ...
}

Click Preview and try crashing your ship into an asteroid. You should see a "Game Over" message appear.

info

The await keyword lets you wait for things to happen before continuing. See Awaiting to learn more about how await works.