Build a GameCreating Pacific Wings

6  Aces and Heavy Bombers

Goal

Two more kinds of enemy join the route. Red-orange Aces weave from side to side and aim at you. Big olive-green Heavy Bombers crawl south and take five hits to bring down. Waves 2 and 3 and the final approach wait along the route, so a mission now has a long, crowded sky to fly through.

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 7 from it.

Pacific Wings, Chapter 6 finishedMyth project · 1.5 MB

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

6.1 The Idea

Five Enemy Fighters come down the sky in a neat little group. They fly straight, they fire straight, and you learn to sit beside them. After that, the route goes quiet.

Now picture what comes after. Two red-orange planes slide into view and start swinging left and right, never where you aimed. A red tracer leaves one nose and leans toward you. Behind them, a big olive-green bomber crawls in, rocking gently, and five hits later it finally breaks up.

Those are the Enemy Ace and the Heavy Bomber. Back in Chapter 3, a promise was made: new enemies would cost no new rules. This chapter keeps it. Each is two more lines in work you’ve done: a row in Enemy Types, and a plane that carries the same Logic the Enemy Fighter does.

Two things are new, and both are moves. The first is a weave, a slide from side to side. It’s like a pendulum’s swing: out one way, back the other, on a steady beat. A recurring timer keeps that beat.

The second is the aimed tracer, an Enemy Tracer fired toward the Fighter. Every Enemy Fighter shoots straight south today. An Ace looks for you first, and so does a Heavy Bomber. Each turns its gun your way, but never more than 65 degrees from south.

Both moves need a number the enemy keeps for itself, such as which way it’s swinging. Return Flight did the same in Chapter 4, with isDimmed. The number lives in a Data Structure of its own, made by the Logic and carried by each plane that wears it. Two Aces weaving side by side each keep their own, and neither ever reads the other’s.

6.2 The Plan

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

  • An Ace or a Heavy Bomber comes into view -> Weave sends weaveTurn to its own plane, and starts its weave timer.
  • The message weaveTurn arrives -> Weave flips weaveSide, sets the sideways speed and tips the nose into the turn.
  • The weave timer rings -> Weave sends weaveTurn again.
  • The enemyFire timer rings -> Fire When Seen looks for the Fighter, works out the direction toward it, keeps it heading south and within aimConeDegrees of south, and fires that way.

6.3 Set Up

Two pictures from the Starter Kit join the game, Enemy Ace Art and Heavy Bomber Art. They’re in your project already.

Set upWhat this chapter adds
ART (sprites, from the starter kit)
Enemy Ace Art
Heavy Bomber Art
SCENE Route: PLACE THESE
ObjectXY
Enemy Ace4.68837.5
Enemy Ace15.31337.5
Enemy Fighter8.12535.625
Enemy Fighter11.87535.625
Enemy Ace1048.75
Enemy Fighter6.71946.875
Enemy Fighter13.28146.875
Heavy Bomber1067.5
Enemy Ace5.46965.313
Enemy Ace14.53165.313
Enemy Fighter3.12563.125
Enemy Fighter16.87563.125
Heavy Bomber6.87582.188
Heavy Bomber13.12582.188
Enemy Ace1079.688
Enemy Ace4.84494.688
Enemy Ace15.15694.688
Heavy Bomber1097.188

PLACE THESE is the new enemies’ route, eighteen planes in all, and it waits for the Objects. It comes in the chapter’s last part, once the planes it names have been made.

6.4 Two New Rows

Every kind of enemy reads its numbers from Enemy Types. The two new kinds need two new rows. The rows need five new blanks.

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

Open Enemy Stats and press Add Field five times, as you did in Chapter 3. Each new field arrives as a Number at 0, under a name of Myth’s, so type the card’s name over it. Only two Values change. weaveSeconds is 1, and tracerSpeed is 7.7.

Each new field is a column of Enemy Types at once. Open the table and see.

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
Row 1Enemy Ace
speed4.25
health2
points250
firstShotSeconds1
shotSecondsMin0.9
shotSecondsMax1.7
weaveSpeed3.5
weaveSeconds0.68
weaveAngle6
tracerSpeed8.9
aimConeDegrees65
Row 2Heavy Bomber
speed2.1
health5
points600
firstShotSeconds2.15
shotSecondsMin0.78
shotSecondsMax1.25
weaveSpeed1.5
weaveSeconds1.45
weaveAngle6
tracerSpeed8.9
aimConeDegrees65

Look at the Enemy Fighter’s row first, where the five new cells all start at 0, even tracerSpeed. Each cell got its 0 when its field was added, and a Value typed later never reaches a row that’s already there. So double-click the Enemy Fighter’s tracerSpeed and type 7.7. Its other new cells are 0 on the card, so they stay.

