Build a GameCreating Pacific Wings

8  Pickups and Weapon Levels

Goal

Shot-down enemies sometimes leave something behind, and supply drops arrive ahead of you. Wing Cannons spin as they drift down, and each one you catch gives your Fighter bigger guns. A Repair Crate gives you one more plane. Lose a plane, and the guns lose a level.

The game as it is at the end of this chapter. Click the game, then press Enter to start.

If your game and this chapter part ways, here is the chapter’s finished project. Open it and compare, or start Chapter 9 from it.

Pacific Wings, Chapter 8 finishedMyth project · 1.7 MB

To open it, start Myth Engine, choose File › Open Project... and pick the downloaded file.

8.1 The Idea

Fly past the V of five Enemy Fighters from Chapter 3. Behind them, something small and gold is coming down the screen, turning slowly as it drifts. It’s a wings badge, with a blue gem at its middle.

Fly through it. A bright chime, a shine of pale blue sparks, and the badge is gone. Hold Space. The tracer from your Fighter’s nose has stopped. Two come from the wingtips instead.

That badge is a pickup, something the Fighter collects by flying through it. This one is the Wing Cannons. The other is the Repair Crate, a green crate with a cream cross. It gives you one more plane.

Since Chapter 2, your guns have been the same: one tracer, every 0.14 seconds. Now they have a weapon level. Level 0 is the center gun alone, as it’s always been. Level 1 is the two wing guns, and level 2 is all three. Each Wing Cannons raises the level by one, and each plane you lose takes one away.

Think of the levels as a short menu, three lines long, and the weapon level as a finger pointing at one line. Fighter Guns never asks how the finger got there. It reads the line, and fires what it says.

Pickups come two ways. A shot-down enemy may leave one where it fell. And now and then, Game Rules sends a supply drop: a pickup that appears up ahead of you, on a timer.

8.2 The Plan

Here’s how the pieces work together, one chain to a line.

  • The Fighter flies through a pickup -> Collected by Fighter sends its message, wingCannonsCollected or repairCrateCollected. It plays its chime and sparkle, and the pickup is gone.
  • wingCannonsCollected arrives -> Weapon Level raises $weaponLevel, and Score adds 250.
  • repairCrateCollected arrives -> Planes adds a plane, and Score adds 100.
  • Fire is held -> Fighter Guns reads the row of Weapon Levels that $weaponLevel points at, and fires the guns it names.
  • The Fighter is lost -> Weapon Level lowers $weaponLevel.
  • An enemy is shot down -> Shot Down sends shotDown to it, and Drops Pickups rolls for each pickup.
  • The supplyDrop timer rings -> Supply Drops calls Drop Supply once for each pickup.
  • The last milestone is gone -> Stages sends commandBomberApproaching, and Supply Drops stops.

8.3 Set Up

Two pictures and two sounds join the game. They’re in your project already.

Set upWhat this chapter adds
ART (sprites, from the starter kit)
Wing Cannons Art
Repair Crate Art
SOUNDS (from the starter kit)
Power Up
Extra Plane
GLOBAL VARIABLES
$weaponLevelNumberstarts at 0
SCENE Route: PLACE THESE
ObjectXY
Wing Cannons1028.438
Repair Crate15.62575.938

GLOBAL VARIABLES has one new row, $weaponLevel, made the way you made $stage, from a Globals card. It’s a Number that starts at 0, so all you type is its name.

PLACE THESE holds the two pickups. Place them later, once you’ve made them, when each part tells you to.

8.4 Wing Cannons

The first pickup comes together from three small pieces: a shine, a spin, and a way to be caught.

8.4.1 Pickup Shine

When the Fighter catches a Wing Cannons, it bursts into a puff of sparks. White, then pale blue, then a deeper blue, all glowing, and all gone in under a second.

Particle EffectPickup Shine
Emitter
Emission > Burst Count36
Emitter Shape > Emit Radius (m)0.25
Motion
Lifecycle > Each Particle Lives (ms)min 260, max 720
Motion > Speed (m/s)min 1.406, max 5.938
Appearance
Appearance > Color#8EEFFF
(Color first: choosing Multiple Colors hides it)
Appearance > Color SourceMultiple Colors
Appearance > Multiple Colors#FFFFFF, #8EEFFF, #3FB6FF
Appearance > Glow1.6
Appearance Over Life > Scalestart 0.8, end 0
Appearance Over Life > Size Variation (0–1)0.5
Appearance Over Life > Starting RotationRandom Spin On

Make it as you made Gun Muzzle in Chapter 2. Go page by page, from the card, and follow the note in its Appearance group. Once Multiple Colors is on, the single Color is hidden and shows nothing, but the game keeps it, so the card does too. Set it first, while you can still reach it.

8.4.2 Spin

The badge turns all the time, from the moment it’s made. That’s one Action, Set Spin, at Create. Myth Engine keeps it turning from there, so nothing has to turn it again each frame.

Logic ComponentSpinAttach to: Wing Cannons

Turns this Object steadily at spinDegreesPerSecond from the moment it exists.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
spinDegreesPerSecondNumberConstant-96
When Created
WhenCreate
1Set Spin
Spin
@self.spinDegreesPerSecond

Make it as an Empty Logic Component, with the Shared Variable spinDegreesPerSecond. A minus number turns clockwise. At -96, that’s a little over a quarter turn every second. Add Create, then Set Spin. Its Spin box takes @self.spinDegreesPerSecond.

8.4.3 Collected by Fighter

From the badge’s point of view, its whole life is waiting for one touch. When the Fighter flies into it, it does four things, in order. It announces that it’s been caught. It plays its sound, bursts into its sparkle, and it’s gone.

