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.
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)
}
}
(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.
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
}
}
}
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
}
}
}
// ...
}
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" }
}
}
}
}
}
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.