Build a GameCreating Pitch Shot

10  Remembering, Settings and Finish

Goal

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.

Pitch Shot, Chapter 10 finishedZip of a Myth project · 2.8 MB

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

Set upWhat this chapter adds
ART (sprites, from the starter kit)
How To Play Panel Art
Red Button Art
GLOBAL VARIABLES
$soundOffBooleanstarts at false
$calmMotionBooleanstarts at false
$howToPlaySeenBooleanstarts at false
GAME SETTINGS > Display > Page Background (Outside the Game)
Color#1B3552
ImageChoose Image..., then the file
SCENE Field: PLACE THESE
ObjectXY
Sound Button1740-64
with Z-Index = 10010
Motion Button1860-64
with Z-Index = 10010
How To Play Panel960-520
with Z-Index = 10050
How To Play Text960-490
with Z-Index = 10060
Got It Button960-715
with Z-Index = 10060
Finale Panel960-370
with Z-Index = 10050
Finale Message960-400
with Z-Index = 10060
Finale Button960-480
with Z-Index = 10060
Finale Star850-316
with Z-Index = 10060
Finale Star960-302
with Z-Index = 10060
Finale Star1070-316
with Z-Index = 10060

Add 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.

Data StructurePlaceAddDelete

One place: its name, its backdrop, its tune, and the key its stars are kept under.

nameTextdefault (empty)
backdropThing Spritedefault None
tuneThing Sounddefault None
starsKeyTextdefault (empty)
Data TablePlacesData Structure: PlaceAddDelete
Row 0Place 1
nameSunny Meadow
backdropBackdrop Sunny Meadow
tuneMusic Sunny Meadow
starsKeystars-0
Row 1Place 2
nameSeaside Beach
backdropBackdrop Seaside Beach
tuneMusic Seaside Beach
starsKeystars-1
Row 2Place 3
nameAutumn Woods
backdropBackdrop Autumn Woods
tuneMusic Autumn Woods
starsKeystars-2
Row 3Place 4
nameRed Rock Canyon
backdropBackdrop Red Rock Canyon
tuneMusic Red Rock Canyon
starsKeystars-3
Row 4Place 5
nameCandy Hills
backdropBackdrop Candy Hills
tuneMusic Candy Hills
starsKeystars-4
Row 5Place 6
nameSnowy Peaks
backdropBackdrop Snowy Peaks
tuneMusic Snowy Peaks
starsKeystars-5
Row 6Place 7
nameJungle Falls
backdropBackdrop Jungle Falls
tuneMusic Jungle Falls
starsKeystars-6
Row 7Place 8
namePirate Cove
backdropBackdrop Pirate Cove
tuneMusic Pirate Cove
starsKeystars-7
Row 8Place 9
nameMushroom Grove
backdropBackdrop Mushroom Grove
tuneMusic Mushroom Grove
starsKeystars-8
Row 9Place 10
nameVolcano Isle
backdropBackdrop Volcano Isle
tuneMusic Volcano Isle
starsKeystars-9
Row 10Place 11
nameCloud Kingdom
backdropBackdrop Cloud Kingdom
tuneMusic Cloud Kingdom
starsKeystars-10
Row 11Place 12
nameMoonlit Castle
backdropBackdrop Moonlit Castle
tuneMusic Moonlit Castle
starsKeystars-11

Add 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.

FunctionUnpack A Place's Stars

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

PARAMETERS
NameTypeStarts at
placeNumber0
packedNumber0
ACTIONS
1Repeat$stagesPerPlacetimes
Each time
2Set Data Cell
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.

Logic ComponentRead Back What Was KeptAttach to: Title Director

When the game opens, reads back once what this device kept: the stars (into the Stages table) and whether How to play was seen.

