- Learn
- Build a Game
- Pitch Shot
- 10 Remembering, Settings and Finish
10 Remembering, Settings and Finish
Close Pitch Shot and return to your stars. Sound and Motion keep your choices, How to play welcomes you once, and the last stage has its own farewell.
The game as it is at the end of this chapter. Click the game, then press Enter to start.
If you’d like to compare, download this chapter’s finished project. Unzip it first. This is the complete Pitch Shot you played before Chapter 1.
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. Nunito-OFL.txt beside it comes with the game.
10.1 The Idea
Sunny Meadow holds its first stars. The next stage waits beyond a lock that has opened for you. Close the game. The stars go too. Tomorrow the meadow looks as though you never came.
This chapter keeps what belongs to you. A Persistent Value stays on the device after the game closes. Its Key is the name you keep it under. Give each place its own key, and its stars have somewhere to wait.
The stages already keep their stars in a table. During play, that table is still the record every map reads. A win keeps a copy. When the whole game opens again, it reads that copy back. The maps read Stages.
There are other small things worth keeping. Perhaps you want quiet sound, or notes that stand still. Perhaps How to play has already told you everything you need to begin. Those choices stay too.
And at the far end, the castle has an ending. Its notes sing from low to high, then together. The first meadow was silent. Now even the last stage has its own song to share with you.
10.2 The Plan
- The stars return when you reopen the game. Read Back What Was Kept hears Game Start. It loads each place’s stars, then Unpack A Place’s Stars writes them into Stages.
- A win keeps its best result. Win The Stage writes the best stars, then sends starsCounted. Keep The Stars saves it.
- The icons remember your choices. Sound and Motion change Globals, save the choice, and update their icons. The Field’s Logic reads those same Globals.
- The welcome remembers you. Got It Button saves howToPlaySeen and sends howToPlayClosed. The notes can begin.
- The last stage has a farewell. After the last win card, Go On To The Next Stage sends finaleShown. The ordinary card hides, the finale appears, and the last notes sing.
Build the remembering first. Then give the waiting buttons their work, and finish the Field’s cards.
10.3 Set Up
#1B3552Choose Image..., then the fileAdd the three Boolean Globals from an open Logic canvas’s Globals card. Pick No for each starting false. Read Back What Was Kept will replace them with the device’s choices when the game starts.
The new placements all belong to Field. Make their Objects below before placing them. Sound Button and Motion Button already exist; this chapter gives them Logic and adds them to the Field too.
The page outside the game gets its own picture. Download the page background, then choose that file through Display > Page Background (Outside the Game) > Image. Use the card’s Color too. On an upright phone, this picture asks the player to turn it sideways.
10.4 Stars That Wait for Tomorrow
The meadow shouldn’t have to earn its stars again every morning.
10.4.1 Place and Places, Changed
Each place gets a name for its kept stars. That’s starsKey. It belongs beside the place’s name, picture and tune.
One place: its name, its backdrop, its tune, and the key its stars are kept under.
Sunny MeadowBackdrop Sunny MeadowMusic Sunny Meadowstars-0Seaside BeachBackdrop Seaside BeachMusic Seaside Beachstars-1Autumn WoodsBackdrop Autumn WoodsMusic Autumn Woodsstars-2Red Rock CanyonBackdrop Red Rock CanyonMusic Red Rock Canyonstars-3Candy HillsBackdrop Candy HillsMusic Candy Hillsstars-4Snowy PeaksBackdrop Snowy PeaksMusic Snowy Peaksstars-5Jungle FallsBackdrop Jungle FallsMusic Jungle Fallsstars-6Pirate CoveBackdrop Pirate CoveMusic Pirate Covestars-7Mushroom GroveBackdrop Mushroom GroveMusic Mushroom Grovestars-8Volcano IsleBackdrop Volcano IsleMusic Volcano Islestars-9Cloud KingdomBackdrop Cloud KingdomMusic Cloud Kingdomstars-10Moonlit CastleBackdrop Moonlit CastleMusic Moonlit Castlestars-11Add the field, then fill only the new starsKey column in Places. Keep the existing names, pictures and tunes. The keys run from stars-0 to stars-11; each place needs its own. Text cells need no quotes.
10.4.2 Unpack A Place’s Stars
A place keeps all its stage results in one number. Think of little dials. Each dial has four positions: no stars, or one, two or three. The number holds all those positions together.
This is base 4. Each next position has four times the weight of the one before it. Unpack A Place’s Stars finds one position at a time, then writes its stars back into Stages.
Writes the stars of a place's stages ($stagesPerPlace of them) into the Stages table from one number kept on the device (base 4: the place's stage k is digit k).
$stagesPerPlacetimes- Table
Stages- Row
place * $stagesPerPlace + blockLocal("index")- Column
"stars"- Value
mod(floor(packed / pow(4, blockLocal("index"))), 4)
The Function has two Number Parameters, place and packed. Its Repeat visits every stage of that place. Here blockLocal(“index”) reads this Repeat’s counter, starting at zero, as local.index does in the other cards. Copy the spelling shown here. In the Value, pow finds a position’s weight, floor drops the smaller positions, and mod keeps the position being read.
Every place has its own set of dials. Its key keeps those stars separate from every other place’s results, even though all the maps share one running table. That is why the load fetches each place’s own number before the Function writes its rows back into the running table. The maps still read that table. The saved number is a copy of those rows, ready to rebuild them when a new game starts tomorrow.
The formula looks crowded. Build it as shown, then watch the stars return. You can come back to its arithmetic with a working map beside you.
10.4.3 Read Back What Was Kept
The game opens at Title. Before its buttons are created, Read Back What Was Kept visits each place and fetches its packed stars. It hands that number to the Function you made above.
It also fetches the choices for sound, motion and How to play. A missing kept value uses Default instead. So a device that has never played begins with no stars and the ordinary sound and motion.
When the game opens, reads back once what this device kept: the stars (into the Stages table) and whether How to play was seen.
Any ObjectdataCount("Places")times- Action Name
keptStars- Key
data("Places", local.index, "starsKey")- Default
0
- place
local.index- packed
keptStars.value
- Action Name
keptHowToPlay- Key
"howToPlaySeen"- Default
0
keptHowToPlay.value == 1- Variable
$howToPlaySeen- Value
true
- Variable
$howToPlaySeen- Value
false
- Action Name
keptSound- Key
"soundOff"- Default
0
keptSound.value == 1- Variable
$soundOff- Value
true
- Variable
$soundOff- Value
false
- Action Name
keptMotion- Key
"calmMotion"- Default
0
keptMotion.value == 1- Variable
$calmMotion- Value
true
- Variable
$calmMotion- Value
false
Load Persistent Value has a Key and a Default. Name its answer with Action Name, as for the random number in Bob While Waiting. keptStars.value is the number it brought back. Keep that load and its Function call together inside the Repeat.
The other loads follow the Repeat. They use the text keys from their cards, quote marks included. This game stores each choice as a number: 1 means Yes, and 0 means No. The Ifs turn those numbers into Boolean Globals.
Title Director needs this Logic. It uses Game Start, which runs once for the whole game. Returning to Title later won’t load an older copy over the stars you earned during this run.
Nothing has been saved yet. The other half comes next.
10.4.4 Keep The Stars
A win belongs in the running table before it goes into storage. Keep The Stars waits for starsCounted, then gathers the stages of that place into packed. The dials turn the other way: each star count takes its weighted place in the number.
When a stage is won, keeps its place's stars on the device (one number per place), so they survive closing the game.
Any Object- Name
place- Value
0
- Name
packed- Value
0
- Name
stageRow- Value
0
- Variable
place- Value
floor(($stage - 1) / $stagesPerPlace)
- Variable
packed- Value
0
$stagesPerPlacetimes- Variable
stageRow- Value
place * $stagesPerPlace + local.index
- Variable
packed- Operator
+=- Value
data("Stages", stageRow, "stars") * pow(4, local.index)
- Action Name
starsKept- Key
data("Places", place, "starsKey")- Value
packed
Save Persistent Value puts Value away under Key. Here the Key comes from the same Places cell that the load reads. The Repeat finishes before the Save runs, so the kept number holds the whole place.
Attach Keep The Stars to Game Rules now. Its three Declare Variables belong to this one starsCounted Event. They need no Data Structure.
10.4.5 Win The Stage, Changed
The stars are counted, and the best result stays in Stages. Now Win The Stage announces that the record is ready. That message gives the Save its turn.
Finding the called note wins the stage: the game is Stage Won, and the stage earns stars for listening. The Stages table keeps the best stars of each stage.
Any Object- Game State
Playing
- Input Context
Field
- Input Context
Card- Active
No
- Input Context
Menus- Active
No
- Game State
Stage Won
$wrongNotes == 0- Variable
$starsWon- Value
3
$wrongNotes <= @self.twoStarWrongNotes- Variable
$starsWon- Value
2
- Variable
$starsWon- Value
1
$starsWon > data("Stages", $stage - 1, "stars")- Table
Stages- Row
$stage - 1- Column
"stars"- Value
$starsWon
- Custom Event
starsCounted
Put Trigger Custom Event on Object after the best-stars If, on the Event’s main line. It sends starsCounted to Game Rules itself. Keep it outside that If: replaying with fewer stars still saves the best result already in the table.
Run from Title, win the meadow’s first stage, and return to its map. Note the stars. Stop the game and start it again from Title. The stars return. The next stage should be open again, ready for you to find another call.
Your map has a memory now.
Keep testing at the same address in the same browser, so you’re returning to the same stored choices. An editor run and a webpage can have separate storage. Try each against its own earlier run, rather than expecting a download to bring the browser’s stars along. The project contains the game; the player keeps their progress on the device.
10.5 Quiet Sound and Calmer Motion
The two waiting icons can finally answer a click.
10.5.1 Show The Choice
Each icon has two frames. The changed picture should follow the choice the game actually keeps. Both buttons share this Function.
Shows a choice on its button's icon: frame 1 when the choice is on, frame 0 when it is off.
isOn == true- Applies to
button- Frame
1
- Applies to
button- Frame
0
Make button as an Object Parameter and isOn as a Boolean Parameter. This Function acts on the button it receives, rather than every button of that type.
10.5.2 Turn The Sound On And Off
Click Sound, and the whole game grows quiet. Click it again, and sound returns. The icon follows $soundOff, including the choice loaded before the title appeared.
The Sound button: turns all sound off or on; its icon shows which (frame 1 is off), and the choice is kept on the device.
Any Object- Custom Event
showChoice
$soundOff == true- Variable
$soundOff- Value
false
- Action Name
keptNo- Key
"soundOff"- Value
0
- Variable
$soundOff- Value
true
- Action Name
keptYes- Key
"soundOff"- Value
1
- Sound
Sound Click
- Custom Event
showChoice
- isOn
$soundOff- button
self
$soundOff == true- Volume
0
- Volume
1
Create asks showChoice to draw the starting icon. A click changes the Global and saves it; Variable Changed asks showChoice to draw again. The same Event also sets the volume for the whole game. Leave Set Volume’s bus at its default, Master, as the card does.
Attach this Logic to Sound Button. Keep its UI Interaction, which makes the icon respond to a click.
Window / UIUIX 96, Y 102-1Sound Icon ArtX 96, Y 102Its existing title and map placements share the new Logic. Place it in Field too. You don’t need a separate sound choice for each Scene.
10.5.3 Turn Calm Motion On And Off
Motion keeps the same kind of choice. Click it, and $calmMotion changes. Its icon shows the choice, and its kept number waits for the next time the game opens.
The Motion button: turns calm motion on or off (no slow motion, camera shake, bobbing or flying bits); its icon shows which (frame 1 is calm), and the choice is kept on the device.
Any Object- Custom Event
showChoice
$calmMotion == true- Variable
$calmMotion- Value
false
- Action Name
keptNo- Key
"calmMotion"- Value
0
- Variable
$calmMotion- Value
true
- Action Name
keptYes- Key
"calmMotion"- Value
1
- Sound
Sound Click
- Custom Event
showChoice
- isOn
$calmMotion- button
self
Window / UIUIX 96, Y 102-1Motion Icon ArtX 96, Y 102Attach the Logic to Motion Button and keep its UI Interaction. Add its Field placement. Run from Title now and click both icons, then restart the game. Their changed pictures should return. Sound should follow its icon already; the Field’s motion changes come next.
10.5.4 Note State and Bob While Waiting, Changed
A singing note should stand still when calm motion is chosen. Stopping its timer leaves a swing unfinished. Bob While Waiting stops the swing too, then sets the note back at homeY.
What one singing note keeps: where it swings and which way it swings now, its row of the Notes table, its turn in the opening, and the pitch it sings.
Add startDelay to Note State first. It keeps the random starting pause, so turning motion back on can use the same pause again.
Moves this singing note gently up and down around where it was placed, each note starting at its own moment.
Any Objectself.noteState- Component Value
self.noteState.homeY- Value
self.transform.position.y
- Action Name
startOffset- Minimum
0- Maximum
1
- Timer Name
"bob"- Repeat Every (s)
@self.secondsPerSwing- First Delay (s)
startOffset.value * @self.secondsPerSwing
- Component Value
self.noteState.startDelay- Value
startOffset.value * @self.secondsPerSwing
$calmMotion == false- Timer Name
"bob"- Repeat Every (s)
@self.secondsPerSwing- First Delay (s)
self.noteState.startDelay
- Value
self.transform.position.y- Target
self.noteState.homeY + @self.bobMeters * self.noteState.swingDirection- Duration
@self.secondsPerSwing- Ease
Quad in Out
- Component Value
self.noteState.swingDirection- Value
-self.noteState.swingDirection
$calmMotion == true- Timer Name
"bob"
- Which
Value- Value
self.transform.position.y
- X
self.transform.position.x- Y
self.noteState.homeY
- Timer Name
"bob"- Repeat Every (s)
@self.secondsPerSwing- First Delay (s)
self.noteState.startDelay
Replace Create’s old Start Recurring Timer with the new assignment and If. Add the timer inside that If. The new Variable Changed Event watches calmMotion, as the button’s does. Control Tween, with Which on Value and self.transform.position.y in its Value box, ends the motion already underway; Set Position restores the note’s resting height.
The Logic still brings Note State to Singing Note. The changed Data Structure brings its new field along, ready to hold the pause.
10.5.5 Point To The Call, Changed
The hint keeps pointing higher or lower. Calm motion only asks it to stop bobbing. A changed choice brings the arrow back to baseY, ready to be read.
When a hint is wanted, shows this arrow beside the wrong note the shot reached, pointing up or down towards the called note, bobbing gently, for a few seconds.
Any Objectself.pointToTheCallData- Visible
No
- Name
noteRow- Value
0
- Name
noteX- Value
0
- Name
noteY- Value
0
data("Stages", $stage - 1, "noteCount")times- Variable
noteRow- Value
data("Stages", $stage - 1, "firstNote") + local.index
data("Notes", noteRow, "midi") == $aimedPitch- Variable
noteX- Value
data("Notes", noteRow, "x")
- Variable
noteY- Value
data("Notes", noteRow, "y")
$calledPitch > $aimedPitch- Degrees
0
- Component Value
self.pointToTheCallData.baseY- Value
noteY + @self.aboveMeters
- Degrees
180
- Component Value
self.pointToTheCallData.baseY- Value
noteY - @self.aboveMeters
- X
noteX - @self.besideMeters- Y
self.pointToTheCallData.baseY
- Visible
Yes
- Timer Name
"bob"- Repeat Every (s)
@self.secondsPerSwing- First Delay (s)
0
- Timer Name
"hideHint"- Repeat Every (s)
@self.showSeconds- Ring Once
Yes
- Value
self.transform.position.y- Target
self.pointToTheCallData.baseY + @self.bobMeters * self.pointToTheCallData.swingDirection- Duration
@self.secondsPerSwing- Ease
Quad in Out
- Component Value
self.pointToTheCallData.swingDirection- Value
-self.pointToTheCallData.swingDirection
$calmMotion == false- Value
self.transform.position.y- Target
self.pointToTheCallData.baseY + @self.bobMeters * self.pointToTheCallData.swingDirection- Duration
@self.secondsPerSwing- Ease
Quad in Out
- Component Value
self.pointToTheCallData.swingDirection- Value
-self.pointToTheCallData.swingDirection
$calmMotion == true- Which
Value- Value
self.transform.position.y
- X
self.transform.position.x- Y
self.pointToTheCallData.baseY
- Timer Name
"bob"
- Visible
No
Rebuild On the Bob Timer inside the new If, keeping its old Tween and direction change there. Add the Variable Changed Event too. The timer can keep ringing while calm motion is on; the If leaves each ring with no swing to start.
10.5.6 Slow The Moment, Changed
A found note still wins. With calm motion on, the tower falls at its ordinary pace, and the camera stays still.
When the called note is found, slows the world for a moment and gives the camera a small shake.
Any Object- Speed
@self.slowPace- Ease Seconds
@self.easeSeconds- Hold Seconds
@self.slowSeconds
- Intensity
@self.shakeStrength- Seconds
@self.shakeSeconds
$calmMotion == false- Speed
@self.slowPace- Ease Seconds
@self.easeSeconds- Hold Seconds
@self.slowSeconds
- Intensity
@self.shakeStrength- Seconds
@self.shakeSeconds
The new If holds both old Actions. Rebuild both inside Then. The Event’s main line now has only that If.
10.5.7 Fall With A Knock, Changed
Wood and stone keep their knocks, but their splinters and dust wait when calm motion is chosen. The falling blocks still land and leave the Field as before.
Only On Landed changes. You can skim the unchanged collision Events and On Shatter. The two particle conditions are the places to inspect.
Once a block has come loose: it knocks when it lands (wood, stone or ice), throws splinters or dust when it lands hard, and a block of ice shatters when it is hit or lands hard while a tower is coming down. A block that falls out of the field is removed. It reads kind and isFalling (its Block State), set by Place A Block (kind, when it creates the block) and Come Down When Struck (isFalling), on the same block.
Any Objectself.blockStateself.fallWithAKnockDataCollider- Component Value
self.fallWithAKnockData.material- Value
data("Block Kinds", self.blockState.kind, "material")
- Event
Every Step- Mode
Falling
- Component Value
self.fallWithAKnockData.lastSpeed- Value
self.fallWithAKnockData.speed
- Component Value
self.fallWithAKnockData.speed- Value
hypot(self.transform.velocity.x, self.transform.velocity.y)
self.fallWithAKnockData.lastSpeed > @self.landingSpeed && (self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.landingDrop- Custom Event
landed
self.transform.position.y < @self.fieldBottom || self.transform.position.x < @self.fieldLeft || self.transform.position.x > @self.fieldRightself.fallWithAKnockData.material == "ice" && $towersFalling == true && self.fallWithAKnockData.lastSpeed > @self.iceLandingSpeed && (self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.iceLandingDrop- Custom Event
shatter
- Variable
$knockMaterial- Value
self.fallWithAKnockData.material
- Custom Event
blockKnocked
(self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.hardDrop && self.fallWithAKnockData.material == "wood" && $calmMotion == falsecompare(self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.hardDrop && self.fallWithAKnockData.material == "wood"- Particles
Wood Splinters- Position Relative To
World- X
self.transform.position.x- Y
self.transform.position.y- Z Index Offset
@self.effectLayer
(self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.hardDrop && self.fallWithAKnockData.material == "stone" && $calmMotion == falsecompare(self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.hardDrop && self.fallWithAKnockData.material == "stone"- Particles
Stone Dust- Position Relative To
World- X
self.transform.position.x- Y
self.transform.position.y- Z Index Offset
@self.effectLayer
- Name
otherSpeed- Value
0
- Variable
otherSpeed- Value
hypot(other.transform.velocity.x, other.transform.velocity.y)
self.fallWithAKnockData.material == "ice" && self.blockState.isFalling == true && $towersFalling == true && otherSpeed > @self.iceShotSpeed- Custom Event
shatter
- Name
otherSpeed- Value
0
- Variable
otherSpeed- Value
hypot(other.transform.velocity.x, other.transform.velocity.y)
self.fallWithAKnockData.material == "ice" && self.blockState.isFalling == true && $towersFalling == true && otherSpeed > @self.iceKnockSpeed- Custom Event
shatter
- Particles
Ice Shatter- Position Relative To
World- X
self.transform.position.x- Y
self.transform.position.y- Z Index Offset
@self.effectLayer
- Variable
$knockMaterial- Value
'ice'
- Custom Event
blockKnocked
Add the calmMotion comparison to each Condition box, using the new green condition as a whole. The old condition printed beside it is the one being replaced.
Ice already learned to shatter in Chapter 6. Keep On Shatter as it stands. Ice shards, the golden note burst and landing dust still show in calm motion; the choice quiets the wood and stone effects.
Run a stage and click Motion while the notes are bobbing. They settle at their resting heights. Click again. After their starting pauses, the notes begin swinging from home once more. The game still listens either way.
10.6 A Welcome That Knows When to Leave
The first stage has a little card waiting for a new listener.
10.6.1 Messages, Changed
How to play uses the game’s own words. Its instructions have line breaks, which a single-line table cell won’t let you type. Download the finished Messages table, then import it through Messages’ Import CSV. The import keeps the existing rows and adds howToPlay, with its line breaks inside the words cell. Confirm with Import CSV if Myth Engine asks.
Low to high…Listen…Missed! Listen again…Not that one. Listen again…Listen: the note you want is higher.Listen: the note you want is lower.Listen. The notes sing from low to high, [new line] then one of them is called. [new line] Pull the happy note back and let go. [new line] Hit the singing note that matches: [new line] higher sounds sit higher up, lower sounds lower down.10.6.2 Show How To Play
The card has a panel, some words, and Got it. They all ask the same question when created: has this device seen the welcome already? If so, they stay hidden.
A part of the How to play card: shown only the very first time a stage is played on this device, until "Got it".
Any Object$howToPlaySeen == true- Visible
No
- Visible
Yes
- Visible
No
All three parts hear howToPlayClosed. The message hides them together, so nothing is left covering the meadow.
10.6.3 Close How To Play
While the card waits, Enter belongs to the welcome. The happy note waits. Close How To Play hands the keys to Menus until Got it is chosen.
The Got it button (and Enter or Space): closes the How to play card for good on this device, and the stage begins.
Any Object$howToPlaySeen == false- Input Context
Field- Active
No
- Input Context
Menus
- Custom Event
gotIt
- Custom Event
gotIt
$howToPlaySeen == false- Sound
Sound Click
- Variable
$howToPlaySeen- Value
true
- Action Name
howToPlayKept- Key
"howToPlaySeen"- Value
1
- Input Context
Menus- Active
No
- Input Context
Field
- Custom Event
howToPlayClosed
A click and the Open Command meet in gotIt. Its If keeps a later press from closing the card twice. Once the choice is kept, it gives the keys back to Field and tells the notes to begin.
10.6.4 The Welcome’s Objects
The panel holds the heading, with the instructions as a separate text Object. Got It Button sits below them, ready for a click or the Open Command.
Window / UIUIX 1040, Y 566-1How To Play Panel ArtX 1040, Y 566How to playNunito54Extra Bold#EC4A33CenterVectorX 0, Y 205Window / UIUIX 960, Y 250{data("Messages", "howToPlay", "words")}Nunito31Extra Bold#3A1D0BCenterVectorWindow / UIUIX 300, Y 106-1Red Button ArtX 300, Y 106Got itNunito40Extra BoldCenterVectorX 0, Y 3Make all three as Screen UI Objects. Keep UI Interaction only on Got It Button. Remove it from the panel and text; the instructions shouldn’t behave like buttons. Attach their Logic, then place them in Field from Set up. Select each placement in the Scene editor’s Scene Explorer. Scroll down its Properties to Transform, and find Screen Anchor above Screen offset (px). Choose the top-left dot, then enter the offsets. Its Object stays unchanged.
10.6.5 Sing In The Opening, Changed
The notes wait while the welcome is showing. After Got it, they sing their familiar opening. On later visits, when the welcome stays hidden, they begin without waiting for it.
Before the first call, the singing notes sing once from the lowest to the highest. Each note sings its own turn and swells while it sings. It reads noteRow and turn (its Note State), set by Build The Stage, when it creates the note.
Any Objectself.noteState- Component Value
self.noteState.pitch- Value
data("Notes", self.noteState.noteRow, "midi")
- Timer Name
"singTurn"- Repeat Every (s)
@self.openingStartSeconds + self.noteState.turn * @self.secondsBetweenNotes- Ring Once
Yes
$howToPlaySeen == true- Timer Name
"singTurn"- Repeat Every (s)
@self.openingStartSeconds + self.noteState.turn * @self.secondsBetweenNotes- Ring Once
Yes
- Timer Name
"singTurn"- Repeat Every (s)
@self.afterHowToPlaySeconds + self.noteState.turn * @self.secondsBetweenNotes- Ring Once
Yes
self.noteState.turn == 0- Variable
$message- Value
'lowToHigh'
self.noteState.turn == (data("Stages", $stage - 1, "noteCount") - 1)- Timer Name
"openingDone"- Repeat Every (s)
@self.pauseAfterOpeningSeconds- Ring Once
Yes
- pitch
self.noteState.pitch
- Value
self.transform.scale- Target
@self.swellScale- Duration
@self.swellSeconds / 2- Ease
Quad Out
- Timer Name
"swellFull"- Repeat Every (s)
@self.swellSeconds / 2- Ring Once
Yes
- Value
self.transform.scale- Target
1- Duration
@self.swellSeconds / 2- Ease
Quad in
- Custom Event
openingFinished
Rebuild On Stage Built’s timer inside the new If. Add On How to Play Closed as its own Custom Event. It starts the same singTurn timer after afterHowToPlaySeconds, a short 0.3-second pause for the card to leave.
To test the first welcome again from Myth, save your project first. Temporarily use "welcome-check-new" as Key in two Actions: Read Back What Was Kept’s keptHowToPlay Load, and Close How To Play’s howToPlayKept Save. Use a test key you haven’t used before. Keep both keys identical, with their quote marks, and run from Title. The welcome appears before the opening. Close it with Got it or Enter, and listen for the opening. Restart from Title: it should stay out of the way now. Stop the game. Restore both Keys to "howToPlaySeen" before saving, so later games read your ordinary welcome choice again.
There is room for the music.
10.7 The Castle’s Last Song
The last stage has no next stage waiting behind it. It needs a different card.
10.7.1 Show With The Finale
When you find the castle’s last called note, its ordinary win card appears first. Then the farewell takes its place. Show With The Finale keeps its panel and stars hidden until finaleShown gives them their turn.
A part of the finale: hidden until the last stage is won.
Any Object- Visible
No
- Visible
Yes
10.7.2 Finale Stars and Count All The Stars
The final words count every star you’ve earned, across every place. Finale Stars holds that total for the text to read. Make its Data Structure first.
The stars earned in the whole game, for the finale's text.
Counts the stars of every stage (the Stages table) for the finale: the text reads starsInAll.
Any Objectself.finaleStars- Component Value
self.finaleStars.starsInAll- Value
0
dataCount("Stages")times- Component Value
self.finaleStars.starsInAll- Operator
+=- Value
data("Stages", local.index, "stars")
Count All The Stars visits every row in Stages when finaleShown arrives. Kept stars count too. A return visit can finish with all the stars you’ve kept.
10.7.3 The Finale’s Objects
The panel says Every stage sang! Above it, three lit stars stand together. The line below counts your stars, and Map takes you back to the castle’s stages.
Window / UIUIX 820, Y 426-1Card Panel ArtX 820, Y 426Every stage sang!Nunito54Extra Bold#EC4A33CenterVectorX 0, Y 162Window / UIUIX 740, Y 50You have {starsInAll} of {dataCount("Stages") * 3} stars.Nunito34Extra Bold#3A1D0BCenterVectorCount All The StarsFinale StarsWindow / UIUIX 300, Y 106-1Red Button ArtX 300, Y 106MapNunito40Extra BoldCenterVectorX 0, Y 3Window / UIUIX 96, Y 96-1Star Art1X 96, Y 96Make these as Screen UI Objects too. Keep UI Interaction only on Finale Button. Finale Message gets Finale Stars from Count All The Stars when you attach it. Its Text template reads starsInAll through the short field name.
Finale Star uses Frame 1, the lit star, in its Sprite’s Appearance. Place this Object three times. Select each finale placement in Field’s Scene Explorer, as for the welcome. In Properties > Transform > Screen Anchor, choose the top-left dot. Enter its offsets next.
10.7.4 Sing The Finale
The castle’s notes sing a quick run, low to high. Then they sound together. Sing The Finale uses the piano Function from Chapter 2, with a timer to give each note its turn.
Object Data of Logic "Sing The Finale" (its state that lasts between Events).
After the last stage: every note of that stage sings once from low to high, then all of them together.
Any Objectself.singTheFinaleData- Component Value
self.singTheFinaleData.turn- Value
0
- Component Value
self.singTheFinaleData.firstNote- Value
data("Stages", $stage - 1, "firstNote")
- Timer Name
"finaleNote"- Repeat Every (s)
@self.secondsBetweenNotes- First Delay (s)
@self.firstNoteSeconds
self.singTheFinaleData.turn < data("Stages", $stage - 1, "noteCount")- pitch
data("Notes", self.singTheFinaleData.firstNote + self.singTheFinaleData.turn, "midi")
- Component Value
self.singTheFinaleData.turn- Operator
+=- Value
1
data("Stages", $stage - 1, "noteCount")times- pitch
data("Notes", self.singTheFinaleData.firstNote + local.index, "midi")
- Timer Name
"finaleNote"
Make Sing The Finale Data before adding it to the Logic. turn and firstNote stay between timer rings, so they belong there. After the run, the Repeat plays all the notes in one ring, and the timer stops.
10.7.5 The Old Card Makes Room
The next-stage line has nowhere to send you after the castle. The farewell needs that space for its total and Map button. The old card leaves. Add On Finale Shown to Show With The Card and Show The Next Stage. Each Event hides its own Object.
A part of the card that shows when a stage is won: hidden until then.
Any Object- Visible
No
- Visible
Yes
- Visible
No
The card's last line, the number and name of the next stage: shown with the card when there is a next stage.
Any Object- Visible
No
- Visible
No
$stage < dataCount("Stages")- Visible
Yes
The existing Objects already carry those Logic Components. Their new Events go with them, including the stars and the filling bar. Their placements stay.
10.7.6 Go On To The Next Stage, Changed
The win card still waits and fills its bar. Now goOn checks for another stage before sending the game back to its opening. If there is, it restarts with the next stage number. The final stage sends finaleShown.
From the card, the next stage starts: by itself when the card has had its time, or at once with Enter or a click. After the last stage the finale comes instead.
Any Object- Custom Event
goOn
- Custom Event
goOn
- Custom Event
goOn
- Logic Component
Go On To The Next Stage- State
Disable
- Variable
$stage- Operator
+=- Value
1
$stage >= dataCount("Stages")- Custom Event
finaleShown
- Variable
$stage- Operator
+=- Value
1
Keep Set Logic State first. Replace the old increment and Restart Scene with the new If. Otherwise gets those two Actions. The last stage stays selected while its finale sings.
Sound The FallSound The Fall DataRepeat The CallRepeat The Call DataMake Way For The NotesMake Way For The Notes DataSing The FinaleSing The Finale DataSinging NoteKeep The Stars is already attached. Attach Sing The Finale now; its Data Structure comes with it. Keep singingNote set to Singing Note under Variables.
Try a stage from Title, and find its call as you did before. Its win card should still lead onward. For the last-stage test, save your project and set the Global stage’s starting value to 120. In Game Settings > General > Startup, temporarily choose Field as Start Scene, then run the game. Close How to play if it appears, then find the call. The win card gives way to the finale. Stop the game afterwards. Restore stage’s starting value to 1 and Start Scene to Title before saving again.
Enter leaves the finale showing. Click Map, or press Escape, to return to the stage map.
Watch what stays after a restart, and what changes at the last stage.
- Win a stage, note its stars, close the game, and reopen it at the same address. The stars and the next open stage return.
- Replay with fewer earned stars. After another restart, the map should still hold the better result you earned before.
- Click Sound, then restart. The icon keeps its choice, and the whole game follows it. Turn sound back on to hear the notes.
- Click Motion during a stage. Notes stop bobbing at home, and resume when you click again. A calm win has no camera shake or slow motion; wood and stone have no splinters or dust.
- On a device new to this game, How to play appears before the first opening. Got it or Enter closes it. The notes begin, and later visits skip the welcome.
- At the last stage, the win card leads to Every stage sang! The total reads your earned stars out of 360. Each note sings alone before the final chord brings the whole stage together. Enter leaves the finale visible; Map or Escape leaves it.
- On an upright phone, the page outside the game asks you to turn it sideways. Sideways, the game fills the view.
The running table keeps the best stars. A win puts a place’s record away, and Game Start brings it back. The buttons work the same way. Messages give the welcome and the finale their place in the game.
A stored choice needs both halves: somewhere to keep it, and a moment to read it back. Here the table and Globals remain the facts the game plays from. Storage waits beyond the window closing.
10.8 If It Didn’t Work
Watch the moment where the game forgets. Was the value written, or did it fail to come back?
Stars show until the game closes? Look at Win The Stage’s starsCounted message, outside its best-stars If. Keep The Stars needs that message. Restore the message, find the call again, and reopen the game from Title.
Every place returns the same stars? Compare Places’ starsKey cells. Each place needs its own key, and both the load and Save must read that cell. Fix the repeated key before trying another win.
The load returns no stars? Start the project from Title, with Read Back What Was Kept on Title Director. Running Field alone skips that Object. Start at Title.
An icon changes but its choice disappears next time? Check its Save’s Key against the matching Load in Read Back What Was Kept. Match the text exactly. Click the button once more, then restart to see whether its choice returns.
Calm notes stop halfway through a swing? Look at Bob While Waiting’s Variable Changed Event. Control Tween must name self.transform.position.y, followed by Set Position with homeY. That brings each note back to rest.
The welcome hides, but the notes stay silent? Check Close How To Play’s howToPlayClosed message and Sing In The Opening’s Event with that name. One sends the invitation; the other starts singTurn. Join them again.
The last win tries to restart an absent stage? Look at Go On To The Next Stage’s outer If. Then sends the finale message. Otherwise increments the stage and restarts Field. Put them back under the matching branches and try the last stage again.
The finale appears, but the old card covers it? Check On Finale Shown in Show With The Card and Show The Next Stage. Each must hide its Object. Run again, and the farewell should have the whole card to itself.
You can compare with the Goal’s project. Make one correction and play again, watching the part that went missing before.
What would you like a returning player to find? Change the welcome’s words in Messages, or give the finale a line of your own. You could arrange its stars into a different shape, or let its notes sing more slowly through secondsBetweenNotes. Keep a copy of Pitch Shot, then try the change you want to hear.
Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).
In Chapter 1, two golden notes waited in a silent meadow. Now your game remembers every place where you found the call, ready for another visit. What will you make with it next?
Free demo
Free
- No time limit
- Build, save and playtest your games
- Follow every game guide and build its game
Publishing needs the full version.
Try the free demoFull version
$84.99 USD, once
No subscription, no paid upgrades, every update included.
Everything in the free demo, plus:
- Publish your game as one HTML file, a web folder, or desktop and mobile projects you build and sign
- Share it or sell it, with no royalties, however much it earns
- Let players build their own version of your game, within the parts you allow (Builder Mode)
Windows 10/11, 64-bit · One person, on computers you own or control · Checkout and downloads through itch.io · Made by 3D Mage
License agreementMac, refunds and other questionsWhy pay for Myth Engine when free engines exist?
