Build a GameCreating Crystal Cascade

6  Game Over and Results

Goal

Let the well fill, watch the stack turn to stone, and read the record of your game. Then choose PLAY AGAIN for a fresh well.

The game as it is at the end of this chapter. Click the game to play.

If your game and this chapter part ways, open this chapter’s finished project to compare. You can also start Chapter 7 from it.

Crystal Cascade, Chapter 6 finishedZip of a Myth project · 4.6 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.

6.1 The Idea

The stack reaches the rim. A new piece has nowhere to stand, and the bright crystals begin turning to gray stone from the floor upward. The well has been lost.

For a moment, let that change be seen. Then a glass panel opens over a dark veil. It keeps the score, lines and level, but also the small record of that game: its time, cascades, twists and best combo.

PLAY AGAIN waits underneath. Press Enter, and the well is empty once more. Your saved best stays on the device, while the new score starts fresh.

The hard part is the boundary between those games. A Global can survive restarting a Scene. A count that should start over needs an owner that resets it. We’ll give each part that responsibility rather than making one long reset routine.

The ending has a boundary too. Counting must stop when play ends, before the results arrive. The panel reads a finished record; it doesn’t keep scoring while you look at it.

6.2 The Plan

  • A blocked new piece, or a lock wholly above the visible well -> Check Top Out changes the Game State and sends gameOver.
  • That message freezes the record, hides the moving pictures and starts the stone wave.
  • Open Results waits -> it switches input contexts, opens the panel and sends resultsShown.
  • The panel shows the record. Confirm or a click on PLAY AGAIN runs Start A New Game.
  • Restart Scene recreates the Objects. Each owner prepares its own values when the Scene starts.

6.3 Set Up

Continue from Chapter 5 or its catch-up project. The Starter Kit already holds the veil, panel, button pictures and menu sounds.

In Input Manager, use Default’s Edit button to change Input Context Name to Play, then Save Input Context. Keep its gameplay Commands there. Use Add Input Context to make Menus, select its row, and add Confirm with both card keys. Set its startup switch to Inactive at Start. These input contexts are groups of Commands you can switch on and off together.

Add Game Over alongside Playing in Available Game States. That’s the game’s phase, separate from which Commands can be heard. A result panel needs menu input while the Game State remains Game Over.

The placements wait until their Objects are made. Follow their order: background first, button last. These are Screen UI Objects, so their positions count from the card’s screen anchors.

Set upWhat this chapter adds
ART (sprites, from the starter kit)
White Pixel
Large Glass Panel
Aero Button
Aero Button Lit
SOUNDS (from the starter kit)
Game Over Jingle
Menu Select
Menu Bubble
COMMANDS (Game Settings > Input Manager)
ConfirmButton
KeybindingsEnter, Space
INPUT CONTEXTS (Game Settings > Input Manager)
PlayActive at Start(rename Default)
CommandsMove Left, Move Right, Rotate Clockwise, Rotate Counterclockwise, Rotate 180, Soft Drop, Hard Drop, Hold
MenusInactive at Start(new)
CommandsConfirm
GAME SETTINGS > General > Game State
Available Game StatesPlaying, Game Over
SCENE Gameplay: PLACE THESE
ObjectXY
Results Veil00
with Per Instance Variables > Shown Alpha = 0.55
Results Panel00
Results Title0-243
with Z-Index = 145
Result SCORE-225165
with Z-Index = 145
Result SCORE Value225165
with Z-Index = 145
Result LINES-225121.5
with Z-Index = 145
Result LINES Value225121.5
with Z-Index = 145
Result LEVEL-22578
with Z-Index = 145
Result LEVEL Value22578
with Z-Index = 145
New Best0-312
with Z-Index = 145
Result TIME-22534.5
with Z-Index = 145
Result TIME Value22534.5
with Z-Index = 145
Result CASCADES-225-9
with Z-Index = 145
Result CASCADES Value225-9
with Z-Index = 145
Result TWISTS-225-52.5
with Z-Index = 145
Result TWISTS Value225-52.5
with Z-Index = 145
Result BEST COMBO-225-96
with Z-Index = 145
Result BEST COMBO Value225-96
with Z-Index = 145
PLAY AGAIN Button0348
PLAY AGAIN Highlight0348
PLAY AGAIN Label0346.5
with Z-Index = 148

6.4 Finding the End

When is the glass full?

Add crystalsInView to Piece. Lock Piece will count how many crystals of a locking shape reached the visible well. A new shape gets a separate test.

Data StructurePieceAddDelete

The falling piece: its kind (1-7, Piece Kinds), rotation (0 up, 1 right, 2 down, 3 left), the well cell of its 4 x 4 box's bottom-left corner (column from the left, row from the bottom), how long it rests before it locks (lockDelaySeconds, read by the piece and its crystals), how long it takes to fall one row (fallSeconds), and which way the player is moving it (moveDirection: -1 left, 1 right, 0 still). landingRow: the lowest row its box can fall to (Find Landing Row). hardDropRows: the rows its last hard drop crossed. lowestRow: the lowest row it has reached; lockResets: the moves or rotations that restarted its lock delay since touching down there; hasTouchedDown: whether it has touched down since reaching that lowest row; isLockDelayOver: whether its lock delay ran out before it landed again. heldKind: the piece put away with Hold (0 none); hasHeld: Hold was used for this piece. lastMoveWasRotation: its last successful action was a rotation; lastKickTest: the Kicks test (0-4) that rotation used; twist: what it locked as (0 none, 1 mini, 2 full twist). crystalsInView: how many of its crystals locked inside the visible well (0: a lock out).

kindNumberdefault 0
rotationNumberdefault 0
columnNumberdefault 3
rowNumberdefault 18
lockDelaySecondsNumberdefault 0.5
fallSecondsNumberdefault 1
moveDirectionNumberdefault 0
landingRowNumberdefault 0
hardDropRowsNumberdefault 0
lowestRowNumberdefault 0
lockResetsNumberdefault 0
hasTouchedDownBooleandefault false
isLockDelayOverBooleandefault false
heldKindNumberdefault 0
hasHeldBooleandefault false
lastMoveWasRotationBooleandefault false
lastKickTestNumberdefault 0
twistNumberdefault 0
crystalsInViewNumberdefault 0

6.4.1 Spawn Piece

A new piece now asks about its starting place before its controls wake. A fit lets it enter as before. A failure sends pieceBlocked instead, leaving no new shape for gravity to pull through the stack.

The green branch keeps the old work: waking the controls, checking the room below and finding the landing row. The red Enable Actions at the top move inside that branch. After the starting-place test, successful spawning belongs inside Then. Otherwise only sends pieceBlocked.

On Next Piece Due also checks the Game State. Once play has ended, a late request for another piece can’t deal one into the finished well.

Logic ComponentSpawn PieceAttach to: Falling PieceAddDelete

Puts a new piece at the top of the well: the next kind from the Queue (or the held piece, from Hold Piece), rotation up, its box in the hidden rows, then one row down into view if it fits.