It doesn’t raise the weapon level itself. It only reports. Game Rules decides what a catch is worth.

So one piece of Logic serves both pickups, because its message, its sound and its sparkle are all Shared Variables. The Wing Cannons keeps them one way, and the Repair Crate another.

Logic ComponentCollected by FighterAttach to: Wing Cannons, Repair Crate

When the Fighter touches this pickup, sends collectedEvent, plays its sound and sparkle, and removes the pickup.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
collectedEventTextConstantwingCannonsCollected
soundSoundConstantPower Up
sparkleParticlesConstantPickup Shine
sparkleParticlesNumberConstant42
When Starting to Hit Fighter
WhenCollision EnterObject: Fighter
1Trigger Custom Event
Custom Event
@self.collectedEvent
2Control Sound
Sound
@self.sound
3Burst Particles
Particles
@self.sparkle
Count
@self.sparkleParticles
Z Index Offset
2
4Destroy Instance

Make it as an Empty Logic Component. Its first Shared Variable, collectedEvent, is a Text, and its Starting Value is the message itself. Type wingCannonsCollected there, without quote marks. That box takes text as it is.

sound is a new Type, Sound, lower in the Type list among the kinds that run from A to Z. Its Starting Value is one of your sounds: Power Up. Then sparkle, a Particles, starting at Pickup Shine, and the Number last.

Add Collision Enter, and pick Fighter in its Collision Target list, as on Crashes Into Fighter. Trigger Custom Event comes first. Its Custom Event is @self.collectedEvent, the Shared Variable, not a message by that name. So each pickup sends its own message. Control Sound takes @self.sound, and Burst Particles takes @self.sparkle and @self.sparkleParticles, with the Z Index Offset of 2 from the card. Destroy Instance comes last.

8.4.4 Wing Cannons

ObjectWing Cannons
Transform
Bounding Box > SizeX 2.16, Y 2.16
Physics > Allow RotationOn
Sprite
Scene Depth Draw Order36
SpriteWing Cannons Art
Transform > SizeX 2.16, Y 2.16
Collider
CollisionSensor
Shape and Transform > ShapeEllipse
Shape and Transform > SizeX 1.434, Y 0.996
Logic
Fly South When Seen
Spin
Remove When Out of View
Collected by Fighter
Shared Variables
speed2.4

The Wing Cannons is a 2D Object, made down its card. Its Collider is a Sensor, like your tracers, so the Fighter flies right through. Up on its Transform, under Physics, Allow Rotation goes On, so Spin can turn it.

In Logic, attach Fly South When Seen, Spin, Remove When Out of View and Collected by Fighter, in the card’s order. Fly South When Seen asks nothing of its Object, so Compatible Logic lists it here too. Back in Components, the one Shared Variable to change is speed, 2.4: the badge drifts south once it comes into view. Collected by Fighter’s four keep the Logic’s own values, the Wing Cannons’ message, sound and sparkle.

Don’t place it yet. Catching one would change nothing until the guns can read a weapon level. That’s the next part.

8.5 Bigger Guns

Now the weapon level, and the guns that read it.

8.5.1 Weapon Levels

Data StructureWeapon Levels Entry

One row of the "Weapon Levels" Data Table.

centerGunBooleandefault false
wingCannonsBooleandefault false
fireRateSecondsNumberdefault 0.14
Data TableWeapon LevelsData Structure: Weapon Levels Entry
Row 0Center Gun
centerGunYes
wingCannonsNo
fireRateSeconds0.14
Row 1Wing Cannons
centerGunNo
wingCannonsYes
fireRateSeconds0.15
Row 2Wing Cannons and Center Gun
centerGunYes
wingCannonsYes
fireRateSeconds0.15

This is the short menu from the start of the chapter. Each row is one level, and its three cells say which guns fire, and how often. Row 0 is the center gun, every 0.14 seconds, the way your Fighter has always fired. Rows 1 and 2 are a hair slower, every 0.15 seconds, with more guns.

Make it as Reinforcement Orders was made in Chapter 7. The table makes Weapon Levels Entry, and its fields are the columns. centerGun and wingCannons are Booleans. Pick Boolean in each one’s Type list. Leave its Value on No. The third, fireRateSeconds, is a Number with a Value of 0.14. In the table, a Boolean cell is a switch, and on is Yes. Add Row three times. Then double-click each row’s name in the Entry column, and type its own.

8.5.2 Weapon Level

Logic ComponentWeapon LevelAttach to: Game Rules

Raises $weaponLevel by one for each Wing Cannons collected (up to the last row of Weapon Levels) and lowers it by one for each plane lost.

Attach toAny Object
On Wing Cannons Collected
WhenwingCannonsCollectedCustom Event
1If$weaponLevel < (dataCount('Weapon Levels') - 1)
Then
2Change Variable
Variable
$weaponLevel
Operator
+=
Value
1
On Fighter Lost
WhenfighterLostCustom Event
1If$weaponLevel > 0
Then
2Change Variable
Variable
$weaponLevel
Operator
-=
Value
1

Weapon Level lives on Game Rules, and it keeps $weaponLevel. It listens for two messages. Each one moves the finger one line.

A Wing Cannons moves it down to the next row, but never past the last. How does it know which row is last? The answer is dataCount(), which counts a table’s rows. Here, that’s three. The rows count from 0, so the last row is 2. That’s one less than the count. That’s why the If asks for less than dataCount('Weapon Levels') - 1. Add a fourth row one day, and a fourth level comes with it, with no change here.

A lost plane moves the finger back up one row. It never goes above row 0.