Press Add Row twice. Each new row is named Row, so type Enemy Ace over the first and Heavy Bomber over the second. Go across each row, cell by cell, from the card.

The rows read like two characters. The Ace is quick, at 4.25 meters a second, with health 2 and a weave every 0.68 seconds. The Heavy Bomber crawls at 2.1, takes five hits, and swings slowly, every 1.45 seconds. Both aim, 65 degrees either side of south, while the Enemy Fighter’s 0 means straight down, as it always has.

Nothing in the game has changed yet. Two rows are waiting, with every number filled in, for planes that will read them.

6.5 The Weave

Now the new enemies learn to swing.

6.5.1 Weave

From the plane’s point of view, the weave is one small job, done again and again: turn to the other side.

It flips which side it’s heading for, slides that way, and leans into the turn. The side is weaveSide, in Weave’s own Data Structure: 1 for right, -1 for left. A flip is a multiply by -1. The speed and the lean both follow weaveSide, so they always point the same way.

That job is a message the plane sends itself, weaveTurn. Who sends it? Two things. Enter View sends it once, the moment the plane comes into sight, so the swing starts at once. After that, the weave timer sends it every weaveSeconds.

Data StructureWeave Data

Object Data of Logic "Weave" (its state that lasts between Events).

weaveSideNumberdefault -1
Logic ComponentWeaveAttach to: Enemy Ace, Heavy Bomber

Once in view, slides from side to side around where it came into view, turning every weaveSeconds and banking weaveAngle degrees into each turn.

Attach toAny Object
DATA STRUCTURES
Weave Dataself.weaveData
SHARED VARIABLES
NameTypeKindStarting Value
weaveSpeedNumberConstant3.5
weaveSecondsNumberConstant0.68
weaveAngleNumberConstant6
When Entering the View
WhenEnter View
1Trigger Custom Event on Object
Custom Event
weaveTurn
2Start Recurring Timer
Timer Name
"weave"
Repeat Every (s)
@self.weaveSeconds
First Delay (s)
@self.weaveSeconds / 2
On the Weave Timer
WhenRecurring TimerTimer name: weave
1Trigger Custom Event on Object
Custom Event
weaveTurn
On Weave Turn
WhenweaveTurnCustom Event
1Change Component Value
Component Value
self.weaveData.weaveSide
Operator
*=
Value
-1
2Set Velocity
X Velocity
@self.weaveSpeed * self.weaveData.weaveSide
3Set Rotation
Degrees
@self.weaveAngle * self.weaveData.weaveSide

Here is one small trick. The timer’s First Delay is weaveSeconds / 2, half a beat. So the first swing is short, half a beat to the right, and every swing after it is a full beat, back across. That way the plane weaves around the spot where it came into view, instead of drifting off to one side.

And here’s why the plane still flies south. Set Velocity leaves its Y Velocity empty, and an empty box keeps the speed the plane has. Chapter 3 left X empty in Fly South When Seen for exactly this moment. Each of the two pieces sets its own half of the speed, so they never fight.

Make Weave as an Empty Logic Component, with its three Shared Variables. Its Data Structure is next. It comes the way Return Flight’s did. Press the + on the Data Structures card, choose New Data Structure, and Myth makes Weave Data. Add one field, weaveSide, a Number. Its Value is -1, so the first flip turns the plane to the right.

Enter View comes first. Trigger Custom Event on Object sends a message to one Object, Self here, as it already says. Type weaveTurn into its Custom Event box. Next comes Start Recurring Timer. Its Timer Name is "weave", with its quote marks, and its Repeat Every (s) is @self.weaveSeconds. Its First Delay (s) box starts empty, and @self.weaveSeconds / 2 goes there.

The weave timer’s Event is next. Add Recurring Timer, choose Type a timer name…, and type weave. Its one step is a second Trigger Custom Event on Object, with weaveTurn.

Last comes the message itself. weaveTurn is in the Add Event list now, since the first step sends it. Pick it there. Under it, Change Component Value changes self.weaveData.weaveSide. Click the = on its block, pick *= · Multiply, the one with a star in front, and type -1 into the Value. The star is how Myth writes “times”, so type the two products after it with their stars.

The card says Weave attaches to the Ace and the Heavy Bomber. They don’t exist yet. That’s next.

6.6 Two New Planes

Now the rows get planes to read them.

ObjectEnemy Ace
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
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
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")
speed > KindExpression
speed > ExpressionobjectComponentProperty("self","enemyStats","speed")
ObjectHeavy Bomber
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
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
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")
speed > KindExpression
speed > ExpressionobjectComponentProperty("self","enemyStats","speed")