Attach toAny Object
When the Game Starts
WhenGame Start
1RepeatdataCount("Places")times
Each time
2Load Persistent Value
Action Name
keptStars
Key
data("Places", local.index, "starsKey")
Default
0
3Unpack A Place's StarsFunction
place
local.index
packed
keptStars.value
4Load Persistent Value
Action Name
keptHowToPlay
Key
"howToPlaySeen"
Default
0
5IfkeptHowToPlay.value == 1
Then
6Change Variable
Variable
$howToPlaySeen
Value
true
7Otherwise
8Change Variable
Variable
$howToPlaySeen
Value
false
9Load Persistent Value
Action Name
keptSound
Key
"soundOff"
Default
0
10IfkeptSound.value == 1
Then
11Change Variable
Variable
$soundOff
Value
true
12Otherwise
13Change Variable
Variable
$soundOff
Value
false
14Load Persistent Value
Action Name
keptMotion
Key
"calmMotion"
Default
0
15IfkeptMotion.value == 1
Then
16Change Variable
Variable
$calmMotion
Value
true
17Otherwise
18Change Variable
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.

ObjectTitle DirectorAddDelete
Logic
Play The Title Tune
Read Back What Was Kept

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.

Logic ComponentKeep The StarsAttach to: Game Rules

When a stage is won, keeps its place's stars on the device (one number per place), so they survive closing the game.

Attach toAny Object
On Stars Counted
WhenstarsCountedCustom Event
1Declare Variable
Name
place
Value
0
2Declare Variable
Name
packed
Value
0
3Declare Variable
Name
stageRow
Value
0
4Change Variable
Variable
place
Value
floor(($stage - 1) / $stagesPerPlace)
5Change Variable
Variable
packed
Value
0
6Repeat$stagesPerPlacetimes
Each time
7Change Variable
Variable
stageRow
Value
place * $stagesPerPlace + local.index
8Change Variable
Variable
packed
Operator
+=
Value
data("Stages", stageRow, "stars") * pow(4, local.index)
9Save Persistent Value
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.

Logic ComponentWin The StageAttach to: Game RulesAddDelete

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.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
twoStarWrongNotesNumberConstant2
When the Scene Starts
WhenScene Start
1Set Game State
Game State
Playing
2Set Input Context Active
Input Context
Field
3Set Input Context Active
Input Context
Card
Active
No
4Set Input Context Active
Input Context
Menus
Active
No
On Call Found
WhencallFoundCustom Event
1Set Game State
Game State
Stage Won
2If$wrongNotes == 0
Then
3Change Variable
Variable
$starsWon
Value
3
4Otherwise
5If$wrongNotes <= @self.twoStarWrongNotes
Then
6Change Variable
Variable
$starsWon
Value
2
7Otherwise
8Change Variable
Variable
$starsWon
Value
1
9If$starsWon > data("Stages", $stage - 1, "stars")
Then
10Set Data Cell
Table
Stages
Row
$stage - 1
Column
"stars"
Value
$starsWon
11Trigger Custom Event on Object
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.

FunctionShow The Choice

Shows a choice on its button's icon: frame 1 when the choice is on, frame 0 when it is off.

PARAMETERS
NameTypeStarts at
buttonObject
isOnBooleanNo
ACTIONS
1IfisOn == true
Then
2Set Frame
Applies to
button
Frame
1
3Otherwise
4Set Frame
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.

Logic ComponentTurn The Sound On And OffAttach to: Sound Button

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.

Attach toAny Object
When Created
WhenCreate
1Trigger Custom Event on Object
Custom Event
showChoice
On Hud Activated
WhenUI Activated
1If$soundOff == true
Then
2Change Variable
Variable
$soundOff
Value
false
3Save Persistent Value
Action Name
keptNo
Key
"soundOff"
Value
0
4Otherwise
5Change Variable
Variable
$soundOff
Value
true
6Save Persistent Value
Action Name
keptYes
Key
"soundOff"
Value
1
7Control Sound
Sound
Sound Click
On Variable Changed · Sound Off
WhenVariable ChangedGlobal Variable: soundOff
1Trigger Custom Event on Object
Custom Event
showChoice
On Show Choice
WhenshowChoiceCustom Event
1Show The ChoiceFunction
isOn
$soundOff
button
self
2If$soundOff == true
Then
3Set Volume
Volume
0
4Otherwise
5Set Volume
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.