It’s an Empty Logic Component with no Shared Variables, and its first Event is new to the list. In the Add Event window, type Custom into the filter, pick Add Custom Event…, and type wingCannonsCollected. fighterLost is in the list already, since Lost on Hit sends it. Each Event is an If with one step inside. In the fighterLost Event, that step’s Operator is -= · Subtract, so the level goes down by one when a plane is lost.

Attach Weapon Level to Game Rules now, with nothing to set.

8.5.3 Fighter Guns

Your Fighter needs two more muzzles.

ObjectFighterAddDelete
Transform
Bounding Box > SizeX 2.88, Y 2.88
Sprite
Scene Depth Draw Order40
SpriteFighter Art
Transform > SizeX 2.88, Y 2.88
Collider
Shape and Transform > ShapeCapsule
Shape and Transform > PositionX 0, Y -0.09
Shape and Transform > SizeX 0.698, Y 1.733
Coordinate
NameCenter Muzzle
Local PositionX 0, Y 1.193
NameLeft Muzzle
Local PositionX -0.563, Y 1.058
NameRight Muzzle
Local PositionX 0.563, Y 1.058
Data Structure
From LogicReturn Flight
Data StructureReturn Flight Data
Logic
Vertical Shooter Controls
Fighter Guns
Lost on Hit
Return Flight
Shared Variables
moveSpeed7.8
scrollSpeed > KindExpression
scrollSpeed > Expression$scrollSpeed
tracerFighter Tracer

Each is a Coordinate, as Center Muzzle is, so click Coordinate in the toolbox’s Core group twice. Coordinate 1 and Coordinate 2 appear under Center Muzzle. Name them Left Muzzle and Right Muzzle, and type their Local Positions. They sit a little behind the nose, a bit over half a meter to each side.

Now the guns. This is the change to slow down for.

Logic ComponentFighter GunsAttach to: FighterAddDelete

While Fire is held, fires Fighter Tracers north from the muzzles the weapon level's row in Weapon Levels names, as often as that row says.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
tracerObjectConstantNone
fireRateSecondsNumberConstant0.14
tracerSpeedNumberConstant17.5
centerFlashParticlesNumberConstant5
wingFlashParticlesNumberConstant4
muzzleFlashParticlesNumberConstant5
While Fire Is Held
WhenCommand HeldCommand: Fire
ModeFire the Guns (Starting Mode)
1Check Cooldown Ready
Action Name
fireRateReady
Cooldown Key
"fireRate"
2IffireRateReady.value == true
Then
3Fire From MuzzleFunction
shooter
self
tracer
@self.tracer
muzzle
"Center Muzzle"
tracerSpeed
@self.tracerSpeed
flashParticles
@self.muzzleFlashParticles
3Ifdata('Weapon Levels', $weaponLevel, 'centerGun') == true
Then
4Fire From MuzzleFunction
shooter
self
tracer
@self.tracer
muzzle
"Center Muzzle"
tracerSpeed
@self.tracerSpeed
flashParticles
@self.centerFlashParticles
5Ifdata('Weapon Levels', $weaponLevel, 'wingCannons') == true
Then
6Fire From MuzzleFunction
shooter
self
tracer
@self.tracer
muzzle
"Left Muzzle"
tracerSpeed
@self.tracerSpeed
flashParticles
@self.wingFlashParticles
7Fire From MuzzleFunction
shooter
self
tracer
@self.tracer
muzzle
"Right Muzzle"
tracerSpeed
@self.tracerSpeed
flashParticles
@self.wingFlashParticles
8Control Sound
Sound
Machine Guns
9Start Cooldown
Cooldown Key
"fireRate"
Seconds
data('Weapon Levels', $weaponLevel, 'fireRateSeconds')
Seconds
@self.fireRateSeconds
While Fire Is Held
WhenCommand HeldCommand: Fire
ModeHolding Fire
(no Actions)

Until now, Fighter Guns has fired one call to Fire From Muzzle, from the Center Muzzle, whenever its cooldown was ready. Now it asks the row two questions first: does it have the center gun, and does it have the wing cannons? Each answer is a Boolean cell, a Yes or a No. Yes to the first, and the center fires. Yes to the second, and both wings fire, left and right together.

The cooldown reads the row too. Its Seconds come from the row’s fireRateSeconds, not from a Shared Variable.

The card strikes the old call and marks three new ones, inside two new Ifs. Here’s the path.

Start with the Shared Variables. Add centerFlashParticles and wingFlashParticles, two Numbers, at 5 and 4. Leave the two struck ones for now, because boxes on the canvas still name them.

On Fire the Guns’ line, right-click Fire From Muzzle and choose Delete. Then right-click Control Sound, and choose Disconnect from Flow. It slides off, loose. Do the same for Start Cooldown. The If’s True line is empty now, and the two loose blocks still belong to it.

Right-click the If you started with, the one that asks about fireRateReady, and choose Add Inside Then. Pick If, and join it with Connect to Sequence. Type its condition from the card, the data() and the true. data() reads one cell, as in Chapter 7. This time the row is a number, $weaponLevel, and rows count from 0. A Yes cell reads as true.

Now right-click the new If, choose Add Inside Then, and add Fire From Muzzle from the window’s filter. Join it. Its boxes start on the Function’s Defaults, so type the card’s values over them, with @self.centerFlashParticles for its flash.

The second If comes the same way, from the fireRateReady If’s menu, and it joins after the first new one. Its two calls go inside it, each from its own Add Inside Then. Type "Left Muzzle" and "Right Muzzle" into their muzzle boxes. Both flash boxes take @self.wingFlashParticles.

Last, right-click Control Sound, and choose Connect to Sequence. Where does it go? Control Sound slid off the fireRateReady If’s True line, so it still belongs to that line. Connect to Sequence puts it back at the end of it: inside the fireRateReady If, under the second new If. Then do the same for Start Cooldown. Look at the canvas now. Both should sit on the True line, as steps 8 and 9 do on the card. Type the card’s data() into Start Cooldown’s Seconds.