Attach toAny Object
DATA STRUCTURES
Pieceself.piece
SHARED VARIABLES
NameTypeKindStarting Value
startColumnNumberConstant3
On Spawn Piece
WhenspawnPieceCustom Event
1Set Logic State
Logic Component
Hold Piece
2Set Logic State
Logic Component
Piece Gravity
3Set Logic State
Logic Component
Move Piece
4Set Logic State
Logic Component
Rotate Piece
5Set Logic State
Logic Component
Hard Drop Piece
1Change Component Value
Component Value
self.piece.rotation
Value
0
2Change Component Value
Component Value
self.piece.lastMoveWasRotation
Value
false
3Change Component Value
Component Value
self.piece.lastKickTest
Value
0
4Change Component Value
Component Value
self.piece.column
Value
@self.startColumn
5Change Component Value
Component Value
self.piece.row
Value
data('Well Layout', 0, 'rows') - 4
6Piece Fits HereFunction
Action Name
roomBelowNewPiece
Action Name
roomForNewPiece
piece
self
columnShift
0
rowShift
0
rowShift
-1
12IfroomBelowNewPiece.value == true
Then
13Change Component Value
Component Value
self.piece.row
Value
self.piece.row - 1
14Find Landing RowFunction
piece
self
15Trigger Custom Event
Custom Event
pieceMoved
7IfroomForNewPiece.value == true
Then
8Set Logic State
Logic Component
Hold Piece
9Set Logic State
Logic Component
Piece Gravity
10Set Logic State
Logic Component
Move Piece
11Set Logic State
Logic Component
Rotate Piece
12Set Logic State
Logic Component
Hard Drop Piece
13Piece Fits HereFunction
Action Name
roomBelowNewPiece
piece
self
columnShift
0
rowShift
-1
14IfroomBelowNewPiece.value == true
Then
15Change Component Value
Component Value
self.piece.row
Value
self.piece.row - 1
16Find Landing RowFunction
piece
self
17Trigger Custom Event
Custom Event
pieceMoved
18Otherwise
19Trigger Custom Event
Custom Event
pieceBlocked
On Next Piece Due
WhennextPieceDueCustom Event
1Change Component Value
Component Value
self.piece.kind
Value
data('Queue', 0, 'piece')
2Trigger Custom Event
Custom Event
pieceDealt
3Trigger Custom Event on Object
Custom Event
spawnPiece
1Check Game State
Action Name
isPlaying
Game State
Playing
2IfisPlaying.value == true
Then
3Change Component Value
Component Value
self.piece.kind
Value
data('Queue', 0, 'piece')
4Trigger Custom Event
Custom Event
pieceDealt
5Trigger Custom Event on Object
Custom Event
spawnPiece

6.4.2 Lock Piece

A piece can also lock entirely in the hidden rows. Lock Piece starts crystalsInView afresh, then counts a crystal when its row number is less than visibleRows. Rows count upward from the floor, so the visible rows have the smaller numbers. The well reads it after checking for cleared lines.

Logic ComponentLock PieceAttach to: Falling PieceAddDelete

Writes the falling piece's four crystals into the Well, takes the piece out of play, tells the well it locked and the Well Cells that crystals joined.

Attach toAny Object
DATA STRUCTURES
Pieceself.piece
On Lock Piece
WhenlockPieceCustom Event
1Declare Variable
Name
lockedKind
Value
0
2Stop Recurring Timer
Timer Name
"gravity"
3Stop Recurring Timer
Timer Name
"lockDelay"
4Change Variable
Variable
lockedKind
Value
self.piece.kind
5Change Component Value
Component Value
self.piece.crystalsInView
Value
0
6Repeat4times
Each time
7Crystal CellFunction
Action Name
lockedCrystal
kind
self.piece.kind
rotation
self.piece.rotation
crystal
local.index + 1
8Well Cell IndexFunction
Action Name
lockedCell
column
self.piece.column + lockedCrystal.column
row
self.piece.row + lockedCrystal.row
9Declare Variable
Name
cellIndex
Value
lockedCell.value
10Set Data Cell
Table
Well
Row
cellIndex
Column
"kind"
Value
lockedKind
11If(self.piece.row + lockedCrystal.row) < data('Well Layout', 0, 'visibleRows')
Then
12Change Component Value
Component Value
self.piece.crystalsInView
Value
self.piece.crystalsInView + 1
13Control Sound
Sound
Lock Click
14Set Logic State
Logic Component
Piece Gravity
State
Disable
15Set Logic State
Logic Component
Move Piece
State
Disable
16Set Logic State
Logic Component
Rotate Piece
State
Disable
17Set Logic State
Logic Component
Hard Drop Piece
State
Disable
18Set Logic State
Logic Component
Hold Piece
State
Disable
19Change Component Value
Component Value
self.piece.hasHeld
Value
false
20Trigger Custom Event
Custom Event
holdChanged
21Trigger Custom Event
Custom Event
pieceLocked
22Trigger Custom Event
Custom Event
crystalsLocked

6.4.3 Check Top Out

Check Top Out hears both paths. On pieceBlocked, the game ends. A lock with no visible crystals ends it only when that lock cleared no line. The line check comes first.

Declare two Shared Variables: well and piece. Choose Glass Well for well and Falling Piece for piece, so each reference reaches the right structure. Set them on Game Rules, where those Variables already name the same resources for scoring. Attach Check Top Out after scoring. The Game Rules card below shows the order.

On toppedOut, the Logic changes the Game State, plays Game Over Jingle and sends gameOver. Then it disables itself. The same ending won’t be announced again while the panel waits to open.

Logic ComponentCheck Top OutAttach to: Game Rules

Ends the game when the player tops out: a new piece with no room where it appears, or a piece that locked wholly in the hidden rows without clearing a line (a lock out).

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
pieceObjectConstantNone
wellObjectConstantNone
shakeStrengthNumberConstant0.01
shakeSecondsNumberConstant0.5
OBJECT KINDS
pieceFalling Piece
wellGlass Well
When the Scene Starts
WhenScene Start
1Set Game State
Game State
Playing
On Piece Blocked
WhenpieceBlockedCustom Event
1Trigger Custom Event on Object
Custom Event
toppedOut
On Lock Checked
WhenlockCheckedCustom Event
1If@self.well.wellState.linesCleared == 0 && @self.piece.piece.crystalsInView == 0
Then
2Trigger Custom Event on Object
Custom Event
toppedOut
On Topped Out
WhentoppedOutCustom Event
1Set Game State
Game State
Game Over
2Control Sound
Sound
Game Over Jingle
3Camera Shake
Intensity
@self.shakeStrength
Seconds
@self.shakeSeconds
4Trigger Custom Event
Custom Event
gameOver
5Set Logic State
Logic Component
Check Top Out
State
Disable

The listener is ready. Before trying it, give the record a clean beginning and a definite end.

6.5 A Record That Belongs to One Game

The next game deserves its own counters.

Add the marked fields to Scoring State. They keep the time and special clears, plus isNewBest for the ending notice. Game Rules already carries this structure under the Name scoring, so keep that name.

