- Learn
- Build a Game
- Pitch Shot
- 6 The Tower Comes Down
6 The Tower Comes Down
By the end of this chapter, the right note answers. It bursts into golden sparkles and plays its flourish, and the world slows for a breath. Then its tower comes down: wood knocks, splinters fly, and the crash grows with every block that falls.
The game as it is at the end of this chapter. Click the game to play.
If your game and this chapter part ways, here is the chapter’s finished project. Unzip it first. Then open it and compare, or start Chapter 7 from it.
To open it, right-click the downloaded file, choose Extract All... and then Extract. Start Myth Engine, choose File › Open Project... and pick the .mythproj file in the new folder. Nunito-OFL.txt beside it comes with the game.
6.1 The Idea
Shoot at the right note now. The happy note settles softly against the tower, and nothing answers. The game knew it was the call. It said nothing.
This chapter gives that moment its sound. The note bursts into sparkles and plays a short rising run of its own pitch, its flourish. The world slows for half a second, and the tower comes down.
But not all of it. Hit a tower near the top, and only the top falls, but hit it low, and nearly all of it falls. The blocks underneath the place you struck keep standing.
Picture a line drawn across the tower in chalk, at the top of the block you hit. That’s the tower’s cut line. The block you hit comes loose, and so does every block from the line up. The blocks below it stay. A block that has come loose is falling. One that hasn’t is standing, and it doesn’t move at all. Each block decides for itself which it is, by looking at the line.
And when blocks fall, they knock. Wood sounds like wood, and stone like stone. The more blocks hit the grass close together, the bigger the crash.
6.2 The Plan
Here’s how the pieces work together. A name in bold is a piece you’ll make, and a name in plain text is a Custom Event.
- The happy note reaches the call -> Answer The Shot bursts it, plays its flourish with Play Flourish, and sends callFound -> Slow The Moment slows the world and shakes the camera.
- A shot at the call flies -> Fly sends shotNear to everything nearby, on every step -> each block’s Come Down When Struck asks: is it about to touch me? -> struck.
- A block is struck -> it draws its tower’s cut line in the Towers table -> towerCut.
- Every block hears towerCut -> those at or above the line come loose.
- On towerCut, Watch The Towers Fall sets
$towersFallingto true, until a while after the shot ends. Meanwhile a falling block knocks loose any standing block it hits hard, and everything above that block in its tower. - A loose block lands -> Fall With A Knock sends blockKnocked, and throws splinters or dust -> Sound The Fall plays the knock, and a crash as they pile up.
You’ll build it in that order: first the right note’s answer, then what every block knows about itself, then the fall, and last its sound.
6.3 Set Up
ART names four small pictures for the effects: a note glyph for the burst, a splinter, a stone chip and an ice shard. SOUNDS names twenty-seven flourishes, one for every pitch the game uses, then the three knocks and two crashes. The Starter Kit brought them all.
GLOBAL VARIABLES has two. $towersFalling is a Boolean that starts at false, so pick No in its Default list. The cards write a Boolean’s value as false or No, and both mean the same. $knockMaterial is Text that starts empty. Add them from the canvas’s Globals card, as you made $aimedPitch.
6.4 The Right Note Answers
This part brings the moment the player has waited for since Chapter 5, when the right note stayed silent.
6.4.1 Pitch, Changed
One pitch the game uses: its MIDI number and the Sounds that play it.
Every pitch gets a second Sound, its flourish. Add the field at the bottom of Pitch, flourishSound. Pick Sound under Things in its Type list, as you did for noteSound in Chapter 2. Its Value starts at None, the card’s default.
6.4.2 The Pitches Table, Changed
53Note F3Flourish F354Note F sharp3Flourish F sharp355Note G3Flourish G3The table has a new column now, and twenty-seven empty cells in it. You won’t fill them by hand. Download the card’s new file, pitch-shot-pitches-ch06.csv, and press Import CSV again.
Myth reads it as a merge. It finds each row by its name, F3 and the rest, and fills in what’s new. Its window counts 0 new rows and 27 existing rows. Press Import CSV, and each flourishSound cell finds its Sound, the way noteSound found its own.
6.4.3 Play Flourish
Play Flourish is Play Piano Note’s twin. It’s handed a pitch, finds that pitch’s row in the Pitches table, and plays the Sound in the flourishSound column. It sends noteSung too, so the place’s tune steps aside while the flourish plays.
Plays the short rising flourish of one pitch (a MIDI number), the sound of the called note found: the Sound its row of the Pitches table names.
- Action Name
pitchFound- Table
Pitches- Row
dataFind("Pitches", "midi", pitch)
pitchFound.value == true- Sound
data("Pitches", dataFind("Pitches", "midi", pitch), "flourishSound")
- Custom Event
noteSung
You’ve built this before. Make it the way you made Play Piano Note in Chapter 2, with the other column’s name in Control Sound.
6.4.4 Note Burst
10.06min 260, max 320min 0, max 0Smoke Puff#FFF6C8Normalstart 4.5, end 9start 0.8, end 0180.06min 350, max 600min 2.8, max 5.6#FFD23Fstart 1.44, end 0.3290.32min 700, max 1000min 2.2, max 4.6min 20, max 160x 0, y -7Star#FFCF33Normalstart 2.4, end 1.44start 0, end -36040.32min 900, max 1300min 0.9, max 1.8min 55, max 125x 0, y 0.4Note Glyph Art#FFFFFFNormalstart 2.08, end 1.76start 0, end -30The burst is four sprays at once. A soft white flash swells and fades, while golden sparks fly out in every direction. Stars tumble up and fall, turning. And four little white notes float gently upward, slower than everything else.
Each spray is an emitter. A new particle effect has one, Emitter 1. Press Add Emitter for each of the others, and click an emitter’s row to fill in its lines. On the card, each emitter’s name, such as Emitter 1, comes first. Under it are its three pages, Emitter, Motion and Appearance, so the word Emitter turns up twice in a row. A new emitter starts blank, so each one’s lines on the card are only what differs.
Particle Sprite is the list from Chapter 4. Smoke Puff and Star are in its first group, Built-in Particle Sprites. Note Glyph Art is in its second, your project’s own pictures.
6.4.5 Answer The Shot, Changed
When the shot aimed at this singing note reaches it, the note answers: a wrong note sings its own pitch, shakes and says so; the called note bursts and plays its flourish. It reads pitch (its Note State), set by Sing In The Opening, when the note hears "placed".
Any Objectself.noteStateself.answerTheShotData- Component Value
self.answerTheShotData.homeX- Value
self.transform.position.x
- Event
On Shot Arrived- Mode
Waiting
- Custom Event
shotArrived
- Custom Event
shotArrived
self.noteState.pitch != $calledPitch- Custom Event
shotArrived
- Custom Event
shotArrived
self.noteState.pitch == $aimedPitch- Event
On Shot Arrived- Mode
Answered
self.noteState.pitch != $calledPitch- pitch
self.noteState.pitch
- Component Value
self.answerTheShotData.shakeLeft- Value
@self.shakeSeconds
- Timer Name
"shake"- Repeat Every (s)
@self.shakeStepSeconds- First Delay (s)
0
- Custom Event
wrongNoteReached
- Particles
Note Burst- Position Relative To
World- X
self.transform.position.x- Y
self.transform.position.y- Z Index Offset
@self.burstLayer
- pitch
self.noteState.pitch
- Custom Event
callFound
- Component Value
self.answerTheShotData.shakeSide- Value
-self.answerTheShotData.shakeSide
- Component Value
self.answerTheShotData.shakeLeft- Operator
+=- Value
-@self.shakeStepSeconds
self.answerTheShotData.shakeLeft > 0- Value
self.transform.position.x- Target
self.answerTheShotData.homeX + self.answerTheShotData.shakeSide * @self.shakeMeters- Duration
@self.shakeStepSeconds
- Value
self.transform.position.x- Target
self.answerTheShotData.homeX- Duration
@self.shakeStepSeconds
- Timer Name
"shake"
In Chapter 5, the called note stayed silent. Now it has an Else. If the note is the call, it doesn’t shake. It bursts.
Burst Particles plays the Note Burst where the note sits, drawn in front of the tower at burstLayer, 60. Play Flourish plays its flourish, and callFound tells the game. Then Destroy Instance removes the note. It looks like the sparkles come out of the note. The note is already gone by the time you see most of them. A burst plays on by itself once it starts.
The Else at step 8 belongs to the inner If, at step 3. So right-click that If to add it, and build steps 9 to 12 under it.
One more change, in On Tower Struck. The called note doesn’t answer when the tower is struck any more. Its tower is about to come down, and the note waits until the happy note touches it, or the shot ends. So the old step goes, and an If takes its place: only a wrong note answers a strike.
6.4.6 Slow The Moment
When callFound comes, Slow The Moment slows the world and shakes the camera a little.
Set Time Flow sets how fast the whole game runs. At slowPace, 0.3, everything moves at under a third of its speed: the falling blocks, the sparks, the timers. It eases there in 0.05 seconds, holds it for slowSeconds, 0.55 seconds, and glides back to normal by itself.
Camera Shake shakes the view for shakeSeconds, 0.35. Its Intensity is a share of the screen, and 0.008 is a tremor, not an earthquake.
When the called note is found, slows the world for a moment and gives the camera a small shake.
Any Object- Speed
@self.slowPace- Ease Seconds
@self.easeSeconds- Hold Seconds
@self.slowSeconds
- Intensity
@self.shakeStrength- Seconds
@self.shakeSeconds
6.4.7 Game Rules, Changed
Sound The FallSound The Fall DataRepeat The CallRepeat The Call DataMake Way For The NotesMake Way For The Notes DataSinging NoteAttach Slow The Moment to Game Rules now. The other two come later in the chapter, once you’ve made them.
Run the game. Listen, and shoot at the call. The note bursts in gold and plays its flourish, and for a breath, everything slows. The tower still stands. That’s next.
6.5 What a Block Will Know
To fall well, a block needs to know three things about itself: which tower it’s in, what kind of block it is, and what it’s made of. Right now Build The Stage makes the blocks and forgets them. This part makes the forms and tables that will hold what each block knows. In the next part, every block gets its own copy, and Build The Stage fills it in.
Nothing new shows in this part. The towers start to fall in the next one.
6.5.1 Material and Block Kind, Changed
One material: the blocks a floor of it is made of, and the Sound of its knock.
A material gets a knock: the Sound a block of it makes when it lands. Add knockSound to Material, a Sound under Things. Its Value starts at None.
One kind of block: its size and its material.
A kind of block gets its material, as Text. Add material to Block Kind, at the bottom.
6.5.2 The Materials and Block Kinds Tables, Changed
Wood Post1Wood PlankSound WoodStone Cube2Stone PlankSound StoneIce Cube2Wood PlankSound IceEach material’s new knockSound cell takes its Sound from the cell’s list: Sound Wood, Sound Stone, Sound Ice.
0.241.12wood2.360.22wood0.560.56stone2.360.22stone0.560.56iceType each kind’s material into its new cell, all lowercase, as the Materials rows are named.
6.5.3 Block State and Tower
What the Logic of one block shares: which tower of the stage it belongs to, what kind of block it is (a row of the Block Kinds table), and whether it has come loose and is falling.
Block State is a small form every block will carry, like a name tag. Its tower counts from 0, the stage’s leftmost. Its kind is how it finds its own row in Block Kinds, and from there its size and material.
One tower of the stage being played: the height it is cut at (meters).
Tower is the chalk line’s home. Each tower of a stage gets one row, and its cut says how high the line is drawn.
6.5.4 The Towers Table
999999Make the Towers table with Tower as its Entry Type, and add its three rows. Their names count from 0, the way the towers do. No stage has more than three.
Type 99 into every cut cell. That’s far above the top of any tower, so a line drawn there cuts nothing. It’s how the table says a tower stands whole.
6.6 The Tower Comes Down
Now the fall itself. It’s the longest part of the chapter, and the heart of it. First comes the Logic that makes a block fall. Attached to each kind of block, it brings a Block State of its own. Then Build The Stage fills it in.
6.6.1 Come Down When Struck
Every block is standing when the stage begins. Fixed holds it still. Come Down When Struck is how a standing block lets go.
First, a block has to be struck. That sounds like a job for a Collision Enter, the Event that runs when two bodies touch, and partly it is. But a collision comes a moment too late. By the time Myth says two bodies have touched, the happy note has already bounced off. The block would come loose behind a note that’s already leaving.
So the block listens a moment early. A shot at the call sends shotNear, on every step, to everything within reach. Each block that hears it asks: where will the happy note be in a moment? It looks lookAheadSeconds ahead, 0.034 seconds, at the note’s speed. If the note will be within nearMeters, 0.33 meters, of the block’s edges, the block is struck. It lets go a moment before the touch, and the note flies on through. And if a shot at the call does touch a block before its warning came, the touch strikes it anyway. That’s the block’s Collision Enter with the Happy Note.
A falling block can strike too. When one block bumps another while towers are falling, faster than knockSpeed, 0.45 meters a second, the bumped block is struck. That’s how a tower knocks over the one beside it.
The struck block draws the line. Its top is the new cut line, but only if that’s lower than the tower’s cut already, so the lowest strike wins. It writes the line into the Towers table and sends towerCut to every block.
Every block of the stage hears towerCut, and each one looks at its own bottom. Is it at or above its tower’s line? Then it comes loose. The struck block’s own bottom is below the line, because the line is drawn at its top. So it remembers it was struck, in isStruck, and comes loose anyway.
A loose block sets isFalling, frees its body to fall, and tells itself cameLoose. Then it switches this Logic off. Falling blocks can’t be struck.
One more Event comes first on the card, before all of these. When placed arrives, the block reads its width and height from its row in Block Kinds. Place A Block sends placed, later in this part.
Come Down When Struck Data keeps the block’s size and whether it was struck. Make this form before the Logic, alongside the Block State you made above.
Object Data of Logic "Come Down When Struck" (its state that lasts between Events).
A block stands until a shot at the called note strikes it, or a falling block knocks into it while a tower is coming down. Then it cuts its tower at its top, and every block of that tower at or above the cut comes loose and falls. It reads tower and kind (its Block State), set by Place A Block, when it creates the block.
Any Objectself.blockStateself.comeDownWhenStruckDataCollider- Component Value
self.comeDownWhenStruckData.width- Value
data("Block Kinds", self.blockState.kind, "width")
- Component Value
self.comeDownWhenStruckData.height- Value
data("Block Kinds", self.blockState.kind, "height")
$aimedPitch == $calledPitch- Custom Event
struck
- Name
gapY- Value
0
- Name
gapX- Value
0
- Name
reachAhead- Value
0
- Name
otherSpeed- Value
0
- Variable
otherSpeed- Value
hypot(other.transform.velocity.x, other.transform.velocity.y)
- Variable
reachAhead- Value
@self.nearMeters + otherSpeed * @self.lookAheadSeconds
- Variable
gapX- Value
abs(other.transform.position.x - self.transform.position.x)
- Variable
gapY- Value
abs(other.transform.position.y - self.transform.position.y)
gapX < (self.comeDownWhenStruckData.width / 2 + reachAhead) && gapY < (self.comeDownWhenStruckData.height / 2 + reachAhead)- Custom Event
struck
- Name
otherSpeed- Value
0
- Variable
otherSpeed- Value
hypot(other.transform.velocity.x, other.transform.velocity.y)
$towersFalling == true && otherSpeed > @self.knockSpeed- Custom Event
struck
- Name
top- Value
0
- Component Value
self.comeDownWhenStruckData.isStruck- Value
true
- Variable
top- Value
self.transform.position.y + self.comeDownWhenStruckData.height / 2
top < data("Towers", self.blockState.tower, "cut")- Table
Towers- Row
self.blockState.tower- Column
"cut"- Value
top
- Custom Event
towerCut
- Name
bottom- Value
0
- Variable
bottom- Value
self.transform.position.y - self.comeDownWhenStruckData.height / 2
self.comeDownWhenStruckData.isStruck == true || bottom >= (data("Towers", self.blockState.tower, "cut") - @self.cutTolerance)- Component Value
self.blockState.isFalling- Value
true
- Component Value
self.collider.fixed- Value
false
- Component Value
self.transform.gravity- Value
true
- Custom Event
cameLoose
- Logic Component
Come Down When Struck- State
Disable
Make it with Add > Logic Component. Its Data Structures card lists Block State and Come Down When Struck Data. Add self.collider on Components. Its Shared Variables set how near, and how hard, a strike must be.
The Event supplies other, the Object that touched this block or sent the message. Events: the When bars shows it in the Collision paragraph. A Collision Enter has no speed of its own, so the block reads the speed of whatever touched it through other. In On Shot Near, other is the happy note that sent shotNear. In When Starting to Hit Anything Tagged Block, it’s the block that bumped this one. Pick Tagged Block at the end of the Collision Target list, under Tags.
shotNear travels by Fire Event In Radius, on Fly’s card, later in this part. On this card, a few lines need care:
- In On Struck, Set Data Cell writes the cut in this block’s tower row,
self.blockState.tower. Its Table list takes Towers, the table whose cut cells you filled in above. Its Column is"cut", with quotes. - The If in On Struck has no Else, and step 6 comes after it. Join that step on the Event’s main line, outside the If.
- struck tells this block to let go. It differs from Chapter 5’s towerStruck, which still tells the singing notes about a hit. towerCut tells every block to check its tower’s line.
- The cutTolerance, 0.02 meters, is a little slack for blocks that sit a hair below the line.
6.6.2 The Five Blocks, Changed
Attach Come Down When Struck to Wood Post, Wood Plank, Stone Cube, Stone Plank and Ice Cube. They already have Colliders. Each gains Block State and Come Down When Struck Data from the Logic.
X 0.24, Y 1.12On0.35X 0.24, Y 1.120.040.75On30Wood Post ArtX 0.24, Y 1.12BlockCome Down When Struck, Fall With A KnockBlock StateCome Down When StruckCome Down When Struck DataFall With A KnockFall With A Knock DataFor now, Block State’s From Logic names only Come Down When Struck. In the next part, Fall With A Knock joins that line when you attach it to each block. Its other Data Structure comes along too. Keep Tell Of A Strike, from Chapter 5, attached.
6.6.3 Place A Block, Changed
Creates one block of a tower: the block Object named kind, its middle at x, y (meters). The new block is told its tower and its kind, and "placed" tells it to read its size and material.
- Applies to
builder- Object to Create
kind- X
x- Y
y- Variable Name
newBlock
- Variable
newBlock.tower- Value
tower
- Variable
newBlock.kind- Value
kind
- Applies to
newBlock- Custom Event
placed
Every block carries a Block State now, so Place A Block can fill it in. It tells each new block who it is, the way Build The Stage tells a new note its row. It’s handed one more value, tower. After Create Instance, two Change Variables write the block’s tower and kind into its Block State, through newBlock. Then placed tells the block to read the rest for itself.
The created Object lets Change Variable reach Block State through newBlock.tower and newBlock.kind. Inside the block’s own Logic, those fields read through self.blockState. They reach the same form. For each Change Variable Value, pick the matching Function Parameter: tower or kind.
The kind is the block’s Object, such as Wood Post. Written into the Text field, it becomes its name, and that name is its row in Block Kinds.
6.6.4 Build A Floor and Build The Stage, Changed
Builds one floor of a tower from its material (a row of the Materials table): a support at each side, supportSpread from the middle of the tower, and a plank across them, standing on floorBottom (meters).
- Type
Text- Name
supportKind
- Name
supportHeight- Value
0
- Type
Text- Name
plankKind
- Name
plankHeight- Value
0
- Variable
supportKind- Value
data("Materials", material, "support")
- Variable
supportHeight- Value
data("Block Kinds", supportKind, "height")
data("Materials", material, "supportsHigh")times- x
towerX - supportSpread- y
floorBottom + supportHeight * (local.index + 0.5)- tower
tower- builder
builder- kind
supportKind
data("Materials", material, "supportsHigh")times- x
towerX + supportSpread- y
floorBottom + supportHeight * (local.index + 0.5)- tower
tower- builder
builder- kind
supportKind
- Variable
plankKind- Value
data("Materials", material, "plank")
- Variable
plankHeight- Value
data("Block Kinds", plankKind, "height")
- x
towerX- y
floorBottom + floorHeight - plankHeight / 2- tower
tower- builder
builder- kind
plankKind
Build A Floor gets tower too. It passes that number to all three of its Place A Block calls. Add tower to its Parameters on Start, then fill in the new tower box on each call.
When the Field starts, builds the stage being played from the Stages, Tower Plans, Floors and Notes tables: every tower, floor by floor from the ground up, and every singing note where its pitch puts it.
Any Object- Name
tower- Value
0
- Name
towerRow- Value
0
- Name
towerX- Value
0
- Name
floorRow- Value
0
- Name
floorBottom- Value
0
- Type
Text- Name
roofKind
- Name
row- Value
0
- Table
Towers
- Label
"Towers"- Color
#C27A3A
data("Stages", $stage - 1, "towerCount")times- Variable
tower- Value
local.index
- Variable
towerRow- Value
data("Stages", $stage - 1, "firstTower") + tower
- Variable
towerX- Value
data("Tower Plans", towerRow, "x")
- Variable
floorBottom- Value
@self.groundHeight
data("Tower Plans", towerRow, "floorCount")times- Variable
floorRow- Value
data("Tower Plans", towerRow, "firstFloor") + local.index
- tower
tower- towerX
towerX- floorBottom
floorBottom- floorHeight
@self.floorHeight- supportSpread
@self.supportSpread- builder
self- material
data("Floors", floorRow, "material")
- Variable
floorBottom- Operator
+=- Value
@self.floorHeight
- Action Name
roofFound- Table
Materials- Row
data("Tower Plans", towerRow, "roof")
roofFound.value == true- Variable
roofKind- Value
data("Materials", data("Tower Plans", towerRow, "roof"), "support")
- x
towerX - @self.roofSpread- y
floorBottom + data("Block Kinds", roofKind, "height") / 2- tower
tower- builder
self- kind
roofKind
- x
towerX + @self.roofSpread- y
floorBottom + data("Block Kinds", roofKind, "height") / 2- tower
tower- builder
self- kind
roofKind
- Label
"Singing Notes"- Color
#E8B923
data("Stages", $stage - 1, "noteCount")times- Variable
row- Value
data("Stages", $stage - 1, "firstNote") + local.index
- Action Name
noteSpot- noteRow
row- groundHeight
@self.groundHeight- floorHeight
@self.floorHeight- noteAboveFloor
@self.noteAboveFloor
- Table
Notes- Row
row- Column
"x"- Value
noteSpot.x
- Table
Notes- Row
row- Column
"y"- Value
noteSpot.y
- Object to Create
@self.singingNote- X
noteSpot.x- Y
noteSpot.y- Variable Name
newNote
- Variable
newNote.noteRow- Value
data("Stages", $stage - 1, "firstNote") + local.index
- Variable
newNote.turn- Value
local.index
- Applies to
newNote- Custom Event
placed
- Custom Event
stageBuilt
Build The Stage already counts its towers, in its tower Declare Variable. Now it hands that number to Build A Floor, and to the two Place A Blocks of a roof.
It also starts with a new step. Reset Data Table puts the Towers table back the way you made it, every cut at 99, before a single block is placed. The last stage may have cut a tower, and the running game remembers. This wipes the chalk lines off. Its Table list takes Towers, the table whose cut cells you filled in above.
Reset Data Table follows the declarations. Right-click the Repeat inside the Towers Region, and use Disconnect Incoming. Put Reset Data Table before that Repeat, as Actions, one at a time shows near its end.
Each block knows its place now. Two more pieces, Fly and Watch The Towers Fall, and the tower can come down.
6.6.5 Fly, Changed
Flings the happy note from the aim when it is launched, lets it fly, land and roll, and ends the shot when it has stood still for a moment, left the field or flown too long.
Any Objectself.shotSumsself.flyDataCollider- Logic Component
Pull Back- State
Disable
- Component Value
self.collider.fixed- Value
false
- Component Value
self.transform.gravity- Value
true
- X Velocity
$launchVX- Y Velocity
$launchVY
- groundHeight
@self.groundHeight- fieldLeft
@self.fieldLeft- fieldRight
@self.fieldRight- shot
self
$aimedPitch == $calledPitch- Value
@self.hitBounce
- Event
Every Step- Mode
Hard
- Value
@self.springBackBounce
- Sound
Sound Launch
- Timer Name
"watchFlight"- Repeat Every (s)
@self.checkSeconds- First Delay (s)
@self.checkSeconds
- Event
shotNear- Radius
@self.nearRadius
- Name
speed- Value
0
- Component Value
self.flyData.flightSeconds- Operator
+=- Value
@self.checkSeconds
- Variable
speed- Value
hypot(self.transform.velocity.x, self.transform.velocity.y)
speed < @self.stillSpeed- Component Value
self.flyData.stillFor- Operator
+=- Value
@self.checkSeconds
- Component Value
self.flyData.stillFor- Value
0
self.transform.position.y < @self.rollingHeight- Amount
@self.rollingSlowdown
self.flyData.stillFor >= @self.stillSeconds || self.flyData.flightSeconds >= @self.longestFlightSeconds || self.transform.position.x < @self.fieldLeft || self.transform.position.x > @self.fieldRight || self.transform.position.y < @self.fieldBottom- Timer Name
"watchFlight"
- Particles
Landing Dust- Position Relative To
World- X
clamp(self.transform.position.x, @self.screenLeft, @self.screenRight)- Y
max(self.transform.position.y - @self.dustBelowNote, @self.groundHeight)- Z Index Offset
@self.dustLayer
- Custom Event
shotEnded
Fly decides who gets shotNear. A shot at the call does. Every other shot springs back off the towers, so the blocks never need to hear it.
So Fly’s Step has two Modes. Gentle, the one it starts with, does nothing. Hard sends shotNear on every step, to everything within nearRadius, 1.2 meters. In On Happy Note Launched, the If that picks hitBounce now also switches the Step to Hard.
Build the Step Event first, with Gentle and then Hard, as Modes shows. Add step 8 inside Then, after step 7. Add nearRadius to the Shared Variables too. Hard’s one Action is Fire Event In Radius, the shout that every block within reach hears.
6.6.6 Watch The Towers Fall
A falling block knocks a standing one loose only while towers are falling. Watch The Towers Fall keeps track of that, in $towersFalling.
It’s false when the Field starts. The first towerCut makes it true. It stays true while the shot plays out, and for settleSeconds after the shot ends, 2.5 seconds, while the last blocks tumble. Then a timer sets it back to false.
Keeps $towersFalling Yes from the moment a tower is cut until settleSeconds after the shot ends: while it is Yes, falling blocks knock standing blocks loose.
Any Object- Variable
$towersFalling- Value
false
- Variable
$towersFalling- Value
true
- Timer Name
"settle"
$towersFalling == true- Timer Name
"settle"- Repeat Every (s)
@self.settleSeconds- Ring Once
Yes
- Variable
$towersFalling- Value
false
If another tower is cut in that time, its towerCut stops the settle timer, and the next shotEnded starts it over. Its Start Recurring Timer rings once, so pick Yes in Ring Once.
Attach it to Game Rules.
Run the game, and shoot at the call. The note bursts, the world slows, and the tower lets go. Planks tumble, posts topple, and the blocks below the line stay where they stood. It falls in silence, for now.
6.7 Knocks, Splinters and the Crash
The tower falls, but it doesn’t make a sound, so this part gives every landing its knock.
6.7.1 Wood Splinters, Stone Dust and Ice Shatter
60.192min 400, max 700min 1.2, max 3min 10, max 170x 0, y -12Splinter Art#FFFFFFNormalstart 1.28, end 1.12start 0, end -72030.06min 350, max 550min 0.2, max 0.7min 20, max 160Smoke Puff#D9B98ANormalstart 1.92, end 3.84start 0.45, end 0Make the three, as you made the Note Burst. Each has two emitters: chips that fly and spin, and a soft puff. Splinter Art, Stone Chip Art and Ice Shard Art are in the Particle Sprite list’s second group.
60.192min 400, max 700min 1.1, max 2.6min 10, max 170x 0, y -13Stone Chip Art#FFFFFFNormalstart 0.72, end 0.64start 0, end -54050.06min 450, max 700min 0.2, max 0.8min 10, max 170x 0, y 0.15Smoke Puff#B9BEC6Normalstart 2.24, end 4.8start 0.5, end 090.288min 500, max 850min 1.5, max 4.2min 0, max 180x 0, y -11Ice Shard Art#FFFFFFNormalstart 1.76, end 1.28start 0, end -540100.06min 200, max 380min 1.2, max 3.2#DFF6FFstart 1.12, end 0.166.7.2 Fall With A Knock
Once a block has come loose, Fall With A Knock watches it fall. Its question is simple to ask and harder to answer: when did it land?
A Collision Enter won’t say how hard two things met. So the block watches its own speed instead, every step, while it’s falling. A block in the air keeps getting faster. Then it hits something, and in one step most of its speed is gone. That sudden loss is a landing.
So the Step remembers two speeds, lastSpeed and speed. If the block was going faster than landingSpeed, 2.4 meters a second, and lost more than landingDrop, 2.2, in one step, it sends itself landed. And a block that tumbles out of the field removes itself.
The Step has two Modes, like Fly’s. Standing, the one it starts with, does nothing, and Falling does the watching. The message cameLoose switches it on. Come Down When Struck has been sending it since the last part, with nobody listening.
When a block lands, it knocks. It sets $knockMaterial to what it’s made of and sends blockKnocked. A Custom Event carries no values, so the material waits in the Global for whoever hears the knock. A hard landing, one that loses more than hardDrop, 4.2, throws Wood Splinters from wood and Stone Dust from stone.
Ice is different. Ice that has come loose doesn’t only knock. Hit it hard, while the tower is falling, and it shatters into shards, knocks as ice, and is gone. A hard landing does it, and so does a hard hit from the happy note or another block. Each has its own speed among the Shared Variables. Standing ice never shatters. It has to be falling first.
Fall With A Knock Data keeps the material and the two speeds from one step to the next. Make it first, ready to add beside Block State on the Logic’s Data Structures card.
Object Data of Logic "Fall With A Knock" (its state that lasts between Events).
Once a block has come loose: it knocks when it lands (wood, stone or ice), throws splinters or dust when it lands hard, and a block of ice shatters when it is hit or lands hard while a tower is coming down. A block that falls out of the field is removed. It reads kind and isFalling (its Block State), set by Place A Block (kind, when it creates the block) and Come Down When Struck (isFalling), on the same block.
Any Objectself.blockStateself.fallWithAKnockDataCollider- Component Value
self.fallWithAKnockData.material- Value
data("Block Kinds", self.blockState.kind, "material")
- Event
Every Step- Mode
Falling
- Component Value
self.fallWithAKnockData.lastSpeed- Value
self.fallWithAKnockData.speed
- Component Value
self.fallWithAKnockData.speed- Value
hypot(self.transform.velocity.x, self.transform.velocity.y)
self.fallWithAKnockData.lastSpeed > @self.landingSpeed && (self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.landingDrop- Custom Event
landed
self.transform.position.y < @self.fieldBottom || self.transform.position.x < @self.fieldLeft || self.transform.position.x > @self.fieldRightself.fallWithAKnockData.material == "ice" && $towersFalling == true && self.fallWithAKnockData.lastSpeed > @self.iceLandingSpeed && (self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.iceLandingDrop- Custom Event
shatter
- Variable
$knockMaterial- Value
self.fallWithAKnockData.material
- Custom Event
blockKnocked
(self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.hardDrop && self.fallWithAKnockData.material == "wood"- Particles
Wood Splinters- Position Relative To
World- X
self.transform.position.x- Y
self.transform.position.y- Z Index Offset
@self.effectLayer
(self.fallWithAKnockData.lastSpeed - self.fallWithAKnockData.speed) > @self.hardDrop && self.fallWithAKnockData.material == "stone"- Particles
Stone Dust- Position Relative To
World- X
self.transform.position.x- Y
self.transform.position.y- Z Index Offset
@self.effectLayer
- Name
otherSpeed- Value
0
- Variable
otherSpeed- Value
hypot(other.transform.velocity.x, other.transform.velocity.y)
self.fallWithAKnockData.material == "ice" && self.blockState.isFalling == true && $towersFalling == true && otherSpeed > @self.iceShotSpeed- Custom Event
shatter
- Name
otherSpeed- Value
0
- Variable
otherSpeed- Value
hypot(other.transform.velocity.x, other.transform.velocity.y)
self.fallWithAKnockData.material == "ice" && self.blockState.isFalling == true && $towersFalling == true && otherSpeed > @self.iceKnockSpeed- Custom Event
shatter
- Particles
Ice Shatter- Position Relative To
World- X
self.transform.position.x- Y
self.transform.position.y- Z Index Offset
@self.effectLayer
- Variable
$knockMaterial- Value
'ice'
- Custom Event
blockKnocked
Make it with Block State and Fall With A Knock Data on its Data Structures card, and self.collider on Components. The Event supplies other, as before. Build the Step and its two Modes before On Came Loose, because its Switch Mode picks Falling from a list.
In On Landed, the Else at step 3 belongs to the first If. Under it, the If at step 8 comes after the If at step 6, not inside it, so click the If at step 6 before you add it.
In On Shatter, the card writes ‘ice’ in single quote marks. Type it as the card shows it. Single and double quote marks do the same job, and once you’ve typed it, the box shows "ice" in double ones. That’s the same word.
Attach Fall With A Knock to each of the five blocks. Block State is already there. Fall With A Knock brings its own Data Structure too, and the block’s three Logic Components are complete.
6.7.3 Sound The Fall
Every blockKnocked comes to Sound The Fall, on Game Rules. It does two jobs: the knock, and the crash.
The knock comes first. When a whole tower lands at once, a dozen knocks can come in the same instant, and together they’d be a roar. So the knocks take turns, never closer than knockGapSeconds, 0.045 seconds. That’s a cooldown, a named stopwatch. Check Cooldown Ready asks whether the stopwatch called knock has run out, and its answer is knockReady. If it has, Start Cooldown starts it again, and the knock plays. Its Sound is the knockSound in the Materials row that $knockMaterial names.
The crash is a bucket. Every knock pours one in, and the bucket leaks steadily, fadePerSecond, 2.5 a second, on a timer. When it holds smallCrashKnocks, 3, Sound Crash Small plays. When it holds bigCrashKnocks, 8, Sound Crash Big plays, once each. When it has drained below quietKnocks, 0.5, the timer stops, and the next fall can crash again.
Sound The Fall Data holds the bucket and which crash has played. Make this form before the Logic, so the next knock can remember the last.
Object Data of Logic "Sound The Fall" (its state that lasts between Events).
Plays the knock of each block that lands, the Sound its material's row of the Materials table names (never two closer than knockGapSeconds), and a crash that grows with the fall: a small crash once enough knocks come close together, a big crash if many more do.
Any Objectself.soundTheFallData- Component Value
self.soundTheFallData.crash- Operator
+=- Value
1
- Timer Name
"crashFade"- Repeat Every (s)
@self.fadeStepSeconds- First Delay (s)
@self.fadeStepSeconds
- Action Name
knockReady- Cooldown Key
"knock"
knockReady.value == true- Cooldown Key
"knock"- Seconds
@self.knockGapSeconds
- Action Name
materialFound- Table
Materials- Row
$knockMaterial
materialFound.value == true- Sound
data("Materials", $knockMaterial, "knockSound")
- Component Value
self.soundTheFallData.crash- Value
max(0, self.soundTheFallData.crash - @self.fadePerSecond * @self.fadeStepSeconds)
self.soundTheFallData.crash >= @self.bigCrashKnocks && self.soundTheFallData.crashLevel < 2- Component Value
self.soundTheFallData.crashLevel- Value
2
- Sound
Sound Crash Big
self.soundTheFallData.crash >= @self.smallCrashKnocks && self.soundTheFallData.crashLevel < 1- Component Value
self.soundTheFallData.crashLevel- Value
1
- Sound
Sound Crash Small
self.soundTheFallData.crash < @self.quietKnocks- Component Value
self.soundTheFallData.crashLevel- Value
0
- Timer Name
"crashFade"
Its Shared Variables set the timing. They decide when a knock can play, and how fast the bucket drains. Add them in the card’s order.
Both cooldown blocks have a Cooldown Key box, which takes "knock", quote marks and all. In On Block Knocked, the If at step 4 comes after step 3, and everything after it sits inside it. In the timer’s Event, the three Ifs come one after another, so click each If before you add the next.
Attach Sound The Fall to Game Rules, and the Game Rules card is complete.
Run the game. Shoot at the call. The note bursts, the world slows, and the tower comes down knocking. Wood on wood, a spray of splinters as a plank lands hard, and the crash rolling up as the blocks pile onto the grass.
Listen, and watch, for each of these.
- Shoot at the call. The note bursts into a white flash, golden sparks, tumbling stars and little white notes, and you hear its flourish, a short run rising to its pitch.
- In that moment, everything slows to under a third of its speed for about half a second, and the view trembles.
- The tower lets go from the block you hit, upward. Hit the top floor, and only the top falls. Blocks below the line stay standing.
- The falling blocks knock as they land, each knock a little apart from the last, never all at once.
- A plank that lands hard throws splinters, and a big fall ends in a crash.
- Shoot at a wrong note. It shakes and sings, as in Chapter 5, and the tower doesn’t move.
- After the call is found, the next happy note loads, and the rubble stays where it fell. Nothing wins the stage yet.
The right note bursts, plays its flourish and calls callFound, and the world slows for a breath. The shot at the call warns every block it nears. A struck block draws its tower’s cut line, and every block from the line up lets go. Loose blocks watch their own speed, and a sudden loss is a landing. Each landing knocks. The knocks fill a crash that drains away.
Underneath it is one idea: every block decides for itself. Nothing tells the tower to fall. Each block knows its tower, its kind and its material, hears towerCut, and looks at its own bottom against the line. A whole tower can come down together, and not one block needs to know about the others.
6.8 If It Didn’t Work
This chapter has a lot of moving parts, and a slip in any of them shows up as something that doesn’t happen. Watch which moment goes missing, and you’ll know where to look.
The right note never bursts? Look at Answer The Shot’s Else, at step 8. It belongs to the If at step 3, the one that compares the note with $calledPitch.
It bursts, but no flourish? Look at the Pitches table. Do its flourishSound cells name Sounds, or are they still None? If they’re still None, import pitch-shot-pitches-ch06.csv again.
The tower never falls? Perhaps no block hears shotNear. Look at step 8 of Fly’s On Happy Note Launched, which should switch Every Step to Hard. And is Come Down When Struck attached to each of the five blocks, each with a Collider?
The whole tower falls, every time, wherever you hit it? Look at On Struck’s If. Is it top less than the cut, with <? And does Reset Data Table run before Build The Stage makes any blocks, so every cut starts at 99?
Blocks fall, but nothing knocks? Is Fall With A Knock on each block, and does On Came Loose switch Every Step to Falling? Or perhaps the Materials table’s knockSound cells are still None.
Ice never shatters? It shatters only while a tower is falling, and the first ice stands in stage 32. Look at the Ice Cube’s row in Block Kinds, where its material should be ice, and then at On Shatter’s "ice".
Each of these is one small fix. Make it, run the game, and shoot again. And if you’d like to compare, the catch-up download in the Goal is this chapter’s game, finished.
The fall is yours to tune. Under Variables on Game Rules, change slowPace from 0.3 to 0.1, and the moment hangs in the air. Change bigCrashKnocks there from 8 to 4, and even a small tower ends in the big crash. Or try other stages. Open $stage’s pencil on the Globals card, and change its Default to 12 for the first stone tower, or to 32 for the first ice, and listen to them fall. Which sound do you want to hear again? Put everything back before Chapter 7, so your game matches the next chapter’s cards.
Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).
The right note answers, and its tower falls. In Chapter 7, finding it wins the stage.
