Skip to main content

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

// ...
}
warning

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.

tip

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

// ...
}
info

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

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

// ...
}
info

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.