Plasma Bolts
We just added asteroids to the game. Now let's add some weapons to the ship so we can fight back. In this section we will add plasma bolts that shoot out of the front of the ship, and make the asteroids subdivide into smaller asteroids when they are hit.
Spawning plasma bolts
First, we need to define a new category for our plasma bolts, as all physical objects need a category.
Select your categories.easel
file and insert the highlighted line at the bottom:
pub tangible category Category:Ship
pub tangible category Category:Asteroid
pub tangible category Category:Projectile
Now let's create a PlasmaBolt
function to define the behavior of a plasma bolt.
We are going use the built-in Streak function to make the plasma bolt look
like a glowing streak.
When the plasma bolt collides with something, it will break up into sparks and disappear.
We will use Strobe to make the plasma bolt flash when it hits something.
Create a new plasmaBolt.easel
file, and fill it with the following code:
pub fn projectile.PlasmaBolt(ship, [owner]) {
use body=projectile
use speed=50, radius=0.25
use color=#dd00ff
use luminous=1, layer=-5
Body(
pos=ship.Pos,
velocity = speed * Direction(ship.Heading),
bullet=true,
)
PolygonCollider(shape=Circle, category=Category:Projectile)
on Paint {
Streak(dissipate=0.1s, bloom=2, bloomAlpha=1, glare=1, shadow=0.5)
}
once BeforeCollide that {
Strobe(shine=1, dissipate=0.5s)
repeat 5 { Spark(shine=0.5, splatter=1, dissipate=0.5s) }
Expire
}
once Tick(1.5s) { Expire }
}
Next we need to make the ship fire plasma bolts when you press the spacebar.
We are going to limit the rate of fire by giving the ship a short cooldown of 0.1
seconds between shots.
Select your ship.easel
file. Within your Ship
function, insert the highlighted code below.
Add it just beneath the other on ButtonDown
block to keep things organized.
pub fn ship.Ship([owner]) {
// ...
on ButtonDown(Click) {
// ...
}
on ButtonUp(Click) {
// ...
}
on ButtonDown(KeySpacebar) {
Spawn projectile {
PlasmaBolt(ship=)
}
await Tick(0.1s)
}
// ...
}
Be sure to write 0.1s
and not 0.1
.
If you omit the s
, Easel will interpret your duration as ticks, not seconds.
There are 60
ticks per second in Easel.
KeySpacebar
is just one of the many keycodes available in Easel.
A comprehensive list of keycodes can be found in the Reference under the Inputs section.
Click Preview and try pressing the spacebar a few times. You will immediately notice something is wrong. There are sparks coming out of our ship when there should be plasma bolts.
Parenting the plasma bolts
A common problem in games is projectiles expiring immediately because they spawn touching the object they are spawned from. This causes them to immediately collide and therefore expire straight away. This is so common it deserves its own section.
Easel has a built-in solution to this.
Setting the parent
parameter of PolygonCollider
tells Easel to ignore collisions with that parent,
but only until the child has cleared (is no longer touching) the parent.
Select your plasmaBolt.easel
file, find the PolygonCollider
function call
and add a new parent=ship
parameter at the end.
Parameters are comma separated, so you will need to add a comma before the new parameter.
Your PlasmaBolt
function should now look like the following:
pub fn projectile.PlasmaBolt(ship, [owner]) {
// ...
PolygonCollider(shape=Circle, category=Category:Projectile, parent=ship)
// ...
}
Click Preview to test your game. Press spacebar a few times, and you should see some plasma bolts shoot out of the front of the ship. Try shooting some asteroids. You'll notice that the plasma bolts disappear when they hit an asteroid, but we are yet to make the asteroids react to plasma bolts.
Subdividing and destroying asteroids
When a plasma bolt hits an asteroid, we want the asteroid to subdivide into smaller asteroids. Smaller asteroids should become faster and therefore more dangerous. This makes the game more interesting.
First, we are going to parameterize your Asteroid
function
so that it we can ask it to create asteroids of different sizes.
This will involve moving some of the variables into the parameter list at the top.
Select your asteroid.easel
file and replace the top part of your Asteroid
function with the highlighted code.
You will need to replace everything from the first line (called the function signature) to its Body
function call.
pub fn asteroid.Asteroid(
radius=4, speed=8, dissipate=0.75s, color=#ff0000,
pos = (BoundaryRadius + radius + 5) * RandomVector,
generation=0, parent?) {
use body=asteroid
Body(
pos=,
velocity = speed * RandomVector,
)
PolygonCollider(shape=Circle, category=Category:Asteroid)
RecoverSpeed
WrapOutsideBoundary
// ...
}
We have just defined some parameters to our Asteroid
function
with default expressions. See Default Expressions to learn more.
This is a good time to click Preview and check that everything still works the same. Experienced programmers often test their code in small increments to avoid getting lost in a sea of errors.
Now we will spawn smaller asteroids each time the asteroid collides with something. We will only do this for a maximum number of generations, otherwise the screen would eventually become filled with tiny asteroids.
In asteroid.easel
, insert the highlighted code below at the bottom of your Asteroid
function:
pub fn asteroid.Asteroid(
// ...
) {
// ...
once BeforeCollide {
const MaxGenerations = 3
Strobe(shine=0.5, dissipate=0.25s)
let generation = generation + 1
let parent = asteroid
if generation <= MaxGenerations {
repeat 2 {
Spawn asteroid {
Asteroid(
pos=Pos, parent=, generation=,
radius = 0.6 * radius,
speed = 1.3 * speed,
dissipate = 0.7 * dissipate,
color = Mix(0.25, color, #ffff00),
)
}
}
}
Expire
}
}
Click Preview and try shooting some asteroids. They should break into smaller asteroids when you hit them. They will do this a maximum of 3 times before disappearing.
Scoring
Let's make the game more interesting by adding a score. This gives the player a goal to attain and compete for.
First, we need somewhere to store the score.
Each player will have their own score, so let's add a property called Score
to the player.
Select your main.easel
file, and insert the highlighted line of code above your Main
function.
To keep things organized, make sure to insert a blank line afterwards like you can see in the example below.
pub prop owner.Score = 0
pub game fn World.Main() {
// ...
}
A prop (or property) stores a value on an entity.
What makes props special is that you can write code to react to when they change,
using various keywords like on
or with
for example.
You will find yourself using props very often in your games.
Each time a plasma bolt hits an asteroid, we want to increase the player's score.
Select your plasmaBolt.easel
file. Add the highlighted lines to the PlasmaBolt
function,
inside your once BeforeCollide
block:
pub fn projectile.PlasmaBolt(ship, [owner]) {
// ...
once BeforeCollide that {
Strobe(shine=1, dissipate=0.5s)
repeat 5 { Spark(shine=0.5, splatter=1, dissipate=0.5s) }
if that.Category.Overlaps(Category:Asteroid) {
Score += 10
}
Expire
}
// ...
}
Now let's display the player's score at the top of the screen.
Select your main.easel
file.
Insert the highlighted code below inside the SpawnEachPlayer
block in your Main
function:
pub game fn World.Main() {
// ...
SpawnEachPlayer owner {
Subspawn ship {
Ship
once BeforeDespawn {
Eliminate
}
}
TopContent {
with Score {
H1 { %(Score) }
}
}
}
// ...
}
In most programming languages, it would require a lot more code to dynamically update the score on the screen.
In Easel, we can simply use a with
block
and Easel magically takes care of the rest.
See UI for more details.
Click Preview and try shooting some asteroids. You should see your score increase each time you hit an asteroid.