ObjectSound ButtonAddDelete
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 96, Y 102
Sprite
Z Index Within Object-1
SpriteSound Icon Art
Transform > SizeX 96, Y 102
UI Interaction(as it is added)
Logic
Turn The Sound On And Off

Its 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.

Logic ComponentTurn Calm Motion On And OffAttach to: Motion Button

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.

Attach toAny Object
When Created
WhenCreate
1Trigger Custom Event on Object
Custom Event
showChoice
On Hud Activated
WhenUI Activated
1If$calmMotion == true
Then
2Change Variable
Variable
$calmMotion
Value
false
3Save Persistent Value
Action Name
keptNo
Key
"calmMotion"
Value
0
4Otherwise
5Change Variable
Variable
$calmMotion
Value
true
6Save Persistent Value
Action Name
keptYes
Key
"calmMotion"
Value
1
7Control Sound
Sound
Sound Click
On Variable Changed · Calm Motion
WhenVariable ChangedGlobal Variable: calmMotion
1Trigger Custom Event on Object
Custom Event
showChoice
On Show Choice
WhenshowChoiceCustom Event
1Show The ChoiceFunction
isOn
$calmMotion
button
self
ObjectMotion ButtonAddDelete
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 96, Y 102
Sprite
Z Index Within Object-1
SpriteMotion Icon Art
Transform > SizeX 96, Y 102
UI Interaction(as it is added)
Logic
Turn Calm Motion On And Off

Attach 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.

Data StructureNote StateAddDelete

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.

homeYNumberdefault 0
swingDirectionNumberdefault 1
startDelayNumberdefault 0
noteRowNumberdefault 0
turnNumberdefault 0
pitchNumberdefault 60

Add startDelay to Note State first. It keeps the random starting pause, so turning motion back on can use the same pause again.

Logic ComponentBob While WaitingAttach to: Singing NoteAddDelete

Moves this singing note gently up and down around where it was placed, each note starting at its own moment.

Attach toAny Object
DATA STRUCTURES
Note Stateself.noteState
SHARED VARIABLES
NameTypeKindStarting Value
bobMetersNumberConstant0.03
secondsPerSwingNumberConstant1.43
When Created
WhenCreate
1Change Component Value
Component Value
self.noteState.homeY
Value
self.transform.position.y
2Generate Random Number
Action Name
startOffset
Minimum
0
Maximum
1
3Start Recurring Timer
Timer Name
"bob"
Repeat Every (s)
@self.secondsPerSwing
First Delay (s)
startOffset.value * @self.secondsPerSwing
3Change Component Value
Component Value
self.noteState.startDelay
Value
startOffset.value * @self.secondsPerSwing
4If$calmMotion == false
Then
5Start Recurring Timer
Timer Name
"bob"
Repeat Every (s)
@self.secondsPerSwing
First Delay (s)
self.noteState.startDelay
On the Bob Timer
WhenRecurring TimerTimer name: bob
1Tween
Value
self.transform.position.y
Target
self.noteState.homeY + @self.bobMeters * self.noteState.swingDirection
Duration
@self.secondsPerSwing
Ease
Quad in Out
2Change Component Value
Component Value
self.noteState.swingDirection
Value
-self.noteState.swingDirection
On Variable Changed · Calm Motion
WhenVariable ChangedGlobal Variable: calmMotion
1If$calmMotion == true
Then
2Stop Recurring Timer
Timer Name
"bob"
3Control Tween
Which
Value
Value
self.transform.position.y
4Set Position
X
self.transform.position.x
Y
self.noteState.homeY
5Otherwise
6Start Recurring Timer
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.

Logic ComponentPoint To The CallAttach to: Hint ArrowAddDelete

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.

