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.
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).
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)
}
}
(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.
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! This is how we can create 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
}
}
}
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.
Marking the player as eliminated
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
}
}
}
// ...
}
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.
Displaying game over once eliminated
In main.easel
, insert the highlighted code inside the SpawnEachPlayer
block inside your Main
function
to display the "Game Over" message once all players are eliminated.
pub game fn World.Main() {
// ...
SpawnEachPlayer owner {
Subspawn ship {
Ship
once BeforeDespawn {
Eliminate
}
}
once AfterEliminated {
OverlayContent {
VStack(align=Align:Center) {
Standout { FlyInWords("Game Over") }
HStack(animate=Animate:FlyFromBottom, align=Align:Center) {
RaisedButton(Main, backgroundColor=Color:Primary) { "Play Again" }
}
}
}
}
}
// ...
}
Make sure you place this inside the SpawnEachPlayer
block!
We are doing this so that if the game is over for one player, it is not over for other players.
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.