Data StructureScoring StateAddDelete

What Game Rules scored: isBackToBackReady, the last line clear was difficult (4 lines or a twist); isBackToBack, the last lock was a difficult clear right after another; lastClearRow, the Clear Names row of the last lock; combo, the clears in a row less one (-1: none yet; 1 and up score); bestCombo, the longest of the game. isNewBest: the game that just ended beat the best kept on this device. cascades: four-line clears this game; twists: locks that were twists; startSeconds: $playTime when play began; playedSeconds: how long the game was played.

lastClearRowNumberdefault 0
comboNumberdefault -1
bestComboNumberdefault 0
isBackToBackReadyBooleandefault false
isBackToBackBooleandefault false
isNewBestBooleandefault false
cascadesNumberdefault 0
twistsNumberdefault 0
startSecondsNumberdefault 0
playedSecondsNumberdefault 0

Cascades get a flag in Clear Names. Add the field to Clear Names Entry, then use this chapter’s changed table card and its CSV. It replaces the table’s rows with the same names and rewards plus the new isCascade values.

Data StructureClear Names EntryAddDelete

One row of the "Clear Names" Data Table. Its fields are the columns of the table. Every kind of lock: row = twist * 5 + lines (twist 0 none, 1 mini, 2 full twist). name: what the player is told (empty: nothing); points: before the level multiplier; sound: what plays; isDifficult: a clear that counts for back-to-back (4 lines, or any twist with lines); allClearPoints: the bonus when such a clear empties the well (row = lines). isCascade: a four-line clear, counted on the results.

nameTextdefault (empty)
pointsNumberdefault 0
soundTextdefault (empty)
isDifficultBooleandefault false
allClearPointsNumberdefault 0
isCascadeBooleandefault false
Data TableClear NamesData Structure: Clear Names EntryAddDelete
IMPORT CSV15 rows, from a file
FIRST ROWS3 of 15, to compare
Row 0plain 0 lines
name
points0
sound
isDifficultNo
allClearPoints0
isCascadeNo
Row 1plain 1 lines
nameSINGLE
points100
soundClear Single
isDifficultNo
allClearPoints800
isCascadeNo
Row 2plain 2 lines
nameDOUBLE
points300
soundClear Double
isDifficultNo
allClearPoints1200
isCascadeNo

6.5.1 Count Clears

A game can end with several kinds of clear behind it. Count Clears keeps the cascades and twists in separate fields, ready for the panel. The scoring row and piece already know which kind happened.

Its gameOver listener disables the counting Logic. A message that arrives after that boundary can’t add another clear to this game’s record.

Logic ComponentCount ClearsAttach to: Game Rules

Counts the game's cascades (four-line clears) and twists for the results.

Attach toAny Object
DATA STRUCTURES
Scoring Stateself.scoring
SHARED VARIABLES
NameTypeKindStarting Value
pieceObjectConstantNone
OBJECT KINDS
pieceFalling Piece
On Clear Scored
WhenclearScoredCustom Event
1Ifdata('Clear Names', self.scoring.lastClearRow, 'isCascade') == true
Then
2Change Component Value
Component Value
self.scoring.cascades
Value
self.scoring.cascades + 1
3If@self.piece.piece.twist > 0
Then
4Change Component Value
Component Value
self.scoring.twists
Value
self.scoring.twists + 1
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Count Clears
State
Disable

6.5.2 Time The Game

The engine’s play clock already measures time. Time The Game remembers its reading at Scene Start, then subtracts that reading when gameOver arrives. The difference is this game’s playedSeconds.

That clock can stop while play is paused. There’s no second stopwatch to keep in step with it when you add Pause later. Keep its Scoring State name as scoring.

Logic ComponentTime The GameAttach to: Game Rules

Times the game with the engine's play clock ($playTime, which stops while the game is paused): from when play begins to when the game ends.

Attach toAny Object
DATA STRUCTURES
Scoring Stateself.scoring
When the Scene Starts
WhenScene Start
1Change Component Value
Component Value
self.scoring.startSeconds
Value
global('playTime')
On Game Over
WhengameOverCustom Event
1Change Component Value
Component Value
self.scoring.playedSeconds
Value
global('playTime') - self.scoring.startSeconds

6.5.3 Count Lines, Score Points and Count Level

Restarting a Scene doesn’t mean every Global becomes its starting value again. Each owner now sets its own count at Scene Start: Count Lines resets lines, Score Points resets score, and Count Level resets currentLevel.

Their gameOver Events stop further counting. Keep the existing clear and level Events; these are additional boundaries around the work you already built.

Logic ComponentCount LinesAttach to: Game RulesAddDelete

Adds the lines the well cleared to the lines count (the Project Global lines).

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
wellObjectConstantNone
OBJECT KINDS
wellGlass Well
When the Scene Starts
WhenScene Start
1Change Variable
Variable
$lines
Value
0
On Lines Cleared
WhenlinesClearedCustom Event
1Change Variable
Variable
$lines
Operator
+=
Value
@self.well.wellState.linesCleared
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Count Lines
State
Disable
Logic ComponentScore PointsAttach to: Game RulesAddDelete

Adds to the score (the Project Global score): each lock's clear (Clear Names points x level), and soft and hard drop rows.

Attach toAny Object
DATA STRUCTURES
Scoring Stateself.scoring
SHARED VARIABLES
NameTypeKindStarting Value
wellObjectConstantNone
pieceObjectConstantNone
softDropPointsNumberConstant1
hardDropPointsNumberConstant2
backToBackTimesNumberConstant1.5
OBJECT KINDS
wellGlass Well
pieceFalling Piece
When the Scene Starts
WhenScene Start
1Change Variable
Variable
$score
Value
0
On Lock Checked
WhenlockCheckedCustom Event
1Declare Variable
Name
clearPoints
Value
0
2Change Component Value
Component Value
self.scoring.lastClearRow
Value
@self.piece.piece.twist * 5 + @self.well.wellState.linesCleared
3Change Component Value
Component Value
self.scoring.isBackToBack
Value
false
4If@self.well.wellState.linesCleared > 0
Then
5Ifdata('Clear Names', self.scoring.lastClearRow, 'isDifficult') == true
Then
6Change Component Value
Component Value
self.scoring.isBackToBack
Value
self.scoring.isBackToBackReady
7Change Component Value
Component Value
self.scoring.isBackToBackReady
Value
true
8Otherwise
9Change Component Value
Component Value
self.scoring.isBackToBackReady
Value
false
10Change Variable
Variable
clearPoints
Value
data('Clear Names', self.scoring.lastClearRow, 'points')
11Change Variable
Variable
clearPoints
Operator
*=
Value
$currentLevel
12Ifself.scoring.isBackToBack == true
Then
13Change Variable
Variable
clearPoints
Operator
*=
Value
@self.backToBackTimes
14Change Variable
Variable
$score
Operator
+=
Value
floor(clearPoints)
15Trigger Custom Event
Custom Event
clearScored
On Well Emptied
WhenwellEmptiedCustom Event
1Declare Variable
Name
allClearBonus
Value
0
2Change Variable
Variable
allClearBonus
Value
data('Clear Names', @self.well.wellState.linesCleared, 'allClearPoints')
3Change Variable
Variable
allClearBonus
Operator
*=
Value
$currentLevel
4Change Variable
Variable
$score
Operator
+=
Value
allClearBonus
On Soft Drop Row
WhensoftDropRowCustom Event
1Change Variable
Variable
$score
Operator
+=
Value
@self.softDropPoints
On Hard Dropped
WhenhardDroppedCustom Event
1Change Variable
Variable
$score
Operator
+=
Value
@self.piece.piece.hardDropRows * @self.hardDropPoints
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Score Points
State
Disable
Logic ComponentCount LevelAttach to: Game RulesAddDelete