Now nothing names fireRateSeconds or muzzleFlashParticles. Delete both on the Shared Variables card, with their trash buttons.

8.5.4 Score

Each pickup is worth points too.

Logic ComponentScoreAttach to: Game RulesAddDelete

Adds the points of every enemy shot down, and of every pickup collected, to $score.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
wingCannonsPointsNumberConstant250
repairCratePointsNumberConstant100
On Enemy Shot Down
WhenenemyShotDownCustom Event
1Change Variable
Variable
$score
Operator
+=
Value
other.enemyStats.points
On Game State Changed
WhenGame State Changed
1Ifevent.current == "mission-failed"
Then
2Set Logic State
Logic Component
Score
State
Disable
On Wing Cannons Collected
WhenwingCannonsCollectedCustom Event
1Change Variable
Variable
$score
Operator
+=
Value
@self.wingCannonsPoints
On Repair Crate Collected
WhenrepairCrateCollectedCustom Event
1Change Variable
Variable
$score
Operator
+=
Value
@self.repairCratePoints

Two Shared Variables, both Numbers, and two new Events, one for each pickup’s message. A message is in the Add Event list once some Logic sends it, or listens for it. Weapon Level listens for wingCannonsCollected, so it’s listed now. Nothing sends or hears repairCrateCollected yet, so add that one with Add Custom Event…, as before. Each Event is one Change Variable, += · Add, as for an enemy.

8.5.5 The First Wing Cannons

Open Route, and place the Wing Cannons from PLACE THESE.

Run the game. Past the V of five, the gold badge comes down the screen, turning. Fly into it. A chime, a shine, and 250 points. Hold Space, and two tracers leave the wingtips.

8.6 The Repair Crate

The second pickup is the first one again, with its own numbers. You’ve made every part of it once already.

Particle EffectRepair Shine
Emitter
Emission > Burst Count36
Emitter Shape > Emit Radius (m)0.25
Motion
Lifecycle > Each Particle Lives (ms)min 260, max 720
Motion > Speed (m/s)min 1.406, max 5.938
Appearance
Appearance > Color#8DFF8A
(Color first: choosing Multiple Colors hides it)
Appearance > Color SourceMultiple Colors
Appearance > Multiple Colors#FFFFFF, #8DFF8A, #FFE066
Appearance > Glow1.6
Appearance Over Life > Scalestart 0.8, end 0
Appearance Over Life > Size Variation (0–1)0.5
Appearance Over Life > Starting RotationRandom Spin On

Repair Shine is Pickup Shine in green and gold. Make it from its card.

ObjectRepair Crate
Transform
Bounding Box > SizeX 1.84, Y 1.84
Sprite
Scene Depth Draw Order36
SpriteRepair Crate Art
Transform > SizeX 1.84, Y 1.84
Collider
CollisionSensor
Shape and Transform > SizeX 1.265, Y 1.107
Logic
Fly South When Seen
Remove When Out of View
Collected by Fighter
Shared Variables
collectedEventrepairCrateCollected
soundExtra Plane
sparkleRepair Shine
sparkleParticles36
speed2

The Repair Crate doesn’t spin, so it has no Allow Rotation and no Spin, and it drifts a little slower. Its Collider is a Sensor too. Under Variables, its Collected by Fighter values are its own: type repairCrateCollected over the message, set sound to Extra Plane and sparkle to Repair Shine, and make sparkleParticles 36.

Logic ComponentPlanesAttach to: Game RulesAddDelete

Counts the planes left; after a loss, launches a reserve plane if one is left; a Repair Crate adds one (up to maximumPlanes).

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
fighterObjectConstantNone
startingPlanesNumberConstant3
reserveDelaySecondsNumberConstant2
reserveStartBelowMiddleNumberConstant17.5
maximumPlanesNumberConstant6
When the Game Starts
WhenGame Start
1Change Variable
Variable
$planes
Value
@self.startingPlanes
On Fighter Lost
WhenfighterLostCustom Event
1Check Game State
Action Name
isPlaying
Game State
Playing
2IfisPlaying.value == true
Then
3Change Variable
Variable
$planes
Operator
-=
Value
1
4If$planes > 0
Then
5Start Recurring Timer
Timer Name
"launchReserve"
Repeat Every (s)
@self.reserveDelaySeconds
Ring Once
Yes
6Otherwise
7Set Game State
Game State
Mission Failed
On the Launch Reserve Timer
WhenRecurring TimerTimer name: launchReserve
1Create Instance
Object to Create
@self.fighter
X
$viewMiddleX
Y
$viewMiddleY - @self.reserveStartBelowMiddle
2Trigger Custom Event on Object
Applies to
created
Custom Event
startReturnFlight
3Control Sound
Sound
Reserve Plane Arrives
On Repair Crate Collected
WhenrepairCrateCollectedCustom Event
1If$planes < @self.maximumPlanes
Then
2Change Variable
Variable
$planes
Operator
+=
Value
1

Planes gains one Shared Variable, maximumPlanes, and one Event, repairCrateCollected. Score listens for it now, so it’s in the Add Event list. Its If keeps a crate from adding a plane past the most you can hold.

Nothing else has to change. The small cream planes under the score have followed $planes since Chapter 4.

Place the Repair Crate from PLACE THESE, where it waits in Wave 3, on the right. Run the game, and fly through it there. A chime, a green shine, and one more cream plane under the score.

8.7 Pickups From the Sky

Two placed pickups are a start. Now the sky starts leaving gifts behind.

8.7.1 Two More Chances

Data StructureEnemy StatsAddDelete

