Firing Plasma Bolts
We just added asteroids to the game. Now let's add some weapons to the ship so we can fight back! But when the asteroids get hit, let's make the asteroids break up into smaller, faster asteroids for even more mayhem!
Spawning plasma bolts
Adding a category
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
Defining the PlasmaBolt
function
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=#ffffff, ownerColor=true
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 }
}
Firing plasma bolts on spacebar down
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
/on ButtonUp
blocks 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)
}
// ...
}
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
Plasma bolts should destroy asteroids. But let's not make it so easy! When a plasma bolt hits an asteroid, we want the asteroid to subdivide into smaller asteroids which are even faster and more dangerous!
Parameterizing the Asteroid
function
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. It's a good idea to test your code in small increments to avoid getting lost in a sea of errors.
Subdividing asteroids on collision
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.
Adding a Score
property
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.
Increasing the score on hit
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
}
// ...
}
Displaying the score
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) }
}
}
once AfterEliminated {
// ...
}
}
// ...
}
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.