Raises the level (the Project Global currentLevel) by one for every linesPerLevel lines, up to lastLevel, and says so.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
linesPerLevelNumberConstant10
lastLevelNumberConstant20
When the Scene Starts
WhenScene Start
1Change Variable
Variable
$currentLevel
Value
1
On Lines Cleared
WhenlinesClearedCustom Event
1Declare Variable
Name
reachedLevel
Value
0
2Change Variable
Variable
reachedLevel
Value
1 + floor(global('lines') / @self.linesPerLevel)
3IfreachedLevel > @self.lastLevel
Then
4Change Variable
Variable
reachedLevel
Value
@self.lastLevel
5IfreachedLevel > global('currentLevel')
Then
6Change Variable
Variable
$currentLevel
Value
reachedLevel
7Trigger Custom Event
Custom Event
levelUp
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Count Level
State
Disable

Count Combo gets the ending listener too. Its Scoring State fields start from their defaults on the recreated Game Rules Object, so the next game can build a new chain.

Logic ComponentCount ComboAttach to: Game RulesAddDelete

Keeps the combo (clears in a row) and the best combo of the game, and scores comboPoints x combo x level from the second clear in a row.

Attach toAny Object
DATA STRUCTURES
Scoring Stateself.scoring
SHARED VARIABLES
NameTypeKindStarting Value
wellObjectConstantNone
comboPointsNumberConstant50
OBJECT KINDS
wellGlass Well
On Clear Scored
WhenclearScoredCustom Event
1If@self.well.wellState.linesCleared > 0
Then
2Change Component Value
Component Value
self.scoring.combo
Value
self.scoring.combo + 1
3Ifself.scoring.combo > self.scoring.bestCombo
Then
4Change Component Value
Component Value
self.scoring.bestCombo
Value
self.scoring.combo
5Ifself.scoring.combo >= 1
Then
6Change Variable
Variable
$score
Operator
+=
Value
@self.comboPoints * self.scoring.combo * global('currentLevel')
7Trigger Custom Event
Custom Event
comboScored
8Otherwise
9Change Component Value
Component Value
self.scoring.combo
Value
-1
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Count Combo
State
Disable

6.5.4 Deal Pieces

Queue is a table the game changes while playing. Deal Pieces now resets it at Scene Start, before its Create Event fills the first bags. The queueLength memory and the waiting rows can therefore agree about where a new game begins.

Logic ComponentDeal PiecesAttach to: Piece QueueAddDelete

Keeps the Queue table full of pieces from shuffled bags of seven: deals two bags at the start, takes the first piece off whenever one is dealt, and adds a bag whenever fewer than seven are left.

Attach toAny Object
DATA STRUCTURES
Deal Pieces Dataself.dealPiecesData
When the Scene Starts
WhenScene Start
1Reset Data Table
Table
Queue
When Created
WhenCreate
1Change Component Value
Component Value
self.dealPiecesData.queueLength
Value
0
2Repeat2times
Each time
3Add A Shuffled BagFunction
firstPlace
self.dealPiecesData.queueLength
4Change Component Value
Component Value
self.dealPiecesData.queueLength
Operator
+=
Value
dataCount('Piece Kinds')
5Trigger Custom Event
Custom Event
nextPieceDue
On Piece Dealt
WhenpieceDealtCustom Event
1Declare Variable
Name
placeRow
Value
0
2Change Variable
Variable
placeRow
Value
0
3WhileplaceRow < (self.dealPiecesData.queueLength - 1)
Each time
4Declare Variable
Name
placeKind
Value
data('Queue', placeRow + 1, 'piece')
5Set Data Cell
Table
Queue
Row
placeRow
Column
"piece"
Value
placeKind
6Change Variable
Variable
placeRow
Operator
+=
Value
1
7Set Data Cell
Table
Queue
Row
placeRow
Column
"piece"
8Change Component Value
Component Value
self.dealPiecesData.queueLength
Operator
-=
Value
1
9Ifself.dealPiecesData.queueLength < dataCount('Piece Kinds')
Then
10Add A Shuffled BagFunction
firstPlace
self.dealPiecesData.queueLength
11Change Component Value
Component Value
self.dealPiecesData.queueLength
Operator
+=
Value
dataCount('Piece Kinds')
12Trigger Custom Event
Custom Event
queueChanged

6.5.5 Keep Bests

Here is the save promised in Chapter 5. When the game ends, Keep Bests compares score with bestMarathonScore. A better score updates that Global, saves it under the same persistent key used to load it, and marks isNewBest.

The best belongs to the device, rather than to one run of the Scene. A new game resets its score while that best remains. The notice reads isNewBest after the comparison has settled whether this game beat the saved score.

Logic ComponentKeep BestsAttach to: Game RulesAddDelete

Reads the best score kept on this device into the Project Global bestMarathonScore when the game starts, and keeps a better score when a game ends.

Attach toAny Object
DATA STRUCTURES
Scoring Stateself.scoring
When the Game Starts
WhenGame Start
1Load Persistent Value
Action Name
savedBest
Key
"bestMarathon"
Default
0
2Change Variable
Variable
$bestMarathonScore
Value
savedBest.value
On Game Over
WhengameOverCustom Event
1Ifglobal('score') > global('bestMarathonScore')
Then
2Change Variable
Variable
$bestMarathonScore
Value
global('score')
3Save Persistent Value
Action Name
savedBestWrite
Key
"bestMarathon"
Value
global('bestMarathonScore')
4Change Component Value
Component Value
self.scoring.isNewBest
Value
true

The counters can stop. Their finished record waits while the pictures show what an ending means.

6.6 The Wave of Stone

The color leaves the stack from the bottom upward.

Add the stone field. Both Crystal Frames Entry and the table need it before a cell can choose the new frame. It names the gray frame the Well Cells will use. The stack’s kinds stay in Well; this is a change in how they are drawn.

Data StructureCrystal Frames EntryAddDelete

One row of the "Crystal Frames" Data Table. Its fields are the columns of the table. Frames of the sprite "Crystal Blocks" that are not a kind of piece. One row. shatter: a crystal in a full line. stone: a crystal when the game is over.