What every enemy carries. Each enemy kind takes its values from its own row of Enemy Types.

speedNumberdefault 0
healthNumberdefault 1
pointsNumberdefault 0
firstShotSecondsNumberdefault 1
shotSecondsMinNumberdefault 1
shotSecondsMaxNumberdefault 2
weaveSpeedNumberdefault 0
weaveSecondsNumberdefault 1
weaveAngleNumberdefault 0
tracerSpeedNumberdefault 7.7
aimConeDegreesNumberdefault 0
wingCannonsDropChanceNumberdefault 0.08
repairCrateDropChanceNumberdefault 0.035
Data TableEnemy TypesData Structure: Enemy StatsAddDelete
Row 0Enemy Fighter
speed3.7
health1
points100
firstShotSeconds1.15
shotSecondsMin1.4
shotSecondsMax2.5
weaveSpeed0
weaveSeconds0
weaveAngle0
tracerSpeed7.7
aimConeDegrees0
wingCannonsDropChance0.08
repairCrateDropChance0.035
Row 1Enemy Ace
speed4.25
health2
points250
firstShotSeconds1
shotSecondsMin0.9
shotSecondsMax1.7
weaveSpeed3.5
weaveSeconds0.68
weaveAngle6
tracerSpeed8.9
aimConeDegrees65
wingCannonsDropChance0.08
repairCrateDropChance0.035
Row 2Heavy Bomber
speed2.1
health5
points600
firstShotSeconds2.15
shotSecondsMin0.78
shotSecondsMax1.25
weaveSpeed1.5
weaveSeconds1.45
weaveAngle6
tracerSpeed8.9
aimConeDegrees65
wingCannonsDropChance0.22
repairCrateDropChance0.12

Every kind of enemy gets two chances, one for each pickup. A chance is a number from 0 to 1: at 0 it never drops, and at 1 it always does. The Enemy Fighter and the Ace drop a Wing Cannons at 0.08. That’s about one time in twelve. The Heavy Bomber is the hardest to bring down, and the most generous: 0.22 and 0.12.

Add the two fields to Enemy Stats. In Enemy Types, every row’s new cells start at 0, so type all six from the card.

8.7.2 Shot Down

Shot Down learns one new thing to say.

Logic ComponentShot DownAttach to: Enemy Fighter, Enemy Ace, Heavy BomberAddDelete

When this enemy has no health left, reports enemyShotDown to Game Rules and shotDown to itself, then explodes and is removed.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
explosionParticlesNumberConstant42
On No More Health
WhennoMoreHealthCustom Event
1Trigger Custom Event
Custom Event
enemyShotDown
2Trigger Custom Event on Object
Custom Event
shotDown
3Control Sound
Sound
Plane Break
4Burst Particles
Particles
Plane Explosion
Count
@self.explosionParticles
Z Index Offset
2
5Destroy Instance

It still tells Game Rules, with enemyShotDown. Now it also tells its own enemy, with shotDown, while the enemy is still in the sky. That’s the moment a pickup can fall from where it was.

The new step goes between two old ones, so use the round handles, as on the Making page. Add Trigger Custom Event on Object, and type shotDown into its Custom Event. Click the round handle at the bottom of Trigger Custom Event, then the new block’s top handle. It slides in, before Control Sound.

8.7.3 Drops Pickups

When shotDown arrives, the enemy rolls a die for each pickup. It’s not really a die. Generate Random Number picks a number between 0 and 1. If the roll is under the enemy’s chance, the pickup appears right where the enemy is.

There are two rolls, one for each pickup, so now and then a Heavy Bomber leaves both.

Logic ComponentDrops PickupsAttach to: Enemy Fighter, Enemy Ace, Heavy Bomber

When this enemy is shot down, it may drop a Wing Cannons or a Repair Crate where it is, by its drop chances.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
wingCannonsObjectConstantNone
repairCrateObjectConstantNone
wingCannonsChanceNumberConstant0
repairCrateChanceNumberConstant0
On Shot Down
WhenshotDownCustom Event
1Generate Random Number
Action Name
wingCannonsRoll
Minimum
0
Maximum
1
2IfwingCannonsRoll.value < @self.wingCannonsChance
Then
3Create Instance
Object to Create
@self.wingCannons
Position Relative To
Self
4Generate Random Number
Action Name
repairCrateRoll
Minimum
0
Maximum
1
5IfrepairCrateRoll.value < @self.repairCrateChance
Then
6Create Instance
Object to Create
@self.repairCrate
Position Relative To
Self
Created As
created2

Make it as an Empty Logic Component. Its two Objects are Shared Variables starting at None, and the two chances are Numbers at 0. Every enemy fills them in later, each from its own row of Enemy Types.

The shotDown message is in the Add Event list already. The first roll goes in first. Its If follows, with the Create Instance inside it. That Create Instance starts on World, so pick Self in its Position Relative To. The second roll goes after the whole If. Its Create Instance names its result created2 by itself, since the first is already created. Neither one needs its Applies to changed, since each Create Instance starts on Self.