Attach toAny Object
DATA STRUCTURES
Point To The Call Dataself.pointToTheCallData
SHARED VARIABLES
NameTypeKindStarting Value
besideMetersNumberConstant0.78
aboveMetersNumberConstant0.58
bobMetersNumberConstant0.08
secondsPerSwingNumberConstant0.52
showSecondsNumberConstant4.5
When Created
WhenCreate
1Set Visible
Visible
No
On Hint Wanted
WhenhintWantedCustom Event
1Declare Variable
Name
noteRow
Value
0
2Declare Variable
Name
noteX
Value
0
3Declare Variable
Name
noteY
Value
0
4Repeatdata("Stages", $stage - 1, "noteCount")times
Each time
5Change Variable
Variable
noteRow
Value
data("Stages", $stage - 1, "firstNote") + local.index
6Ifdata("Notes", noteRow, "midi") == $aimedPitch
Then
7Change Variable
Variable
noteX
Value
data("Notes", noteRow, "x")
8Change Variable
Variable
noteY
Value
data("Notes", noteRow, "y")
9If$calledPitch > $aimedPitch
Then
10Set Rotation
Degrees
0
11Change Component Value
Component Value
self.pointToTheCallData.baseY
Value
noteY + @self.aboveMeters
12Otherwise
13Set Rotation
Degrees
180
14Change Component Value
Component Value
self.pointToTheCallData.baseY
Value
noteY - @self.aboveMeters
15Set Position
X
noteX - @self.besideMeters
Y
self.pointToTheCallData.baseY
16Set Visible
Visible
Yes
17Start Recurring Timer
Timer Name
"bob"
Repeat Every (s)
@self.secondsPerSwing
First Delay (s)
0
18Start Recurring Timer
Timer Name
"hideHint"
Repeat Every (s)
@self.showSeconds
Ring Once
Yes
On the Bob Timer
WhenRecurring TimerTimer name: bob
1Tween
Value
self.transform.position.y
Target
self.pointToTheCallData.baseY + @self.bobMeters * self.pointToTheCallData.swingDirection
Duration
@self.secondsPerSwing
Ease
Quad in Out
2Change Component Value
Component Value
self.pointToTheCallData.swingDirection
Value
-self.pointToTheCallData.swingDirection
1If$calmMotion == false
Then
2Tween
Value
self.transform.position.y
Target
self.pointToTheCallData.baseY + @self.bobMeters * self.pointToTheCallData.swingDirection
Duration
@self.secondsPerSwing
Ease
Quad in Out
3Change Component Value
Component Value
self.pointToTheCallData.swingDirection
Value
-self.pointToTheCallData.swingDirection
On Variable Changed · Calm Motion
WhenVariable ChangedGlobal Variable: calmMotion
1If$calmMotion == true
Then
2Control Tween
Which
Value
Value
self.transform.position.y
3Set Position
X
self.transform.position.x
Y
self.pointToTheCallData.baseY
On the Hide Hint Timer
WhenRecurring TimerTimer name: hideHint
1Stop Recurring Timer
Timer Name
"bob"
2Set Visible
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.

Logic ComponentSlow The MomentAttach to: Game RulesAddDelete

When the called note is found, slows the world for a moment and gives the camera a small shake.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
slowPaceNumberConstant0.3
easeSecondsNumberConstant0.05
slowSecondsNumberConstant0.55
shakeStrengthNumberConstant0.008
shakeSecondsNumberConstant0.35
On Call Found
WhencallFoundCustom Event
1Set Time Flow
Speed
@self.slowPace
Ease Seconds
@self.easeSeconds
Hold Seconds
@self.slowSeconds
2Camera Shake
Intensity
@self.shakeStrength
Seconds
@self.shakeSeconds
1If$calmMotion == false
Then
2Set Time Flow
Speed
@self.slowPace
Ease Seconds
@self.easeSeconds
Hold Seconds
@self.slowSeconds
3Camera Shake
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.

Logic ComponentFall With A KnockAttach to: Wood Post, Wood Plank, Stone Cube, Stone Plank, Ice CubeAddDelete

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.