shatterNumberdefault 8
stoneNumberdefault 9
Data TableCrystal FramesData Structure: Crystal Frames EntryAddDelete
Row 0Frames
shatter8
stone9

6.6.1 Show Well Cell

Every cell hears gameOver at the same time, but waits according to its row. A low row has a shorter wait. A higher row begins later, so the same Event makes a wave rather than changing the whole stack at once.

When turnToStone rings, a filled cell shows the stone frame. An empty cell stays empty. The row’s place controls the delay, while shownKind tells the cell whether there’s a crystal to change.

Logic ComponentShow Well CellAttach to: Well CellAddDelete

One cell of the well: it finds its cell from where it is placed, then shows the crystal stored there whenever the well changes; a crystal a lock just put there pops, and a crystal in a full line bursts into shards.

Attach toAny Object
DATA STRUCTURES
Show Well Cell Dataself.showWellCellData
SHARED VARIABLES
NameTypeKindStarting Value
popScaleNumberConstant1.22
popSecondsNumberConstant0.2
stoneDelaySecondsNumberConstant0.05
stoneRowSecondsNumberConstant0.035
When Created
WhenCreate
1Cell AtFunction
Action Name
myCell
x
self.transform.position.x
y
self.transform.position.y
2Well Cell IndexFunction
Action Name
thisCellIndex
column
myCell.column
row
myCell.row
3Change Component Value
Component Value
self.showWellCellData.cellIndex
Value
thisCellIndex.value
4Trigger Custom Event on Object
Custom Event
wellChanged
On Crystals Locked
WhencrystalsLockedCustom Event
1Ifself.showWellCellData.shownKind == 0 && data('Well', self.showWellCellData.cellIndex, 'kind') != 0
Then
2Set Scale
Horizontal Scale
@self.popScale
Vertical Scale
@self.popScale
3Tween
Value
self.transform.scale
Target
1
Duration
@self.popSeconds
Ease
Quad Out
4Burst Particles
Particles
Lock Glint
Z Index Offset
6
5Trigger Custom Event on Object
Custom Event
wellChanged
On Well Changed
WhenwellChangedCustom Event
1Ifdata('Well', self.showWellCellData.cellIndex, 'isClearing') == true && self.showWellCellData.isShowingShatter == false
Then
2Burst Particles
Particles
Crystal Shards
Z Index Offset
6
Tint
data('Piece Kinds', data('Well', self.showWellCellData.cellIndex, 'kind') - 1, 'color')
3Change Component Value
Component Value
self.showWellCellData.isShowingShatter
Value
data('Well', self.showWellCellData.cellIndex, 'isClearing')
4Change Component Value
Component Value
self.showWellCellData.shownKind
Value
data('Well', self.showWellCellData.cellIndex, 'kind')
5Ifself.showWellCellData.isShowingShatter == true
Then
6Set Frame
Frame
data('Crystal Frames', 0, 'shatter')
7Otherwise
8Set Frame
Frame
self.showWellCellData.shownKind
On Game Over
WhengameOverCustom Event
1Declare Variable
Name
waitSeconds
Value
0
2Declare Variable
Name
rowFromBottom
Value
0
3Change Variable
Variable
rowFromBottom
Value
floor(self.showWellCellData.cellIndex / data('Well Layout', 0, 'columns'))
4Change Variable
Variable
waitSeconds
Value
@self.stoneDelaySeconds + rowFromBottom * @self.stoneRowSeconds
5Start Recurring Timer
Timer Name
"turnToStone"
Repeat Every (s)
waitSeconds
Ring Once
Yes
On the Turn to Stone Timer
WhenRecurring TimerTimer name: turnToStone
1Ifself.showWellCellData.shownKind != 0
Then
2Set Frame
Frame
data('Crystal Frames', 0, 'stone')

6.6.2 Show Piece Crystal and Show Preview Crystal

The moving shape and previews have finished their jobs. Add their gameOver Events to hide them, leaving the stone stack visible behind the coming panel. The next Scene recreates those Objects for the new game.

Logic ComponentShow Piece CrystalAttach to: Falling Crystal, Ghost CrystalAddDelete

One crystal of the falling piece: whenever the piece moves, it goes to its cell (or, with showsLanding, to its cell in the landing row: the ghost) and shows the piece's color; it hides in the two hidden rows, and the piece's own crystals fade while it rests before locking.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
pieceObjectConstantNone
dimmedAlphaNumberConstant0.55
PER INSTANCE VARIABLES
NameTypeStarting Value
crystalNumber1
showsLandingBooleanNo
OBJECT KINDS
pieceFalling Piece
On Piece Moved
WhenpieceMovedCustom Event
1Control Tween
Which
Value
Value
self.transform.opacity
2Set Alpha
3Crystal CellFunction
Action Name
thisCrystal
kind
@self.piece.piece.kind
rotation
@self.piece.piece.rotation
crystal
self.crystal
4Declare Variable
Name
cellColumn
Value
@self.piece.piece.column + thisCrystal.column
5Declare Variable
Name
cellRow
Value
@self.piece.piece.row + thisCrystal.row
6Ifself.showsLanding == true
Then
7Change Variable
Variable
cellRow
Value
@self.piece.piece.landingRow + thisCrystal.row
8Cell MiddleFunction
Action Name
thisCellMiddle
column
cellColumn
row
cellRow
9Set Position
X
thisCellMiddle.x
Y
thisCellMiddle.y
10Set Frame
Frame
data('Piece Kinds', @self.piece.piece.kind - 1, 'frame')
11IfcellRow < data('Well Layout', 0, 'visibleRows')
Then
12Set Visible
Visible
Yes
13Otherwise
14Set Visible
Visible
No
On Lock Delay Started
WhenlockDelayStartedCustom Event
1Ifself.showsLanding == false
Then
2Piece Fits HereFunction
Action Name
roomBelowDimmingPiece
piece
@self.piece
columnShift
0
rowShift
-1
3IfroomBelowDimmingPiece.value == false
Then
4Tween
Value
self.transform.opacity
Target
@self.dimmedAlpha
Duration
@self.piece.piece.lockDelaySeconds
On Game Over
WhengameOverCustom Event
1Set Visible
Visible
No
Logic ComponentShow Preview CrystalAttach to: Preview CrystalAddDelete

One crystal of a preview: the piece in its Queue place, or the held piece in the Hold box (Preview Slots), centered in its box at that box's cell size; hidden while there is no piece; the held piece dims once Hold was used for the falling piece.