ObjectEnemy FighterAddDelete
Transform
Bounding Box > SizeX 2.48, Y 2.48
Sprite
Scene Depth Draw Order30
SpriteEnemy Fighter Art
Transform > SizeX 2.48, Y 2.48
Transform > Rotation (deg)Z -180
Collider
Shape and Transform > ShapeEllipse
Shape and Transform > SizeX 1.453, Y 1.046
Tags and Interactions > InteractionsNever Collide With
Tags and Interactions > Never Collide WithTagged Enemy Aircraft
Data Structure
Data StructureEnemy Stats
Input SourceData Table
Data TableEnemy Types
RowEnemy Fighter
Data Structure
From LogicFire When Seen
Data StructureFire When Seen Data
Coordinate
NameGun Muzzle
Local PositionX 0, Y -0.94
Tags
Object TagsEnemy Aircraft, Enemy Threat
Logic
Fly South When Seen
Takes Hits
Shot Down
Fire When Seen
Drops Pickups
Crashes Into Fighter
Remove When Out of View
Shared Variables
tracerEnemy Tracer
tracerSpeed > KindExpression
tracerSpeed > ExpressionobjectComponentProperty("self","enemyStats","tracerSpeed")
firstShotSeconds > KindExpression
firstShotSeconds > ExpressionobjectComponentProperty("self","enemyStats","firstShotSeconds")
shotSecondsMin > KindExpression
shotSecondsMin > ExpressionobjectComponentProperty("self","enemyStats","shotSecondsMin")
shotSecondsMax > KindExpression
shotSecondsMax > ExpressionobjectComponentProperty("self","enemyStats","shotSecondsMax")
fighterFighter
aimConeDegrees > KindExpression
aimConeDegrees > ExpressionobjectComponentProperty("self","enemyStats","aimConeDegrees")
wingCannonsWing Cannons
repairCrateRepair Crate
wingCannonsChance > KindExpression
wingCannonsChance > ExpressionobjectComponentProperty("self","enemyStats","wingCannonsDropChance")
repairCrateChance > KindExpression
repairCrateChance > ExpressionobjectComponentProperty("self","enemyStats","repairCrateDropChance")
speed > KindExpression
speed > ExpressionobjectComponentProperty("self","enemyStats","speed")
ObjectEnemy AceAddDelete
Transform
Bounding Box > SizeX 2.56, Y 2.56
Sprite
Scene Depth Draw Order30
SpriteEnemy Ace Art
Transform > SizeX 2.56, Y 2.56
Transform > Rotation (deg)Z -180
Collider
Shape and Transform > ShapeEllipse
Shape and Transform > SizeX 1.6, Y 1.04
Tags and Interactions > InteractionsNever Collide With
Tags and Interactions > Never Collide WithTagged Enemy Aircraft
Coordinate
NameGun Muzzle
Local PositionX 0, Y -0.94
Data Structure
Data StructureEnemy Stats
Input SourceData Table
Data TableEnemy Types
RowEnemy Ace
Data Structure
From LogicWeave
Data StructureWeave Data
Data Structure
From LogicFire When Seen
Data StructureFire When Seen Data
Tags
Object TagsEnemy Aircraft, Enemy Threat
Logic
Weave
Fly South When Seen
Takes Hits
Shot Down
Fire When Seen
Drops Pickups
Crashes Into Fighter
Remove When Out of View
Shared Variables
tracerEnemy Tracer
tracerSpeed > KindExpression
tracerSpeed > ExpressionobjectComponentProperty("self","enemyStats","tracerSpeed")
firstShotSeconds > KindExpression
firstShotSeconds > ExpressionobjectComponentProperty("self","enemyStats","firstShotSeconds")
shotSecondsMin > KindExpression
shotSecondsMin > ExpressionobjectComponentProperty("self","enemyStats","shotSecondsMin")
shotSecondsMax > KindExpression
shotSecondsMax > ExpressionobjectComponentProperty("self","enemyStats","shotSecondsMax")
fighterFighter
aimConeDegrees > KindExpression
aimConeDegrees > ExpressionobjectComponentProperty("self","enemyStats","aimConeDegrees")
weaveSpeed > KindExpression
weaveSpeed > ExpressionobjectComponentProperty("self","enemyStats","weaveSpeed")
weaveSeconds > KindExpression
weaveSeconds > ExpressionobjectComponentProperty("self","enemyStats","weaveSeconds")
weaveAngle > KindExpression
weaveAngle > ExpressionobjectComponentProperty("self","enemyStats","weaveAngle")
wingCannonsWing Cannons
repairCrateRepair Crate
wingCannonsChance > KindExpression
wingCannonsChance > ExpressionobjectComponentProperty("self","enemyStats","wingCannonsDropChance")
repairCrateChance > KindExpression
repairCrateChance > ExpressionobjectComponentProperty("self","enemyStats","repairCrateDropChance")
speed > KindExpression
speed > ExpressionobjectComponentProperty("self","enemyStats","speed")
ObjectHeavy BomberAddDelete
Transform
Bounding Box > SizeX 3.36, Y 3.36
Sprite
Scene Depth Draw Order30
SpriteHeavy Bomber Art
Transform > SizeX 3.36, Y 3.36
Transform > Rotation (deg)Z -180
Collider
Shape and Transform > ShapeEllipse
Shape and Transform > SizeX 2.468, Y 1.733
Tags and Interactions > InteractionsNever Collide With
Tags and Interactions > Never Collide WithTagged Enemy Aircraft
Coordinate
NameGun Muzzle
Local PositionX 0, Y -0.932
Data Structure
Data StructureEnemy Stats
Input SourceData Table
Data TableEnemy Types
RowHeavy Bomber
Data Structure
From LogicWeave
Data StructureWeave Data
Data Structure
From LogicFire When Seen
Data StructureFire When Seen Data
Tags
Object TagsEnemy Aircraft, Enemy Threat
Logic
Weave
Fly South When Seen
Takes Hits
Shot Down
Fire When Seen
Drops Pickups
Crashes Into Fighter
Remove When Out of View
Shared Variables
tracerEnemy Tracer
tracerSpeed > KindExpression
tracerSpeed > ExpressionobjectComponentProperty("self","enemyStats","tracerSpeed")
firstShotSeconds > KindExpression
firstShotSeconds > ExpressionobjectComponentProperty("self","enemyStats","firstShotSeconds")
shotSecondsMin > KindExpression
shotSecondsMin > ExpressionobjectComponentProperty("self","enemyStats","shotSecondsMin")
shotSecondsMax > KindExpression
shotSecondsMax > ExpressionobjectComponentProperty("self","enemyStats","shotSecondsMax")
fighterFighter
aimConeDegrees > KindExpression
aimConeDegrees > ExpressionobjectComponentProperty("self","enemyStats","aimConeDegrees")
weaveSpeed > KindExpression
weaveSpeed > ExpressionobjectComponentProperty("self","enemyStats","weaveSpeed")
weaveSeconds > KindExpression
weaveSeconds > ExpressionobjectComponentProperty("self","enemyStats","weaveSeconds")
weaveAngle > KindExpression
weaveAngle > ExpressionobjectComponentProperty("self","enemyStats","weaveAngle")
wingCannonsWing Cannons
repairCrateRepair Crate
wingCannonsChance > KindExpression
wingCannonsChance > ExpressionobjectComponentProperty("self","enemyStats","wingCannonsDropChance")
repairCrateChance > KindExpression
repairCrateChance > ExpressionobjectComponentProperty("self","enemyStats","repairCrateDropChance")
speed > KindExpression
speed > ExpressionobjectComponentProperty("self","enemyStats","speed")