Each card is the Enemy Fighter’s again, with its own numbers. You’ve done this before, in Chapters 3 and 4. Make a 2D Object, and go down its card from the top, as you did then.

Click Coordinate in the toolbox’s Core group, and name it Gun Muzzle. In Attached Components, its row can land below the fold, so scroll down if you don’t see it. Its Y is -0.94 on the Ace and -0.932 on the Heavy Bomber: each nose, a little further south on the bigger plane.

Click Data Structure in the Data group, and Enemy Stats is picked at once. Set its Input Source to Data Table, its Data Table to Enemy Types, and its Row to Enemy Ace. Now the Ace reads its own row. The two Tags are waiting in the Tags list: Enemy Aircraft, then Enemy Threat, each added with the +.

Then pick Logic in the list above the preview. Click each Logic Component the card names, in its order, Weave first. Two Data Structures arrive with them. Weave Data and Fire When Seen Data come from the Logic itself, so there’s nothing to make. Back in Components, the Shared Variables are under Variables. Press tracer’s None button. Pick Enemy Tracer. Every line with Kind Expression reads the plane’s own row. Set those Kinds, and type each line, as you did for the Enemy Fighter. That’s why the Ace and the Heavy Bomber can share one Weave. Each reads its own row.

The card lists two lines you haven’t met yet: fighter, and aimConeDegrees. Leave them for now. They come with the aimed fire, at the end of this chapter. Every other piece of Logic on these planes is one you wrote for the Enemy Fighter. Chapter 3’s promise holds. Not one new rule was written for these two planes, which only carry the old ones.

6.7 Aimed Fire

The last piece changes how every enemy fires.

6.7.1 Fire When Seen

Picture an Ace up ahead and to your left. When its timer rings, it asks a plain question first: where’s the Fighter from here? How far across, and how far down? Those are two fields of Fire When Seen’s own Data Structure, toFighterX and toFighterY, one for each of the two questions.

There may be no Fighter at all. So Check Instance Count counts the Fighters in the sky, and names its answer fighterInSky. If there is one, each distance is the Fighter’s position minus the enemy’s. If not, the enemy aims straight down.

In that case there’s no distance across, and the distance down is 2.8 meters, minimumForwardMeters. On the card it’s -minimumForwardMeters, with a minus sign, because south is the minus way on the Y.

After that come two more checks, told below the card. The shot always heads south, and it stays inside a cone.

Data StructureFire When Seen Data

Object Data of Logic "Fire When Seen" (its state that lasts between Events).

toFighterXNumberdefault 0
toFighterYNumberdefault 0
Logic ComponentFire When SeenAttach to: Enemy Fighter, Enemy Ace, Heavy BomberAddDelete

Once in view, fires its tracer from the Gun Muzzle toward the Fighter (kept within aimConeDegrees of south; 0 fires straight south), after firstShotSeconds and then every shotSecondsMin to shotSecondsMax seconds.

Attach toAny Object
DATA STRUCTURES
Fire When Seen Dataself.fireWhenSeenData
SHARED VARIABLES
NameTypeKindStarting Value
tracerObjectConstantNone
tracerSpeedNumberConstant7.7
firstShotSecondsNumberConstant1.15
shotSecondsMinNumberConstant1.4
shotSecondsMaxNumberConstant2.5
muzzleFlashParticlesNumberConstant5
fighterObjectConstantNone
aimConeDegreesNumberConstant0
minimumForwardMetersNumberConstant2.8
When Entering the View
WhenEnter View
1Start Recurring Timer
Timer Name
"enemyFire"
Repeat Every (s)
@self.firstShotSeconds
Ring Once
Yes
On the Enemy Fire Timer
WhenRecurring TimerTimer name: enemyFire
1Declare Variable
Name
direction
Value
-90
2Check Instance Count
Action Name
fighterInSky
Object
@self.fighter
Operator
>
Value
0
3IffighterInSky.value == true
Then
4Change Component Value
Component Value
self.fireWhenSeenData.toFighterX
Value
@self.fighter.transform.position.x - self.transform.position.x
5Change Component Value
Component Value
self.fireWhenSeenData.toFighterY
Value
@self.fighter.transform.position.y - self.transform.position.y
6Otherwise
7Change Component Value
Component Value
self.fireWhenSeenData.toFighterX
Value
0
8Change Component Value
Component Value
self.fireWhenSeenData.toFighterY
Value
-@self.minimumForwardMeters
9Ifself.fireWhenSeenData.toFighterY > (-@self.minimumForwardMeters)
Then
10Change Component Value
Component Value
self.fireWhenSeenData.toFighterY
Value
-@self.minimumForwardMeters
11Change Variable
Variable
direction
Value
radtodeg(atan2(self.fireWhenSeenData.toFighterY, self.fireWhenSeenData.toFighterX))
12Ifdirection < (-90 - @self.aimConeDegrees)
Then
13Change Variable
Variable
direction
Value
-90 - @self.aimConeDegrees
14Ifdirection > (-90 + @self.aimConeDegrees)
Then
15Change Variable
Variable
direction
Value
-90 + @self.aimConeDegrees
16Fire From MuzzleFunction
shooter
self
tracer
@self.tracer
muzzle
"Gun Muzzle"
tracerSpeed
@self.tracerSpeed
flashParticles
@self.muzzleFlashParticles
direction
direction
direction
-90
flash
'Enemy Gun Muzzle'
17Control Sound
Sound
Enemy Guns
18Generate Random Number
Action Name
nextShotSeconds
Minimum
@self.shotSecondsMin
Maximum
@self.shotSecondsMax
19Start Recurring Timer
Timer Name
"enemyFire"
Repeat Every (s)
nextShotSeconds.value
Ring Once
Yes