Attach toAny Object
DATA STRUCTURES
Show Preview Crystal Dataself.showPreviewCrystalData
SHARED VARIABLES
NameTypeKindStarting Value
pieceObjectConstantNone
usedAlphaNumberConstant0.35
PER INSTANCE VARIABLES
NameTypeStarting Value
slotNumber0
crystalNumber1
OBJECT KINDS
pieceFalling Piece
When Created
WhenCreate
1Declare Variable
Name
boxScale
Value
0
2Change Variable
Variable
boxScale
Value
data('Preview Slots', self.slot, 'cellSize')
3Change Variable
Variable
boxScale
Operator
/=
Value
data('Well Layout', 0, 'cellSize')
4Set Scale
Horizontal Scale
boxScale
Vertical Scale
boxScale
5Trigger Custom Event on Object
Custom Event
queueChanged
On Queue Changed
WhenqueueChangedCustom Event
1Ifdata('Preview Slots', self.slot, 'isHold') == true
Then
2Change Component Value
Component Value
self.showPreviewCrystalData.previewKind
Value
@self.piece.piece.heldKind
3Otherwise
4Change Component Value
Component Value
self.showPreviewCrystalData.previewKind
Value
data('Queue', data('Preview Slots', self.slot, 'queuePlace'), 'piece')
5Ifself.showPreviewCrystalData.previewKind > 0
Then
6Preview Crystal PlaceFunction
Action Name
myPlace
kind
self.showPreviewCrystalData.previewKind
crystal
self.crystal
slot
self.slot
7Set Position
X
myPlace.x
Y
myPlace.y
8Set Frame
Frame
data('Piece Kinds', self.showPreviewCrystalData.previewKind - 1, 'frame')
9Ifdata('Preview Slots', self.slot, 'isHold') == true && @self.piece.piece.hasHeld == true
Then
10Set Alpha
Alpha
@self.usedAlpha
11Otherwise
12Set Alpha
13Set Visible
Visible
Yes
14Otherwise
15Set Visible
Visible
No
On Hold Changed
WhenholdChangedCustom Event
1Trigger Custom Event on Object
Custom Event
queueChanged
On Game Over
WhengameOverCustom Event
1Set Visible
Visible
No

Let the stone settle. Give the player that beat before opening the glass panel.

6.7 The Panel Opens

The choice needs somewhere to wait before a player can choose it, even when there’s only one button.

Panel State remembers which choice is selected and whether the panel is open. The small Panel Declarations pack brings this structure and the declarations for Choose Panel Choice, Panel Choice and Show When Panel Choice. Their Events and Actions are empty.

Install it now, before making those resources. Use File > Install Myth Pack, choose the downloaded pack, review its four resources, then Install. If the window asks, select I trust this pack’s author to enable Install. This pack is part of the recipe.

The pack prepares which panel fields these references can read. Keep those declarations as they arrive, and build every Event and Action from the cards. You still make Results Panel and add its Panel State component below. Its default chosen value selects the first choice.

Data StructurePanel State

What a panel of choices keeps: chosen, the choice chosen now (0 = the top one); isOpen, the panel is open and its choices can be chosen.

chosenNumberdefault 0
isOpenBooleandefault false

6.7.1 Choose Panel Choice

The glow needs to travel when another button is chosen. Choose Panel Choice tells the panel where it belongs, and the pictures respond together.

Open the prepared Choose Panel Choice Function. Its panel Parameter already requires Panel State under the name panelState. That’s the default expression name for this structure. This Function will also serve panels with more choices in later chapters.

FunctionChoose Panel Choice

Makes choice the chosen choice of a panel (its Panel State chosen) and tells the highlights. The only writer of chosen.

PARAMETERS
NameTypeStarts at
panelObjectStructural
Required Components
Slot NamepanelState
Component TypeData Structure
Required SchemaPanel State
choiceNumber0
ACTIONS
1Change Component Value
Component Value
panel.panelState.chosen
Value
choice
2Trigger Custom Event
Custom Event
panelChoiceChanged

6.7.2 Panel Choices

Panel Choices hears panelOpened on its own Object, marks the panel open and announces its chosen choice. Closing the panel clears that open flag and returns its choice to the first one. Make the Logic and attach it when Results Panel is ready.

Logic ComponentPanel ChoicesAttach to: Results Panel

A panel of choices: opens on its chosen choice when told (panelOpened; the top one unless the Logic that opens it chose another) and closes when told (panelClosed), going back to its top choice.

Attach toAny Object
DATA STRUCTURES
Panel Stateself.panelState
On Panel Opened
WhenpanelOpenedCustom Event
1Change Component Value
Component Value
self.panelState.isOpen
Value
true
2Choose Panel ChoiceFunction
panel
self
choice
self.panelState.chosen
On Panel Closed
WhenpanelClosedCustom Event
1Change Component Value
Component Value
self.panelState.isOpen
Value
false
2Change Component Value
Component Value
self.panelState.chosen
Value
0
3Trigger Custom Event
Custom Event
panelChoiceChanged

6.7.3 Show With Results

Pictures and words can wait together. Show With Results hides its Object when created, then shows it at shownAlpha when resultsShown arrives. The veil needs a gentler alpha.

Logic ComponentShow With ResultsAttach to: Results Veil, Results Panel, Results Title, Result SCORE, Result SCORE Value, Result LINES, Result LINES Value, Result LEVEL, Result LEVEL Value, Result TIME, Result TIME Value, Result CASCADES, Result CASCADES Value, Result TWISTS, Result TWISTS Value, Result BEST COMBO, Result BEST COMBO Value, PLAY AGAIN Button, PLAY AGAIN Label

One part of the results panel: hidden during play, shown at shownAlpha when the results open.

Attach toAny Object
PER INSTANCE VARIABLES
NameTypeStarting Value
shownAlphaNumber1
When Created
WhenCreate
1Set Visible
Visible
No
On Results Shown
WhenresultsShownCustom Event
1Set Alpha
Alpha
self.shownAlpha
2Set Visible
Visible
Yes

Make Results Veil, Results Panel and Results Title. Give the panel its Panel State component and both listed Logics. The veil’s Sprite Tint is set in its Sprite Properties; its later Shown Alpha override belongs to the placement.

ObjectResults Veil
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 128
Sprite
Scene Depth Draw Order140
SpriteWhite Pixel
Tint#03213F
Transform > SizeX 1920, Y 1080
Logic
Show With Results
ObjectResults Panel
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 128
Sprite
Scene Depth Draw Order141
SpriteLarge Glass Panel
Transform > SizeX 900, Y 705
Data Structure
Data StructurePanel State
Logic
Show With Results
Panel Choices
ObjectResults Title
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorTop Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextGAME OVER
FontNunito
Font Size69
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X870
Logic
Show With Results

6.7.4 Open Results

Open Results hears gameOver and starts its delay. When that timer rings, it turns Menus on and Play off, opens Results Panel, then announces resultsShown. The ending gets time to be seen. Only after that quiet delay can the panel accept the choice that begins another game.

Declare resultsPanel with Type Object and Kind Constant. The card accepts Objects with Panel State; here, choose Results Panel as its Object Kind. That concrete Object already carries Panel State and is the only panel this Logic will open. Open Results sends it a message without reading its fields, so this declaration needs none of the pack’s prepared field access. On Game Rules, set the value to the Results Panel resource too. Add Check Top Out, Open Results, Count Clears and Time The Game in the order shown here.

Logic ComponentOpen ResultsAttach to: Game Rules