Attach Drops Pickups to each of the three planes. The card lists it after Fire When Seen, and yours joins the end of the list. That makes no difference here. Nothing else on a plane listens for shotDown. Under Variables, press the None buttons, and pick Wing Cannons and Repair Crate. The two chances are Expressions, reading the plane’s own row, as on the card.

8.7.4 Drop Supply

The supply drops are the last piece. Every so often, Game Rules may drop each pickup somewhere ahead of you.

Drop Supply is the Function that drops one. First, it checks that none of that pickup is already in the sky. One at a Time is enough. Then it rolls against the pickup’s chance.

If both say yes, it finds a height to drop from. That’s the Fighter’s height, or the middle of the view, if there’s no Fighter right now. The pickup appears aheadMeters north of that, at a random spot across the route.

FunctionDrop Supply

By chance, and only while none of that pickup is in the sky, the creator drops the pickup aheadMeters north of the Fighter (or of the middle of the view while no Fighter is flying), at a random x.

PARAMETERS
NameTypeStarts at
creatorObject
pickupObject
fighterObjectFighter
chanceNumber0.5
aheadMetersNumber8.4
xMinNumber3.1
xMaxNumber16.9
ACTIONS
1Declare Variable
Name
aheadOfY
Value
0
2Check Instance Count
Action Name
noneInSky
Object
pickup
Operator
<
3Generate Random Number
Action Name
dropRoll
Minimum
0
Maximum
1
4IfnoneInSky.value == true && dropRoll.value < chance
Then
5Check Instance Count
Action Name
fighterInSky
Object
fighter
Operator
>
Value
0
6IffighterInSky.value == true
Then
7Change Variable
Variable
aheadOfY
Value
fighter.transform.position.y
8Otherwise
9Change Variable
Variable
aheadOfY
Value
$viewMiddleY
10Generate Random Number
Action Name
dropX
Minimum
xMin
Maximum
xMax
11Create Instance
Applies to
creator
Object to Create
pickup
X
dropX.value
Y
aheadOfY + aheadMeters

Make it with Add > Function, as Fire From Muzzle was made, with its Parameters on the Start block. The first two, creator and pickup, are Objects, the one at the end of the Type list. The third, fighter, names its Object. Its window asks for an Object Kind, and you pick Fighter there. The four Numbers come last. The value it keeps for itself, aheadOfY, lasts one call. So it’s a Declare Variable, the first step.

Why creator? A Function belongs to no Object, so it has no Self of its own. The creator is the Object that calls it, Game Rules here, and the pickup is made in its name. So pick creator in Create Instance’s Applies to.

Check Instance Count starts on >= with a Value of 1. Pick < and leave the 1: fewer than one of this pickup in the sky. The If joins two tests with &&, both typed in its one box: both must hold.

Inside the first If, Add Inside Then adds the next Check Instance Count, and again the second If after it. The two Change Variables for aheadOfY are the second If’s True and its Else: Add Inside Then for the first, then Add Else and Add Inside Else for the other. The last two steps come from the first If’s menu again, Add Inside Then, so they join after the second If, inside the first.

8.7.5 Supply Drops

Logic ComponentSupply DropsAttach to: Game Rules

Every secondsMin to secondsMax seconds (the first after firstSeconds), may drop a Wing Cannons and a Repair Crate ahead of the Fighter, until the Command Bomber approaches.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
wingCannonsObjectConstantNone
repairCrateObjectConstantNone
fighterObjectConstantNone
firstSecondsNumberConstant9.5
secondsMinNumberConstant10
secondsMaxNumberConstant14
wingCannonsChanceNumberConstant0.7
repairCrateChanceNumberConstant0.32
wingCannonsAheadMetersNumberConstant8.4
repairCrateAheadMetersNumberConstant8.9
xMinNumberConstant3.1
xMaxNumberConstant16.9
When the Scene Starts
WhenScene Start
1Start Recurring Timer
Timer Name
"supplyDrop"
Repeat Every (s)
@self.firstSeconds
Ring Once
Yes
On the Supply Drop Timer
WhenRecurring TimerTimer name: supplyDrop
1Drop SupplyFunction
creator
self
pickup
@self.wingCannons
fighter
@self.fighter
chance
@self.wingCannonsChance
aheadMeters
@self.wingCannonsAheadMeters
xMin
@self.xMin
xMax
@self.xMax
2Drop SupplyFunction
creator
self
pickup
@self.repairCrate
fighter
@self.fighter
chance
@self.repairCrateChance
aheadMeters
@self.repairCrateAheadMeters
xMin
@self.xMin
xMax
@self.xMax
3Generate Random Number
Action Name
waitRoll
Minimum
@self.secondsMin
Maximum
@self.secondsMax
4Start Recurring Timer
Timer Name
"supplyDrop"
Repeat Every (s)
waitRoll.value
Ring Once
Yes
On Command Bomber Approaching
WhencommandBomberApproachingCustom Event
1Stop Recurring Timer
Timer Name
"supplyDrop"