Two more questions make the aim fair. The first keeps the tracer moving south. If the Fighter is beside the enemy, or behind it, toFighterY would send the tracer sideways or back up the screen. So the Fighter is always counted at least 2.8 meters south. So when toFighterY is more than -minimumForwardMeters, the Fighter is less than 2.8 meters south. Then toFighterY becomes -minimumForwardMeters.

Next, the two distances become one direction, in degrees, the way Fire From Muzzle wants it. That’s what atan2 does. It takes the down first, then the across, as the card writes them. It hands back the angle of the line from the enemy to the Fighter. radtodeg turns that angle into degrees. Straight south is -90, the same -90 your enemies have fired along since Chapter 4.

The second question keeps the aim inside a cone. A direction more than aimConeDegrees past -90, either way, is pulled back to the cone’s edge. With 65, an Ace can aim well off to the side, but never flat across. With the Enemy Fighter’s 0, both edges are -90. The cone shuts, and it fires straight south, the same as always.

Fire From Muzzle then takes direction in place of the old -90. direction lasts one run of the Event. Declare Variable makes it at the start, at -90, and it’s gone when the Event ends.

This card is the longest change in the chapter, and all of it goes in one place: between the timer’s Event and Fire From Muzzle. Open Fire When Seen. Add the three new Shared Variables: fighter, an Object starting at None, like tracer, and the two Numbers. Then make its Data Structure with New Data Structure, as you did for Weave. Myth names it Fire When Seen Data. Its two fields, toFighterX and toFighterY, are Numbers that start at 0, one for each distance.

On the canvas, find the timer’s Event. Then right-click its Fire From Muzzle block and choose Disconnect from Flow. It slides off the line, loose, with everything you typed still in it. Do the same for the three blocks after it, one by one, until nothing hangs under the Event. The line is empty.

Declare Variable goes first, joined with Connect to Sequence. Its Type stays Number. Type direction into its Name and -90 into its Value. Check Instance Count comes next. It counts the Fighters in the sky. Type fighterInSky over its Action Name, and @self.fighter as its Object. Its Operator starts on >=. Pick > in its list, then type 0 over the 1 in its Value.

Next comes an If that asks fighterInSky.value == true. Its True line holds the two Change Component Value steps for the distances, and its Else holds the other two. Each one’s Component Value is a field of Fire When Seen Data, such as self.fireWhenSeenData.toFighterX. Every step on each line comes from the If’s own menu, Add Inside Then or Add Inside Else, as in Chapter 4.

The rest join the end of the line, one after another. Here’s where they land. On the canvas, the If’s True line runs straight down, and its Else line runs off to the right. Below them, the two lines meet again. A block you add from the Action List lands beside the main line, and Connect to Sequence joins it to the end of that line. So it hangs under the meeting point, after the whole If. It never goes inside the True line or the Else, because only the If’s own menu puts a block there.

And an If with no Else? It has only its True line, and a block joined after it still goes to the main line, below the whole If.

So, after the first If: an If that asks whether toFighterY is more than -minimumForwardMeters, and its one step. A Change Variable for direction. Two more Ifs, each with one step. Type each condition and Value exactly as the card shows it, brackets and all, since one missing bracket stops the whole game from running.

Last of all, right-click Fire From Muzzle and choose Connect to Sequence. It joins under the last If, on the main line. Then do the same for Control Sound, Generate Random Number and Start Recurring Timer, in that order. Type the word direction over the -90 in Fire From Muzzle’s direction box. The card shows that change on two lines: the new direction, and the old -90, struck through.