Opens the results panel resultsDelaySeconds after the game ends (resultsShown).

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
resultsDelaySecondsNumberConstant1.6
resultsPanelObjectConstantNone
OBJECT KINDS
resultsPanelObjects with Panel State
When the Scene Starts
WhenScene Start
1Set Input Context Active
Input Context
Play
2Set Input Context Active
Input Context
Menus
Active
No
On Game Over
WhengameOverCustom Event
1Start Recurring Timer
Timer Name
"resultsDelay"
Repeat Every (s)
@self.resultsDelaySeconds
Ring Once
Yes
On the Results Delay Timer
WhenRecurring TimerTimer name: resultsDelay
1Set Input Context Active
Input Context
Menus
2Set Input Context Active
Input Context
Play
Active
No
3Trigger Custom Event on Object
Applies to
@self.resultsPanel
Custom Event
panelOpened
4Trigger Custom Event
Custom Event
resultsShown
ObjectGame RulesAddDelete
Data Structure
Data StructureScoring State
Logic
Count Lines
Score Points
Count Combo
Count Level
Keep Bests
Check Top Out
Open Results
Count Clears
Time The Game
Shared Variables
wellGlass Well
pieceFalling Piece
resultsPanelResults Panel

Place the veil, panel and title using the setup’s first lines. They hide themselves during play. The rest of the list will fill the panel with the record and its button.

6.8 Reading the Finished Game

The familiar counts can keep their familiar templates.

Make the SCORE, LINES and LEVEL label-and-value pairs from their cards. Their Globals supply the values. Across these pairs, the label and template change while the text styling stays familiar. Compare those changing fields first, then check the shared styling. A label stays a word, and its matching value stays a number.

ObjectResult SCORE
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextSCORE
FontNunito
Font Size30
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X390
Logic
Show With Results
ObjectResult SCORE Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$score:,}
FontNunito
Font Size36
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X390
Logic
Show With Results
ObjectResult LINES
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextLINES
FontNunito
Font Size30
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X390
Logic
Show With Results
ObjectResult LINES Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$lines}
FontNunito
Font Size36
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X390
Logic
Show With Results
ObjectResult LEVEL
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextLEVEL
FontNunito
Font Size30
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X390
Logic
Show With Results
ObjectResult LEVEL Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$currentLevel}
FontNunito
Font Size36
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X390
Logic
Show With Results

6.8.1 Show New Best

New Best starts hidden. Show New Best reads Game Rules’ isNewBest when resultsShown arrives, after Keep Bests has compared and saved. A game that didn’t beat the saved score leaves the notice hidden.

Set rules to Game Rules on this text Object. It reads the finished comparison; it doesn’t make another one after the best has already changed.

Logic ComponentShow New BestAttach to: New Best

Says "NEW PERSONAL BEST!" on the results panel when the game that ended beat the best kept on this device.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
rulesObjectConstantNone
OBJECT KINDS
rulesGame Rules
When Created
WhenCreate
1Set Visible
Visible
No
On Results Shown
WhenresultsShownCustom Event
1If@self.rules.scoring.isNewBest == true
Then
2Set Visible
Visible
Yes
ObjectNew Best
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorTop Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextNEW PERSONAL BEST!
FontNunito
Font Size36
EmphasisBold
Color#FFF3B0
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X870
Logic
Show New Best
Shared Variables
rulesGame Rules

6.8.2 Show Game Record

The remaining values live in Scoring State. Make Game Record Text, then make the TIME, CASCADES, TWISTS and BEST COMBO value Objects shown below. Add one Data Structure component to each yourself. Choose Game Record Text as its resource and set Name to shown. This is the plain component on each Object card.

Data StructureGame Record Text

What a results value shows, copied from the record of the game when the results open: timeMinutes and timeSeconds (the time played), cascades, twists, bestCombo. Each value text reads the fields it needs through its template.

timeMinutesNumberdefault 0
timeSecondsNumberdefault 0
cascadesNumberdefault 0
twistsNumberdefault 0
bestComboNumberdefault 0

Show Game Record copies the finished record into that structure when resultsShown arrives. It also divides playedSeconds for the clock. Each text template then chooses the fields it needs. TIME reads both clock fields; the other values each read one count. The labels name those counts beside them.

In Show Game Record, declare rules with Type Object, Kind Constant, and Object Kind Game Rules.

On each value Object, add and name the component before attaching Show Game Record. Its DATA STRUCTURES entry describes that same shown component. Keep that single copy. Choose Game Rules for rules in the Object’s Shared Variables section.

Return to the Logic to build the expressions that read self.shown. Its named component is ready for those values. Show With Results handles visibility alongside it. The four value Objects need this component and reference; their fixed labels do not.

Logic ComponentShow Game RecordAttach to: Result TIME Value, Result CASCADES Value, Result TWISTS Value, Result BEST COMBO Value

For a results value: copies the record of the game from Game Rules into its Game Record Text (timeMinutes, timeSeconds, cascades, twists, bestCombo), which the template of the text reads.

Attach toAny Object
DATA STRUCTURES
Game Record Textself.shown
SHARED VARIABLES
NameTypeKindStarting Value
rulesObjectConstantNone
OBJECT KINDS
rulesGame Rules
On Results Shown
WhenresultsShownCustom Event
1Change Component Value
Component Value
self.shown.timeMinutes
Value
floor(@self.rules.scoring.playedSeconds / 60)
2Change Component Value
Component Value
self.shown.timeSeconds
Value
@self.rules.scoring.playedSeconds - self.shown.timeMinutes * 60
3Change Component Value
Component Value
self.shown.cascades
Value
@self.rules.scoring.cascades
4Change Component Value
Component Value
self.shown.twists
Value
@self.rules.scoring.twists
5Change Component Value
Component Value
self.shown.bestCombo
Value
@self.rules.scoring.bestCombo
ObjectResult TIME
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextTIME
FontNunito
Font Size30
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X390
Logic
Show With Results
ObjectResult TIME Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{timeMinutes}:{timeSeconds:02.2}
FontNunito
Font Size36
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X390
Data Structure
Data StructureGame Record Text
Logic
Show With Results
Show Game Record
Shared Variables
rulesGame Rules
ObjectResult CASCADES
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextCASCADES
FontNunito
Font Size30
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X390
Logic
Show With Results
ObjectResult CASCADES Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{cascades}
FontNunito
Font Size36
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X390
Data Structure
Data StructureGame Record Text
Logic
Show With Results
Show Game Record
Shared Variables
rulesGame Rules
ObjectResult TWISTS
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextTWISTS
FontNunito
Font Size30
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X390
Logic
Show With Results
ObjectResult TWISTS Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{twists}
FontNunito
Font Size36
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X390
Data Structure
Data StructureGame Record Text
Logic
Show With Results
Show Game Record
Shared Variables
rulesGame Rules
ObjectResult BEST COMBO
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextBEST COMBO
FontNunito
Font Size30
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X390
Logic
Show With Results
ObjectResult BEST COMBO Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{bestCombo}
FontNunito
Font Size36
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X390
Data Structure
Data StructureGame Record Text
Logic
Show With Results
Show Game Record
Shared Variables
rulesGame Rules