Supply Drops runs the timer, and asks Drop Supply twice each time it rings, once for each pickup. Each wait is a new random number, so the gifts never come on a beat you could count. When the last milestone is gone, it stops, like the reinforcements.

Its Shared Variables are three Objects starting at None, then the eight Numbers, each at the card’s value.

Scene Start comes first, as on Stages, and starts the timer. The timer’s own Event is a Recurring Timer, named supplyDrop, and each Drop Supply call in it comes from the Functions group. Its creator is self, as a new call starts. Type the card’s @self. values into the rest. commandBomberApproaching is in the Add Event list, since Stages sends it.

ObjectGame RulesAddDelete
Collider(as it is added)
Logic
Score
Planes
Mission Failed Screen
Mission Start
Pause
Best Score
Play Again
Stages
Weapon Level
Supply Drops
Shared Variables
fighterFighter
milestoneMilestone
wingCannonsWing Cannons
repairCrateRepair Crate

Attach Supply Drops to Game Rules. Under Variables, press the None buttons for wingCannons and repairCrate, and pick Wing Cannons and Repair Crate. The third, fighter, is there already, set to Fighter. Planes and Supply Drops both read the one Shared Variable of that name.

Save, and run the game.

The badge comes down past the V of five, turning, and your wing guns open up. A few seconds later, a second gift appears up ahead. Shoot down a Heavy Bomber, and now and then a green crate tumbles out of the explosion.

Play it!

Watch for each of these, one at a time.

  • Past the V of five, the Wing Cannons drifts south, turning clockwise.
  • Fly through it. A chime, a shine of pale blue sparks, and 250 more points.
  • Hold Space after one Wing Cannons. Two tracers leave the wingtips, and none from the nose. After a second Wing Cannons, all three guns fire together.
  • A third Wing Cannons still gives its 250 points, but the guns stay as they are. Two is the top level.
  • Lose a plane while your wing guns fire, and the next plane flies in one level lower.
  • In Wave 3, on the right, the green Repair Crate drifts south. Fly through it, and one more small cream plane appears under the score, with 100 points.
  • Fly for a while. Every so often, a pickup appears up ahead of you, and drifts down to meet you. But no supply drop brings a Wing Cannons while one is in the sky already, drifting toward you.
  • Shoot down enemies, and now and then one leaves a pickup that drifts south. Heavy Bombers leave the most.
How it works

A pickup drifts, spins, and waits for one touch. When it’s caught, it reports, and it’s gone. Game Rules decides what the report means: Score adds the points, and Weapon Level and Planes do the rest.

Fighter Guns doesn’t know any of that happened. It reads one row of Weapon Levels, every time it fires. The level moves the finger. The row decides the guns.

That’s the idea under this chapter: keep what changes in a table, and let the Logic read it. Three rows of Yes and No turned one gun into three, with no new Event at all.

8.8 If It Didn’t Work

Pickups have two halves: the catch, and what the catch does. Find out which half isn’t working first.

Press Play, and a window called Can’t run yet opens? Its message names the piece and what’s wrong with it, so fix that, and press Play again.

You fly through the badge, and nothing happens? Look at its Collider first. Collision should be Sensor. Next, look at Collected by Fighter’s Collision Enter, where the Collision Target should read Fighter.

The badge sits still, and doesn’t turn? Look at its Transform, under Physics, for Allow Rotation. It should be On.

The badge vanishes with a chime, but the guns don’t change? Perhaps the message’s name doesn’t match. Collected by Fighter’s collectedEvent and Weapon Level’s Event must both read wingCannonsCollected, letter for letter.

The guns change, but fire the wrong way? Look at the two Ifs in Fighter Guns, and the three muzzle boxes. Each wing call names its own muzzle. Then look at the Fighter’s two new Coordinates.

Hold Space, and one tracer leaves, then only the rattle goes on? Control Sound and Start Cooldown have slipped below the whole fireRateReady If, so the cooldown starts again every moment you hold Fire. Take each off with Disconnect from Flow, and join it again from its own menu, Connect to Sequence. A loose block joins the line it came from.

No tracers at all now? Perhaps Start Cooldown’s Seconds still names fireRateSeconds, or a call’s flash box still names muzzleFlashParticles. Each of those boxes turns red, and the Errors bar under the canvas says which. Type the card’s value into it.

The crate adds no plane? Check repairCrateCollected, spelled the same on the Repair Crate and in Planes.

No supply drops ever come? Supply Drops belongs on Game Rules, with Wing Cannons and Repair Crate picked under Variables. And its Scene Start starts the timer.

Each of these is one fix. Make it, run the game again, and you’re back with the chapter. The catch-up download in the Goal is this chapter’s game, finished, if you’d like to compare.

Challenge

Make the levels your own. Open Weapon Levels and change Wing Cannons and Center Gun’s fireRateSeconds to 0.08. How does the top level feel now? Or add a fourth row, with every gun on and a faster rate. Weapon Level reaches it with no change at all. Put the table back before Chapter 9.

Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).

Your Fighter catches gifts now, and its guns grow and shrink with how well you fly. In Chapter 9, quick shooting starts to pay.