- Learn
- Build a Game
- Sky Ace
- 6 Mission Timeline
6 Mission Timeline
The mission gets its shape. Four waves come down on a clock, and each one opens with a banner across the window: WAVE 1 - CORAL SEA PATROL first, WAVE 4 - HEAVY SQUADRON last. Zero Fighters arrive in Vs, in columns, in rows and in long slanting sweeps from the sides, each formation at its own second. By the end of this chapter, a mission is almost two minutes of flying.
The game as it is at the end of this chapter. Click the game to play.
This chapter builds on Chapter 5’s game. Starting here? The catch-up download in Chapter 5’s Goal is that game, finished, ready to start from.
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.
To open it, right-click the downloaded file, choose Extract All... and then Extract. Start Myth Engine, choose File › Open Project... and pick the .mythproj file in the new folder. Beside it, OFL.txt is the license of the project’s font, Bebas Neue.
6.1 The Idea
So far a mission is one V. It comes down three seconds in, and after that the sky stays empty.
Now, a second into the mission, a dark strip appears across the window, with pale gold letters on it: WAVE 1 - CORAL SEA PATROL. At the top right, under the word STAGE, it says WAVE 1. The strip goes, and the V comes.
Two columns of Zero Fighters follow it, one down each side. Then a slanting line of planes sweeps in from the left edge and crosses the window as it falls. Two small Vs, a sweep from the right, a row of six, a wide V of seven. At 26 seconds the banner comes back: WAVE 2 - ACES HIGH.
That’s a wave: one of the mission’s four parts, each with its banner. Inside a wave, every group of planes still comes in a formation, the way the V did in Chapter 3. There are four kinds now. You know the V. The column, the row and the side sweep are new.
What brings them is the Mission Timeline from Chapter 3, grown to a row for each formation and its moment. But the way Chapter 3 waited has a catch. It set an alarm for the gap until the next row. A timer can only ring on a step of the game, so each ring comes a hair late, up to a sixtieth of a second. And each gap starts from that late ring.
Chain a gap for every row, and by the last V the hairs could add up to more than a third of a second. You’d hardly see that. But the table keeps growing, chapter by chapter, and every row should come when its time says. So this chapter gives the mission a clock.
Every step of play adds its own length to one number, the mission time. It works like the clock on a station wall. Every train has its time in the timetable, and it leaves when the station clock says so. A late train never makes the next one late, because nobody counts from the last train. Run Mission Timeline will read the timetable against the clock.
6.2 The Plan
Here’s how the pieces work together. Read each line from left to right: it starts with something that happens in the game.
- Gameplay starts -> Mission Clock sets
$missionTimeto 0 -> every step after that adds the step’s own length. - A row’s time comes on the clock -> Run Mission Timeline’s alarm rings -> Make a Row Arrive reads the row -> the row’s formation picks the Function that makes the planes.
- A wave’s time comes -> Run Waves sets
$waveand$bannerand sends waveStarted -> Show Banner shows the banner for a moment. - Whenever
$wavechanges -> the Wave Text, under STAGE, shows the new wave. - The mission fails -> nothing more arrives, and a banner that’s up goes away at once.
6.3 Set Up
This chapter adds three shared values and four placements. There are no new pictures.
Each Global Variable is made where it’s first needed, with the + on the Globals card of a canvas. You’ll make $missionTime in the first part, on Mission Clock’s canvas, and the other two in the last part, on Run Waves’. PLACE THESE waits too.
One line on the card looks cut off. The line for $banner stops after starts at, because $banner starts empty. Nothing is missing.
6.4 A Clock for the Mission
First, the mission learns to count its own time.
6.4.1 Mission Clock
Mission Clock does one small thing, over and over: every step of the game, it adds that step’s length to $missionTime. That length is $deltaTime, about a sixtieth of a second, and Myth gives it fresh every step. So the mission time is the seconds you’ve flown so far.
Its first Event, When the Scene Starts, sets it back to 0. That Event runs every time Gameplay starts, FLY AGAIN too, before any placed Object runs its When Created. So every mission starts from zero, and the alarms that read the clock will find it there.
Why not use the time since the game started? Myth Engine keeps that one too, but it goes on counting while the game is paused. This clock doesn’t. A paused game runs no steps, so there’s nothing to add. In Chapter 10 the game learns to pause, and the mission time will wait with it.
Mission Clock’s card has no Variables of its own. It changes only a Global Variable, never its Object. So make $missionTime first. Press the + on the canvas’s Globals card and choose New Global…, as you did for the score in Chapter 3. Give it Name missionTime, Type Number and Default 0.
For the second Event, type Step into the Add Event window’s filter. Pick Step itself, at the top of the list, above Stepped on Cell and Begin Step.
Its Change Variable adds on. So its Operator is +=, not the = it starts with. Then type $deltaTime into the Value box, as the card has it. The box keeps it as you typed it.
Counts the mission time ($missionTime): the seconds the mission has been played. A paused game runs no steps, so pauses are not counted.
Any Object- Variable
$missionTime- Value
0
- Variable
$missionTime- Operator
+=- Value
$deltaTime
Attach Mission Clock to Game Rules. Nothing shows yet. Nobody playing Sky Ace ever sees the mission time, yet every plane from here on arrives by it.
You can watch it count, though, if you borrow the score for a minute. Open Score Text, the score’s number from Chapter 3, and type {$missionTime} over {$score} in its Text.
Save, and run the game. Where the score was, a number climbs, with a long tail of decimals that spills onto a second line. Sixtieths of a second never come out even. Then put {$score} back.
6.5 Three More Formations
Next, three new ways for planes to arrive.
Each formation is a Function like Spawn V Formation, with a Repeat that makes its planes one at a time. The column and the row make them above the top of the window, at entryY. So they slide into view as they fly down. Each plane comes from the enemy Parameter, a Thing Object, so the table can send any kind.
You’ve built one of these already, so these three go faster. Make each one’s PARAMETERS first, on its Start block: Caller, then enemy, then the numbers. In Create Instance, pick From a Value at the bottom of Object to Create, and type enemy in the box that opens. A Parameter’s Default box keeps three decimals, and the cards give each default that way.
6.5.1 Spawn Column
A column is planes in single file. The first starts at entryY, and each one after it starts gap meters higher, at the same x. So they come down the window one behind another.
Creates a single-file line of enemies above the top edge at x, one every "gap" meters upward.
counttimes- Applies To
Caller- Object to Create
enemy- X
x- Y
entryY + local.index * gap
6.5.2 Spawn Row
A row is planes side by side, across the whole window. Spawn Row spaces them evenly. With count planes, there are count plus one gaps, all the same size, so none touches a side. Plane 1 sits one gap in from the left, plane 2 two gaps in, and so on. That’s the card’s X.
Creates a row of enemies spread evenly across the screen, just above the top edge.
counttimes- Applies To
Caller- Object to Create
enemy- X
width * (local.index + 1) / (count + 1)- Y
entryY
6.5.3 Spawn Side Sweep
A side sweep comes in from the side, not the top. The planes start beyond one edge of the window, each one farther out and lower than the last. All of them fly across and down together, so they cross the window as a slanting line.
The side comes from edgeX. Its If asks whether edgeX is more than 0. If it is, the sweep starts past the right edge, 22.5 meters across, and flies left. If not, it comes from the left and flies right. The True line and the False line are the same moves, mirrored.
Fly Down, on the Zero Fighter, sends each plane straight down the moment it’s made. Set Velocity changes that once, right after: acrossSpeed sideways, 7.5 meters a second, and fallSpeed down, about three.
Build the True line first. The If’s Add inside Then gives it its Repeat, and the Repeat’s Add inside Repeat its two blocks. Then right-click the If and choose Add Else. The Else’s own Add inside Else adds the False line’s Repeat. The Create Instance inside that Repeat names its plane created2 by itself, in Created As, because the first one already took created. So the two Repeats never mix up their planes. Each Set Velocity you add after a Create Instance is already aimed at its plane.
Creates a diagonal stream of enemies entering from beyond the left or right edge, flying across and down.
edgeX > 0counttimes- Applies To
Caller- Object to Create
enemy- X
width + startOut + local.index * stepOut- Y
startY - local.index * stepDown
- Applies To
created- X Velocity
-acrossSpeed- Y Velocity
-fallSpeed
counttimes- Applies To
Caller- Object to Create
enemy- X
-startOut - local.index * stepOut- Y
startY - local.index * stepDown- Created As
created2
- Applies To
created2- X Velocity
acrossSpeed- Y Velocity
-fallSpeed
6.6 One Row Arrives
Now the table can choose among four formations.
6.6.1 Make a Row Arrive
Make a Row Arrive gets a row’s number and makes that row happen. It reads the row’s formation cell, and four Ifs ask which word it is.
V calls Spawn V Formation. Column calls Spawn Column, Row calls Spawn Row, and Side Sweep calls Spawn Side Sweep. Only one of the four is true. So only one formation comes.
Each call hands its Function only the cells it needs. Every call gets enemy, from the object column, and count. A V and a column get x and spacing too, a sweep gets only x, and a row needs neither. Two Functions have their own names for a cell: Spawn Column’s spacing is gap, and Spawn Side Sweep’s x is edgeX. Any field a call leaves out keeps the Function’s own default, as the sweep’s two speeds do.
Make its two PARAMETERS first, Caller and row. Each If’s condition is one box. Type it whole.
In a Function, every box reads what you type as a small formula. So a word needs its quotes: "V", as the card has it.
Each call goes on its If’s True line. Right-click the If, choose Add inside Then, and find the Function in the window’s Functions group. Join it with Connect to sequence. On the call’s block, Caller is a list, and it already holds Caller. The others are boxes, enemy too. Type the card’s expressions into them, and press Enter to keep each one.
The next If comes after the first, back on the main line, so the card gives it no indent. Add it from the Action List, and join it with Connect to sequence. It goes under the first If, not inside it. The other two follow.
Makes one Mission Timeline row arrive: the Object its object cell names, in the row's formation.
data("Mission Timeline", row, "formation") == "V"- caller
Caller- enemy
data("Mission Timeline", row, "object")- x
data("Mission Timeline", row, "x")- count
data("Mission Timeline", row, "count")- spacing
data("Mission Timeline", row, "spacing")
data("Mission Timeline", row, "formation") == "Column"- caller
Caller- enemy
data("Mission Timeline", row, "object")- x
data("Mission Timeline", row, "x")- count
data("Mission Timeline", row, "count")- gap
data("Mission Timeline", row, "spacing")
data("Mission Timeline", row, "formation") == "Row"- caller
Caller- enemy
data("Mission Timeline", row, "object")- count
data("Mission Timeline", row, "count")
data("Mission Timeline", row, "formation") == "Side Sweep"- caller
Caller- enemy
data("Mission Timeline", row, "object")- edgeX
data("Mission Timeline", row, "x")- count
data("Mission Timeline", row, "count")
6.7 The Whole Mission
Now the timeline grows to the whole mission. And the planes come.
6.7.1 Mission Timeline
Twenty-three rows make the whole mission, Row 0 to Row 22, from the V at 3 seconds to a last V at 105. Most of the table is typing. It’s the longest typing of the book so far, so take it a wave at a time. Wave 1’s nine rows already use all four formations. The waves after it bring no new kind, only new times and places.
Each row’s name starts with its wave, so you can see where one wave ends. Type the names as the card has them.
The defaults do much of it. A new row starts with every column’s default: formation V, a Zero Fighter, x 11.25, count 5 and spacing 2.25. So each row needs only its name, its time, and what differs.
The card prints every cell, defaults too. Comparing them takes time, so type every cell if you like. A default typed over itself changes nothing.
What differs follows the kind of row. A V may change its x and count, and the wide Vs of seven their spacing. A column changes its formation, x, count and spacing, as the two at 6.5 seconds do: Column, their x, a count of 4 and a spacing of 2.8125. A side sweep’s x is the edge it comes from, 0 for the left and 22.5 for the right. Every row keeps its Zero Fighter.
The sweeps and the row have a spacing of 0. Nothing reads it for them, because a row spaces itself across the window and a sweep has numbers of its own. Type the 0 anyway, so your table matches the game’s.
Your first row is already there, with its old name. The card starts with its new name in green, Row 0: Wave 1: V at 3 s. Under it, red and struck, is the old one, Wave 1 V.
The unmarked values under those are the first row’s. They stay as they are. Double-click Wave 1 V in the Entry column and type the new name.
Add the others after it in the card’s order, which is time order. Run Mission Timeline reads the rows from the top. A row out of order would hold up every row after it, however early their times.
3VZero Fighter11.2552.256.5ColumnZero Fighter5.312542.81256.5ColumnZero Fighter17.187542.812510Side SweepZero Fighter05013VZero Fighter7.187532.2513VZero Fighter15.312532.2516Side SweepZero Fighter22.55019RowZero Fighter11.256022VZero Fighter11.257228ColumnZero Fighter11.2532.812536VZero Fighter11.2552.2540Side SweepZero Fighter04040Side SweepZero Fighter22.54048VZero Fighter7.812532.2548VZero Fighter14.687532.2558ColumnZero Fighter2.812532.812558ColumnZero Fighter19.687532.812568Side SweepZero Fighter22.55076VZero Fighter11.257282ColumnZero Fighter3.7532.812582ColumnZero Fighter18.7532.8125100Side SweepZero Fighter050105VZero Fighter11.2552.256.7.2 Run Mission Timeline
Run Mission Timeline still sets an alarm and waits. What’s new is how it sets it, and what it does when it rings.
The alarm is set by the clock now. When Created sets it for the first row’s time minus the mission time, so it rings when the clock reaches that row. Every later alarm is set the same way.
When it rings, Check Game State asks first whether the mission is still being played. If it is, a While takes over. A While runs its Actions again and again, as long as its condition holds. This one’s condition has two rows: there’s a row left, and that row’s time has come on the clock. Each pass through the While, Make a Row Arrive brings the row, and nextRow moves on to the next.
Follow it at 6.5 seconds. The alarm rings, and the While looks at row 1. Its time has come, so the left column arrives. Row 2’s has come too, and the right column arrives with it, side by side. Row 3 is at 10 seconds, not yet.
The While stops. Last, an If sets the alarm again, for 10 minus the clock’s 6.5, about three and a half seconds. Two rows with one time arrive together. And a ring that comes a hair late never makes the next one late.
Most of this card changes. Three of the four fields of Run Mission Timeline Data are struck. Open it from the Resources, and press each one’s Delete Field button, at its row’s end. Only nextRow stays.
Object Data of Logic "Run Mission Timeline" (its state that lasts between Events).
Back on the Logic’s canvas, in When Created, add the minus and $missionTime to the end of First Delay. On the card that line shows twice, the new one in green and the old one struck under it.
In the timer’s Event, delete the old blocks. Right-click a block and choose Delete. Deleting an If takes everything inside it along, so three deletes clear the Event: the first If, the Change Component Value and the last If. Changes cards has this too.
Now build the new steps, from Check Game State down. Check Game State already starts on Playing. The If after it is one box, and its condition is stillPlaying.value == true, as on the card.
The While goes on that If’s True line. Right-click the If, choose Add inside Then, pick While, and join it with Connect to sequence. A While’s condition is one box too, and this one asks two things at once, joined by &&, which means and. Type it whole, as the card has it. If, Else, Repeat and While has more on a While.
Two steps go inside the While. Right-click it and choose Add inside While for each, then join each with Connect to sequence. The call to Make a Row Arrive starts its Caller list on self, as the card has it. Type the card’s path into its Row box. Then Change Component Value moves nextRow on, with Plus in its Operator list, as in Chapter 3.
The last If goes after the While, on the first If’s True line. Right-click the first If, choose Add inside Then, and join it with Connect to sequence. It joins the end of that line, after the While. Its Start Recurring Timer goes on its own True line. The Timer Name takes timeline bare, and Ring Once is a list on Yes.
The order once more: Check Game State, the If, and on its True line the While with its two steps, then the last If with its timer.
Runs the Mission Timeline: each row's formation arrives at its mission time, in order.
Any Objectself.runMissionTimelineData- Timer Name
timeline- First Delay (s)
data("Mission Timeline", self.runMissionTimelineData.nextRow, "timeSeconds") - $missionTime- First Delay (s)
data("Mission Timeline", self.runMissionTimelineData.nextRow, "timeSeconds")- Ring Once
Yes
data("Mission Timeline", self.runMissionTimelineData.nextRow, "formation") == "V"- caller
self- enemy
data("Mission Timeline", self.runMissionTimelineData.nextRow, "object")- x
data("Mission Timeline", self.runMissionTimelineData.nextRow, "x")- count
data("Mission Timeline", self.runMissionTimelineData.nextRow, "count")- spacing
data("Mission Timeline", self.runMissionTimelineData.nextRow, "spacing")
- Component Value
self.runMissionTimelineData.nextRow- Operator
+=- Value
1
self.runMissionTimelineData.nextRow < dataCount("Mission Timeline")- Component Value
self.runMissionTimelineData.lastRowTime- Value
data("Mission Timeline", self.runMissionTimelineData.nextRow - 1, "timeSeconds")
- Component Value
self.runMissionTimelineData.nextRowTime- Value
data("Mission Timeline", self.runMissionTimelineData.nextRow, "timeSeconds")
- Component Value
self.runMissionTimelineData.gapSeconds- Value
self.runMissionTimelineData.nextRowTime - self.runMissionTimelineData.lastRowTime
- Timer Name
timeline- First Delay (s)
self.runMissionTimelineData.gapSeconds- Ring Once
Yes
- Action Name
stillPlaying- Game State
Playing
stillPlaying.value == trueself.runMissionTimelineData.nextRow < dataCount("Mission Timeline") && data("Mission Timeline", self.runMissionTimelineData.nextRow, "timeSeconds") <= $missionTime- caller
self- row
self.runMissionTimelineData.nextRow
- Component Value
self.runMissionTimelineData.nextRow- Operator
+=- Value
1
self.runMissionTimelineData.nextRow < dataCount("Mission Timeline")- Timer Name
timeline- First Delay (s)
data("Mission Timeline", self.runMissionTimelineData.nextRow, "timeSeconds") - $missionTime- Ring Once
Yes
Save, and run the game. The V comes at three seconds, as before. Keep watching. Two columns come down the sides, and a sweep slants in from the left. A minute later the planes are still coming, in every formation, until the last V at 105 seconds.
After it, the sky is quiet. There’s no banner yet, because the banners are the last part. No plane at all? Then the clock is the first place to look, in If it didn’t work.
That’s the biggest part of the chapter behind you. If you’d like a break, this is a good place for one.
6.9 If It Didn’t Work
Something may be a little off the first time. That’s normal. This chapter is long, and most of it is typing, where one wrong number hides well. Run the game, watch what it does, and think it through from the clock to the planes.
No plane at all? Look at the clock first. Is Mission Clock attached to Game Rules, where Run Mission Timeline is? Is Every Step’s Operator +=, and its Value $deltaTime?
With = there, the mission time never gets past a sixtieth of a second. No row’s time ever comes.
Once the waves are in, the first banner still comes. Its alarm is a countdown, and a countdown runs out whatever the clock says. The planes wait for more. The While lets a row through only when the clock itself has reached the row’s time.
The game won’t start, and Myth says Can’t run yet? Its message names the Logic Component that’s wrong, and what’s wrong with it. Open that Logic, and the Errors bar under its canvas points at the block. Perhaps a box lost a quote or a bracket. Count them against the card.
Only the first V comes, or the rest come late? Look at the While’s condition. Its first half asks whether a row is left, and its second whether that row’s time has come on $missionTime. Look last at the second If’s Start Recurring Timer, whose First Delay ends in - $missionTime.
A formation comes in the wrong shape? Look at that row in Mission Timeline: its formation word must be spelled the way Make a Row Arrive compares it, V, Column, Row or Side Sweep. Then look at that If’s box. The word needs its quotes there.
A side sweep comes in from the wrong side, or flies off the wrong way? Look at Spawn Side Sweep’s two Set Velocities. The True line’s X Velocity is -acrossSpeed, and the False line’s is acrossSpeed. Each should be aimed at its own plane, created or created2.
The banner never shows? Look for Show Banner on both pieces, and waveStarted spelled the same in Run Waves and in Show Banner.
The banner shows but stays? Then the timer and its Event don’t share a name. Show Banner’s Start Recurring Timer names it banner, and so must the When bar of On the Banner Timer.
The banner’s words sit behind the strip? Give the Banner Text’s placement its Z-Index of 111, so the words draw on top.
A dark strip with no words lies across the window from the very start? A banner piece was placed visible. Turn off Visible at Game Start on both placements, and the strip waits for its wave.
The banner says WAVE 2, but the wave under STAGE still reads WAVE 1? Look at Run Waves’ step 3, which puts the wave’s label into $wave. Then look at the Wave Text’s Text. It should be {$wave}, braces and all.
A long number with a tail of decimals sits where the score should be? Score Text still holds {$missionTime}, from when you watched the clock. Type {$score} back into its Text.
Each of these is one small fix. Make it, run the game again, and you’re back with the chapter. And if you’d like to compare, the catch-up download in the Goal is this chapter’s game, finished.
The mission is yours to rearrange now. One thing first: Chapter 7 starts from the cards’ tables. So note what you change, and put it back before you go on. Or start Chapter 7 from this chapter’s catch-up download.
Open Mission Timeline and move a row’s time, and watch its formation come sooner. Or add a row of your own: a column of seven down the middle, or two sweeps from both sides at once.
Or give the mission a fifth wave. It needs a row in Waves, with its own name on the banner, and a few rows in the timeline after it. What would you call it?
Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).
Four waves fly on a clock now, and every plane in them is a Zero Fighter. In Chapter 7, the banners keep their word: Red Aces, Dive Bombers and Heavy Bombers.