Attach toAny Object
DATA STRUCTURES
Block Stateself.blockState
Fall With A Knock Dataself.fallWithAKnockData
COMPONENTS
self.colliderCollider
SHARED VARIABLES
NameTypeKindStarting Value
landingSpeedNumberConstant2.4
landingDropNumberConstant2.2
hardDropNumberConstant4.2
iceLandingSpeedNumberConstant4.8
iceLandingDropNumberConstant3
iceShotSpeedNumberConstant3.3
iceKnockSpeedNumberConstant5.2
fieldBottomNumberConstant-3
fieldLeftNumberConstant-3
fieldRightNumberConstant22.2
effectLayerNumberConstant60
On Placed
WhenplacedCustom Event
1Change Component Value
Component Value
self.fallWithAKnockData.material
Value
data("Block Kinds", self.blockState.kind, "material")
On Came Loose
WhencameLooseCustom Event
1Switch Mode
Event
Every Step
Mode
Falling
Every Step
WhenStep
ModeStanding (Starting Mode)
(no Actions)
Every Step
WhenStep
ModeFalling
1Change Component Value
Component Value
self.fallWithAKnockData.lastSpeed
Value
self.fallWithAKnockData.speed
2Change Component Value
Component Value
self.fallWithAKnockData.speed
Value
hypot(self.transform.velocity.x, self.transform.velocity.y)
3Ifself.fallWithAKnockData.lastSpeed > @self.landingSpeed && (self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.landingDrop
Then
4Trigger Custom Event on Object
Custom Event
landed
5Ifself.transform.position.y < @self.fieldBottom || self.transform.position.x < @self.fieldLeft || self.transform.position.x > @self.fieldRight
Then
6Destroy Instance
On Landed
WhenlandedCustom Event
1Ifself.fallWithAKnockData.material == "ice" && $towersFalling == true && self.fallWithAKnockData.lastSpeed > @self.iceLandingSpeed && (self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.iceLandingDrop
Then
2Trigger Custom Event on Object
Custom Event
shatter
3Otherwise
4Change Variable
Variable
$knockMaterial
Value
self.fallWithAKnockData.material
5Trigger Custom Event
Custom Event
blockKnocked
6If(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"
Then
7Burst Particles
Particles
Wood Splinters
Position Relative To
World
X
self.transform.position.x
Y
self.transform.position.y
Z Index Offset
@self.effectLayer
8If(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"
Then
9Burst Particles
Particles
Stone Dust
Position Relative To
World
X
self.transform.position.x
Y
self.transform.position.y
Z Index Offset
@self.effectLayer
When Starting to Hit Happy Note
WhenCollision EnterObject: Happy Note
1Declare Variable
Name
otherSpeed
Value
0
2Change Variable
Variable
otherSpeed
Value
hypot(other.transform.velocity.x, other.transform.velocity.y)
3Ifself.fallWithAKnockData.material == "ice" && self.blockState.isFalling == true && $towersFalling == true && otherSpeed > @self.iceShotSpeed
Then
4Trigger Custom Event on Object
Custom Event
shatter
When Starting to Hit Anything Tagged Block
WhenCollision EnterObject: tag:Block
1Declare Variable
Name
otherSpeed
Value
0
2Change Variable
Variable
otherSpeed
Value
hypot(other.transform.velocity.x, other.transform.velocity.y)
3Ifself.fallWithAKnockData.material == "ice" && self.blockState.isFalling == true && $towersFalling == true && otherSpeed > @self.iceKnockSpeed
Then
4Trigger Custom Event on Object
Custom Event
shatter
On Shatter
WhenshatterCustom Event
1Burst Particles
Particles
Ice Shatter
Position Relative To
World
X
self.transform.position.x
Y
self.transform.position.y
Z Index Offset
@self.effectLayer
2Change Variable
Variable
$knockMaterial
Value
'ice'
3Trigger Custom Event
Custom Event
blockKnocked
4Destroy Instance

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.

Data TableMessagesData Structure: MessageAddDelete
Row 0lowToHigh
wordsLow to high…
Row 1listen
wordsListen…
Row 2missed
wordsMissed! Listen again…
Row 3notThatOne
wordsNot that one. Listen again…
Row 4hintHigher
wordsListen: the note you want is higher.
Row 5hintLower
wordsListen: the note you want is lower.
Row 6howToPlay
wordsListen. 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.

Logic ComponentShow How To PlayAttach to: How To Play Panel, How To Play Text, Got It Button

A part of the How to play card: shown only the very first time a stage is played on this device, until "Got it".

Attach toAny Object
When Created
WhenCreate
1If$howToPlaySeen == true
Then
2Set Visible
Visible
No
3Otherwise
4Set Visible
Visible
Yes
On How to Play Closed
WhenhowToPlayClosedCustom Event
1Set Visible
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.

Logic ComponentClose How To PlayAttach to: Got It Button

The Got it button (and Enter or Space): closes the How to play card for good on this device, and the stage begins.

Attach toAny Object
When Created
WhenCreate
1If$howToPlaySeen == false
Then
2Set Input Context Active
Input Context
Field
Active
No
3Set Input Context Active
Input Context
Menus
On Hud Activated
WhenUI Activated
1Trigger Custom Event on Object
Custom Event
gotIt
When Open Is Pressed
WhenCommand PressedCommand: Open
1Trigger Custom Event on Object
Custom Event
gotIt
On Got It
WhengotItCustom Event
1If$howToPlaySeen == false
Then
2Control Sound
Sound
Sound Click
3Change Variable
Variable
$howToPlaySeen
Value
true
4Save Persistent Value
Action Name
howToPlayKept
Key
"howToPlaySeen"
Value
1
5Set Input Context Active
Input Context
Menus
Active
No
6Set Input Context Active
Input Context
Field
7Trigger Custom Event
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.

ObjectHow To Play Panel
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 1040, Y 566
Sprite
Z Index Within Object-1
SpriteHow To Play Panel Art
Transform > SizeX 1040, Y 566
Text / UI
TextHow to play
FontNunito
Font Size54
EmphasisExtra Bold
Color#EC4A33
AlignmentCenter
RenderingVector
Transform > PositionX 0, Y 205
Logic
Show How To Play
ObjectHow To Play Text
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 960, Y 250
Text / UI
Text{data("Messages", "howToPlay", "words")}
FontNunito
Font Size31
EmphasisExtra Bold
Color#3A1D0B
AlignmentCenter
RenderingVector
Logic
Show How To Play
ObjectGot It Button
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 300, Y 106
Sprite
Z Index Within Object-1
SpriteRed Button Art
Transform > SizeX 300, Y 106
Text / UI
TextGot it
FontNunito
Font Size40
EmphasisExtra Bold
AlignmentCenter
RenderingVector
Transform > PositionX 0, Y 3
UI Interaction(as it is added)
Logic
Show How To Play
Close How To Play

Make 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.

Logic ComponentSing In The OpeningAttach to: Singing NoteAddDelete

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.

Attach toAny Object
DATA STRUCTURES
Note Stateself.noteState
SHARED VARIABLES
NameTypeKindStarting Value
openingStartSecondsNumberConstant0.6
afterHowToPlaySecondsNumberConstant0.3
secondsBetweenNotesNumberConstant0.52
swellScaleNumberConstant1.32
swellSecondsNumberConstant0.45
pauseAfterOpeningSecondsNumberConstant0.75
On Placed
WhenplacedCustom Event
1Change Component Value
Component Value
self.noteState.pitch
Value
data("Notes", self.noteState.noteRow, "midi")
On Stage Built
WhenstageBuiltCustom Event
1Start Recurring Timer
Timer Name
"singTurn"
Repeat Every (s)
@self.openingStartSeconds + self.noteState.turn * @self.secondsBetweenNotes
Ring Once
Yes
1If$howToPlaySeen == true
Then
2Start Recurring Timer
Timer Name
"singTurn"
Repeat Every (s)
@self.openingStartSeconds + self.noteState.turn * @self.secondsBetweenNotes
Ring Once
Yes
On How to Play Closed
WhenhowToPlayClosedCustom Event
1Start Recurring Timer
Timer Name
"singTurn"
Repeat Every (s)
@self.afterHowToPlaySeconds + self.noteState.turn * @self.secondsBetweenNotes
Ring Once
Yes
On the Sing Turn Timer
WhenRecurring TimerTimer name: singTurn
1Ifself.noteState.turn == 0
Then
2Change Variable
Variable
$message
Value
'lowToHigh'
3Ifself.noteState.turn == (data("Stages", $stage - 1, "noteCount") - 1)
Then
4Start Recurring Timer
Timer Name
"openingDone"
Repeat Every (s)
@self.pauseAfterOpeningSeconds
Ring Once
Yes
5Play Piano NoteFunction
pitch
self.noteState.pitch
6Tween
Value
self.transform.scale
Target
@self.swellScale
Duration
@self.swellSeconds / 2
Ease
Quad Out
7Start Recurring Timer
Timer Name
"swellFull"
Repeat Every (s)
@self.swellSeconds / 2
Ring Once
Yes
On the Swell Full Timer
WhenRecurring TimerTimer name: swellFull
1Tween
Value
self.transform.scale
Target
1
Duration
@self.swellSeconds / 2
Ease
Quad in
On the Opening Done Timer
WhenRecurring TimerTimer name: openingDone
1Trigger Custom Event
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.

Logic ComponentShow With The FinaleAttach to: Finale Panel, Finale Message, Finale Button, Finale Star

A part of the finale: hidden until the last stage is won.

Attach toAny Object
When Created
WhenCreate
1Set Visible
Visible
No
On Finale Shown
WhenfinaleShownCustom Event
1Set Visible
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.

Data StructureFinale Stars

The stars earned in the whole game, for the finale's text.

starsInAllNumberdefault 0
Logic ComponentCount All The StarsAttach to: Finale Message

Counts the stars of every stage (the Stages table) for the finale: the text reads starsInAll.

Attach toAny Object
DATA STRUCTURES
Finale Starsself.finaleStars
On Finale Shown
WhenfinaleShownCustom Event
1Change Component Value
Component Value
self.finaleStars.starsInAll
Value
0
2RepeatdataCount("Stages")times
Each time
3Change Component Value
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.

ObjectFinale Panel
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 820, Y 426
Sprite
Z Index Within Object-1
SpriteCard Panel Art
Transform > SizeX 820, Y 426
Text / UI
TextEvery stage sang!
FontNunito
Font Size54
EmphasisExtra Bold
Color#EC4A33
AlignmentCenter
RenderingVector
Transform > PositionX 0, Y 162
Logic
Show With The Finale
ObjectFinale Message
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 740, Y 50
Text / UI
TextYou have {starsInAll} of {dataCount("Stages") * 3} stars.
FontNunito
Font Size34
EmphasisExtra Bold
Color#3A1D0B
AlignmentCenter
RenderingVector
Data Structure
From LogicCount All The Stars
Data StructureFinale Stars
Logic
Show With The Finale
Count All The Stars
ObjectFinale Button
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 300, Y 106
Sprite
Z Index Within Object-1
SpriteRed Button Art
Transform > SizeX 300, Y 106
Text / UI
TextMap
FontNunito
Font Size40
EmphasisExtra Bold
AlignmentCenter
RenderingVector
Transform > PositionX 0, Y 3
UI Interaction(as it is added)
Logic
Show With The Finale
Go Back
ObjectFinale Star
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 96, Y 96
Sprite
Z Index Within Object-1
SpriteStar Art
Frame1
Transform > SizeX 96, Y 96
Logic
Show With The Finale

Make 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.

Data StructureSing The Finale Data

Object Data of Logic "Sing The Finale" (its state that lasts between Events).

turnNumberdefault 0
firstNoteNumberdefault 0
Logic ComponentSing The FinaleAttach to: Game Rules

After the last stage: every note of that stage sings once from low to high, then all of them together.

Attach toAny Object
DATA STRUCTURES
Sing The Finale Dataself.singTheFinaleData
SHARED VARIABLES
NameTypeKindStarting Value
secondsBetweenNotesNumberConstant0.16
firstNoteSecondsNumberConstant0.1
On Finale Shown
WhenfinaleShownCustom Event
1Change Component Value
Component Value
self.singTheFinaleData.turn
Value
0
2Change Component Value
Component Value
self.singTheFinaleData.firstNote
Value
data("Stages", $stage - 1, "firstNote")
3Start Recurring Timer
Timer Name
"finaleNote"
Repeat Every (s)
@self.secondsBetweenNotes
First Delay (s)
@self.firstNoteSeconds
On the Finale Note Timer
WhenRecurring TimerTimer name: finaleNote
1Ifself.singTheFinaleData.turn < data("Stages", $stage - 1, "noteCount")
Then
2Play Piano NoteFunction
pitch
data("Notes", self.singTheFinaleData.firstNote + self.singTheFinaleData.turn, "midi")
3Change Component Value
Component Value
self.singTheFinaleData.turn
Operator
+=
Value
1
4Otherwise
5Repeatdata("Stages", $stage - 1, "noteCount")times
Each time
6Play Piano NoteFunction
pitch
data("Notes", self.singTheFinaleData.firstNote + local.index, "midi")
7Stop Recurring Timer
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.

Logic ComponentShow With The CardAttach to: Card Panel, Card Message, Card Star, Card Bar Track, Card BarAddDelete

A part of the card that shows when a stage is won: hidden until then.

Attach toAny Object
When Created
WhenCreate
1Set Visible
Visible
No
On Card Shown
WhencardShownCustom Event
1Set Visible
Visible
Yes
On Finale Shown
WhenfinaleShownCustom Event
1Set Visible
Visible
No
Logic ComponentShow The Next StageAttach to: Card NextAddDelete

The card's last line, the number and name of the next stage: shown with the card when there is a next stage.

Attach toAny Object
When Created
WhenCreate
1Set Visible
Visible
No
On Finale Shown
WhenfinaleShownCustom Event
1Set Visible
Visible
No
On Card Shown
WhencardShownCustom Event
1If$stage < dataCount("Stages")
Then
2Set Visible
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.

Logic ComponentGo On To The Next StageAttach to: Game RulesAddDelete

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.

Attach toAny Object
On Card Time Up
WhencardTimeUpCustom Event
1Trigger Custom Event on Object
Custom Event
goOn
On Go on Wanted
WhengoOnWantedCustom Event
1Trigger Custom Event on Object
Custom Event
goOn
When Goon Is Pressed
WhenCommand PressedCommand: Go On
1Trigger Custom Event on Object
Custom Event
goOn
On Go on
WhengoOnCustom Event
1Set Logic State
Logic Component
Go On To The Next Stage
State
Disable
2Change Variable
Variable
$stage
Operator
+=
Value
1
3Restart Scene
2If$stage >= dataCount("Stages")
Then
3Trigger Custom Event
Custom Event
finaleShown
4Otherwise
5Change Variable
Variable
$stage
Operator
+=
Value
1
6Restart Scene

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.

ObjectGame RulesAddDelete
Data Structure
From LogicSound The Fall
Data StructureSound The Fall Data
Data Structure
From LogicRepeat The Call
Data StructureRepeat The Call Data
Data Structure
From LogicMake Way For The Notes
Data StructureMake Way For The Notes Data
Data Structure
From LogicSing The Finale
Data StructureSing The Finale Data
Logic
Build The Stage
Sound The Fall
Slow The Moment
Watch The Towers Fall
Call A Note
Repeat The Call
Play The Place's Tune
Make Way For The Notes
Count The Shots
Ask Again
Win The Stage
Bring Up The Card
Go On To The Next Stage
Keep The Stars
Sing The Finale
Shared Variables
singingNoteSinging Note

Keep 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.

Play it!

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.
How it works

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.

Challenge

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 demo

Full 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)
Buy Myth Engine — $84.99

Windows 10/11, 64-bit · One person, on computers you own or control · Checkout and downloads through itch.io · Made by 3D Mage