Place the record texts and New Best in the setup list’s order. Each row gives its label and value, so you can check one pair before moving to the next. The button is the last part.

6.9 Another Game

PLAY AGAIN gives the ending somewhere to go.

6.9.1 Start A New Game

The Function is short because the owners already know how to begin. Restart Scene recreates the Objects, runs their Scene Start work and prepares their starting state. It leaves the saved best on the device.

FunctionStart A New Game

Starts the same game again: Restart Scene. Each part of the game sets itself back at Scene Start.

ACTIONS
1Restart Scene

6.9.2 Panel Choice

The same button can answer the keyboard or the mouse. Panel Choice gives both routes the same announcement, choiceRun, so the button’s job stays separate from how you chose it.

Keep the prepared panel declaration with its Panel State component restriction. Choose Results Panel as its value on the button Object. choice identifies which choice this button represents. This panel has one, so its starting value already picks PLAY AGAIN.

Logic ComponentPanel ChoiceAttach to: PLAY AGAIN Button

One choice of a panel: while the panel is open, Confirm runs it when it is the chosen choice; the mouse over it chooses it, and a click on it runs it. Running it plays Menu Select and tells its own button (choiceRun), whose other Logic does the work of the choice.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
panelObjectConstantNone
choiceNumberConstant0
OBJECT KINDS
panelObjects with Panel State
When Confirm Is Pressed
WhenCommand PressedCommand: Confirm
1If@self.panel.panelState.isOpen == true && @self.panel.panelState.chosen == @self.choice
Then
2Control Sound
Sound
Menu Select
3Trigger Custom Event on Object
Custom Event
choiceRun
On Mouse Enter
WhenMouse Enter
1If@self.panel.panelState.chosen != @self.choice
Then
2Choose Panel ChoiceFunction
panel
@self.panel
choice
@self.choice
3Control Sound
Sound
Menu Bubble
When Left Is Clicked
WhenMouse Pressed on ObjectMouse button: left
1Choose Panel ChoiceFunction
panel
@self.panel
choice
@self.choice
2Control Sound
Sound
Menu Select
3Trigger Custom Event on Object
Custom Event
choiceRun

6.9.3 Play Again

Now give that choice its meaning. It listens for choiceRun and calls Start A New Game. A different button can use the same choosing Logic and give that message a different listener.

Logic ComponentPlay AgainAttach to: PLAY AGAIN Button

What the PLAY AGAIN choice does: starts a new game of the same mode (Start A New Game).

Attach toAny Object
On Choice Run
WhenchoiceRunCustom Event
1Start A New GameFunction

Make PLAY AGAIN Button with its sensor Collider and the listed Logics. The mouse needs that shape. It doesn’t need to push anything in the well.

ObjectPLAY AGAIN Button
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 128
Sprite
Scene Depth Draw Order146
SpriteAero Button
Transform > SizeX 442.8, Y 77.76
Collider
CollisionSensor
Shape and Transform > SizeX 418.8, Y 56.76
Logic
Show With Results
Panel Choice
Play Again
Shared Variables
panelResults Panel

6.9.4 Show When Panel Choice

The brighter picture is a separate glow. Show When Panel Choice checks whether the panel is open and its chosen value matches this placement’s choice. Both facts must be true before the brighter picture appears above the button.

Keep Show When Panel Choice’s prepared panel declaration too. On that brighter picture Object, set panel to the Results Panel resource in Shared Variables. Then make PLAY AGAIN Highlight and its label. The last lines in the setup list place these Objects over the button you made. The button sits under the highlight and words, so its sensor remains the thing that hears the mouse.

Logic ComponentShow When Panel ChoiceAttach to: PLAY AGAIN Highlight

The highlight over one choice of a panel: shown while its panel is open and its choice is the chosen one, hidden otherwise.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
panelObjectConstantNone
PER INSTANCE VARIABLES
NameTypeStarting Value
choiceNumber0
OBJECT KINDS
panelObjects with Panel State
When Created
WhenCreate
1Trigger Custom Event on Object
Custom Event
panelChoiceChanged
On Panel Choice Changed
WhenpanelChoiceChangedCustom Event
1If@self.panel.panelState.isOpen == true && @self.panel.panelState.chosen == self.choice
Then
2Set Visible
Visible
Yes
3Otherwise
4Set Visible
Visible
No
ObjectPLAY AGAIN Highlight
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 128
Sprite
Scene Depth Draw Order147
SpriteAero Button Lit
Transform > SizeX 442.8, Y 77.76
Logic
Show When Panel Choice
Shared Variables
panelResults Panel
ObjectPLAY AGAIN Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextPLAY AGAIN
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X442.8
Logic
Show With Results

Run the game and let the well fill. After the stone wave, read the panel and choose PLAY AGAIN. The first piece falls into an empty well, with the previous best still beside it.

6.10 Play It!

  • Keep dropping pieces in the middle until the stack reaches the hidden rows. The game ends instead of dealing shapes into a full well.
  • Watch the stack after Game Over Jingle. Its filled cells turn gray from the bottom upward; the moving piece, ghost and previews hide.
  • Wait for the panel. SCORE, LINES and LEVEL match the ended game, and its time and special-clear counters stay still.
  • Beat the saved best and look for NEW PERSONAL BEST! A later game that doesn’t beat it leaves that notice hidden.
  • Press Enter on PLAY AGAIN. The well empties, score and lines reset, and the first level begins with newly dealt pieces.
  • End another game and click the button instead. The mouse and Confirm should reach the same fresh start.

6.11 How It Works

The ending is a message. Each part hears it, finishes its own work and leaves a stable record. The panel reads after those listeners have finished.

A fresh start follows the same ownership. Restart Scene asks the pieces to begin again; each one knows which values belong to its own game. The best belongs somewhere longer lived.

6.12 If It Didn’t Work

The game keeps asking for pieces after the well fills? Compare Spawn Piece’s starting-place test and its pieceBlocked sender with Check Top Out. Does On Next Piece Due also test Playing? Restore the boundary, then keep dropping into the middle until you reach the top again.

Stone appears all at once? Check Show Well Cell uses rowFromBottom in its wait, rather than only the starting delay. Their waits should differ. Restore the sum and watch the wave climb again.

The panel appears but Enter does nothing? Check Menus includes Confirm, then Open Results turns Menus on before showing the panel. Panel Choices must also receive panelOpened on Results Panel. Try Enter again after restoring those links; the selected choice should now hear you.

The next game keeps the old score? Start with Score Points. A Global survives the restart until its owner resets it. Add that reset from the card, then choose PLAY AGAIN once more.

The record’s self.shown name stays red? Check the value Object’s Game Record Text component is named shown and its Logic is attached. Retype the expression after those names agree. The finished record can then be copied into the component that each value text reads.

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

6.13 Challenge

What would you want to remember about your own game? Try changing which record field a result text shows, or give its panel a different layout. You might make room for a moment you care about that this game doesn’t count yet. Keep the working record beside you while you explore.

The ending works. Next, it gets a front door.