ObjectEnemy Fighter
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
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
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")
speed > KindExpression
speed > ExpressionobjectComponentProperty("self","enemyStats","speed")

Three planes carry Fire When Seen now. Each needs the new lines under its Shared Variables. Open the Enemy Fighter first. Press fighter’s None button. Pick Fighter. aimConeDegrees and tracerSpeed each become an Expression, reading the plane’s own row, as on the card. Fire When Seen Data is already there. It came with the Logic. The Enemy Ace and the Heavy Bomber get the same two lines. Their cards show them. Now every kind of enemy aims with its own row.

6.8 The Waves

Now place the eighteen, from PLACE THESE. Open Route, pick Place, drag each plane from Predefined Objects onto the Scene, then pick Select, click it, and type its World Position. It’s the most repetitive work in the chapter. Put on some music.

The planes come in groups, from south to north. Wave 2 first: two Aces at 37.5 with two Enemy Fighters between them, and another Ace at 48.75 with two more Fighters a little south of it. Wave 3 is built around a Heavy Bomber at 67.5. The final approach brings two Heavy Bombers side by side, with Aces among them, and one last bomber at 97.188.

Save, and run the game.

The first wave comes down as it always has. Then, a little later, two red-orange Aces slide into the top of the view and begin to swing. Their tracers lean toward you. Move left, and the next tracer follows you there.

Play it!

Watch for each of these, one at a time.

  • Wave 1 is unchanged. Enemy Fighters fly straight south and fire straight down, the way they always have.
  • The Aces swing left and right, first to the right. Each swing is about two-thirds of a second, and the nose tips into it.
  • The Heavy Bombers come slowly, rocking from side to side on a longer beat, nearly a second and a half.
  • Sit off to one side. An Ace’s tracers lean toward you, and so do a Heavy Bomber’s. An Enemy Fighter’s still fall straight.
  • Fly right past an Ace. It’s beside you now, and its tracers still go south, never sideways.
  • An Ace takes two hits and is worth 250 points, and a Heavy Bomber takes five, for 600.
How it works

Two rows. Two planes. A swing on a timer, and a gun that looks before it fires.

None of Chapter 3’s Logic changed for the new enemies: the same Logic Components fly them south, count their hits and fire their guns. Each reads its own row, so two numbers in a table make an Ace quick and a Heavy Bomber slow. And when the aim changed, it changed once, in Fire When Seen, for all three kinds. The Enemy Fighter didn’t need a different Logic to keep shooting straight. It needed a 0.

6.9 If It Didn’t Work

A long card means many small boxes, and a slip in one is part of the work. Run the game, and watch what each plane does.

Press Play, and a window called Can’t run yet opens instead of the game? Its message names the piece and what’s wrong with it. Fix it, and press Play again.

The Aces never come? Look in the Scene for their placements, at the World Positions on the card. Perhaps one went in with its X and its Y swapped, so it waits somewhere off the route.

The Aces fly straight down? Open the Enemy Ace and pick Logic, where Weave should be in the Attached Logic. Back in Components, its three Shared Variables should be Expressions. Last, look at the Ace’s Data Structure, where the Row should be Enemy Ace.

They drift away to one side, and never come back? Look at weaveTurn’s Change Component Value. Its Operator should read *=. With =, weaveSide stays -1, and every turn goes the same way.

The Aces swing, but stop flying south? Look at Set Velocity in Weave, where the Y Velocity box should be empty.

Every enemy fires straight down, Aces too? Look at the Ace’s Shared Variables. aimConeDegrees should be an Expression, and fighter should be Fighter. In the Ace’s row of Enemy Types, aimConeDegrees should be 65.

Tracers fly sideways, or north? Look at Fire From Muzzle’s direction box, which should read direction. If it does, look at the two cone Ifs. A minus sign can slip out of a box without a sound, and the cone turns around.

The Enemy Fighters’ tracers hang in the air without moving? Their tracerSpeed cell in Enemy Types is still 0. Type 7.7.

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 a weave of your own. Give the Enemy Fighter’s row a weaveSpeed and a weaveSeconds, and attach Weave to the Enemy Fighter too, with its three Shared Variables as Expressions, as on the Ace. Now wave 1 swings. Try a wide, slow swing, or a fast little shiver. Put the row back to its zeros afterward, and take Weave off the Enemy Fighter, before Chapter 7.

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

Your sky has three kinds of enemy now. One of them is watching you. In Chapter 7, the sea below fills up, and the route itself begins to move the mission on.