Build a GameCreating Pitch Shot

6  The Tower Comes Down

Goal

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.

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

To open it, right-click the downloaded file, choose Extract All... and then Extract. Start Myth Engine, choose File › Open Project... and pick the .mythproj file in the new folder. Nunito-OFL.txt beside it comes with the game.

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 $towersFalling to 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

Set upWhat this chapter adds
ART (sprites, from the starter kit)
Note Glyph Art
Splinter Art
Stone Chip Art
Ice Shard Art
SOUNDS (from the starter kit)
Flourish F3
Flourish F sharp3
Flourish G3
Flourish A flat3
Flourish A3
Flourish B flat3
Flourish B3
Flourish C4
Flourish C sharp4
Flourish D4
Flourish E flat4
Flourish E4
Flourish F4
Flourish F sharp4
Flourish G4
Flourish A flat4
Flourish A4
Flourish B flat4
Flourish B4
Flourish C5
Flourish C sharp5
Flourish D5
Flourish E flat5
Flourish E5
Flourish F5
Flourish F sharp5
Flourish G5
Sound Wood
Sound Stone
Sound Ice
Sound Crash Small
Sound Crash Big
GLOBAL VARIABLES
$towersFallingBooleanstarts at false
$knockMaterialTextstarts at

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

Data StructurePitchAddDelete

One pitch the game uses: its MIDI number and the Sounds that play it.

midiNumberdefault 60
noteSoundThing Sounddefault None
flourishSoundThing Sounddefault None

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

Data TablePitchesData Structure: PitchAddDelete
IMPORT CSV27 rows, from a file
FIRST ROWS3 of 27, to compare
Row 0F3
midi53
noteSoundNote F3
flourishSoundFlourish F3
Row 1F sharp3
midi54
noteSoundNote F sharp3
flourishSoundFlourish F sharp3
Row 2G3
midi55
noteSoundNote G3
flourishSoundFlourish G3

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

FunctionPlay Flourish

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.

PARAMETERS
NameTypeStarts at
pitchNumber60
ACTIONS
1Check Data Row Exists
Action Name
pitchFound
Table
Pitches
Row
dataFind("Pitches", "midi", pitch)
2IfpitchFound.value == true
Then
3Control Sound
Sound
data("Pitches", dataFind("Pitches", "midi", pitch), "flourishSound")
4Trigger Custom Event
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

Particle EffectNote Burst
Emitter 1
Emitter
Emission > Burst Count1
Emitter Shape > Emit Radius (m)0.06
Motion
Lifecycle > Each Particle Lives (ms)min 260, max 320
Motion > Speed (m/s)min 0, max 0
Appearance
Appearance > Particle SpriteSmoke Puff
Appearance > Color#FFF6C8
Appearance > BlendNormal
Appearance Over Life > Scalestart 4.5, end 9
Appearance Over Life > Alphastart 0.8, end 0
Emitter 2
Emitter
Emission > Burst Count18
Emitter Shape > Emit Radius (m)0.06
Motion
Lifecycle > Each Particle Lives (ms)min 350, max 600
Motion > Speed (m/s)min 2.8, max 5.6
Appearance
Appearance > Color#FFD23F
Appearance Over Life > Scalestart 1.44, end 0.32
Emitter 3
Emitter
Emission > Burst Count9
Emitter Shape > Emit Radius (m)0.32
Motion
Lifecycle > Each Particle Lives (ms)min 700, max 1000
Motion > Speed (m/s)min 2.2, max 4.6
Motion > Angle (Deg)min 20, max 160
Forces > Gravityx 0, y -7
Appearance
Appearance > Particle SpriteStar
Appearance > Color#FFCF33
Appearance > BlendNormal
Appearance Over Life > Scalestart 2.4, end 1.44
Appearance Over Life > Rotatestart 0, end -360
Emitter 4
Emitter
Emission > Burst Count4
Emitter Shape > Emit Radius (m)0.32
Motion
Lifecycle > Each Particle Lives (ms)min 900, max 1300
Motion > Speed (m/s)min 0.9, max 1.8
Motion > Angle (Deg)min 55, max 125
Forces > Gravityx 0, y 0.4
Appearance
Appearance > Particle SpriteNote Glyph Art
Appearance > Color#FFFFFF
Appearance > BlendNormal
Appearance Over Life > Scalestart 2.08, end 1.76
Appearance Over Life > Rotatestart 0, end -30

The 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

Logic ComponentAnswer The ShotAttach to: Singing NoteAddDelete

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

Attach toAny Object
DATA STRUCTURES
Note Stateself.noteState
Answer The Shot Dataself.answerTheShotData
SHARED VARIABLES
NameTypeKindStarting Value
shakeMetersNumberConstant0.06
shakeSecondsNumberConstant0.5
shakeStepSecondsNumberConstant0.05
burstLayerNumberConstant60
When Created
WhenCreate
1Change Component Value
Component Value
self.answerTheShotData.homeX
Value
self.transform.position.x
On Happy Note Launched
WhenhappyNoteLaunchedCustom Event
1Switch Mode
Event
On Shot Arrived
Mode
Waiting
When Starting to Hit Happy Note
WhenCollision EnterObject: Happy Note
1Trigger Custom Event on Object
Custom Event
shotArrived
On Tower Struck
WhentowerStruckCustom Event
1Trigger Custom Event on Object
Custom Event
shotArrived
1Ifself.noteState.pitch != $calledPitch
Then
2Trigger Custom Event on Object
Custom Event
shotArrived
On Shot Ended
WhenshotEndedCustom Event
1Trigger Custom Event on Object
Custom Event
shotArrived
On Shot Arrived
WhenshotArrivedCustom Event
ModeWaiting (Starting Mode)
1Ifself.noteState.pitch == $aimedPitch
Then
2Switch Mode
Event
On Shot Arrived
Mode
Answered
3Ifself.noteState.pitch != $calledPitch
Then
4Play Piano NoteFunction
pitch
self.noteState.pitch
5Change Component Value
Component Value
self.answerTheShotData.shakeLeft
Value
@self.shakeSeconds
6Start Recurring Timer
Timer Name
"shake"
Repeat Every (s)
@self.shakeStepSeconds
First Delay (s)
0
7Trigger Custom Event
Custom Event
wrongNoteReached
8Otherwise
9Burst Particles
Particles
Note Burst
Position Relative To
World
X
self.transform.position.x
Y
self.transform.position.y
Z Index Offset
@self.burstLayer
10Play FlourishFunction
pitch
self.noteState.pitch
11Trigger Custom Event
Custom Event
callFound
12Destroy Instance
On Shot Arrived
WhenshotArrivedCustom Event
ModeAnswered
(no Actions)
On the Shake Timer
WhenRecurring TimerTimer name: shake
1Change Component Value
Component Value
self.answerTheShotData.shakeSide
Value
-self.answerTheShotData.shakeSide
2Change Component Value
Component Value
self.answerTheShotData.shakeLeft
Operator
+=
Value
-@self.shakeStepSeconds
3Ifself.answerTheShotData.shakeLeft > 0
Then
4Tween
Value
self.transform.position.x
Target
self.answerTheShotData.homeX + self.answerTheShotData.shakeSide * @self.shakeMeters
Duration
@self.shakeStepSeconds
5Otherwise
6Tween
Value
self.transform.position.x
Target
self.answerTheShotData.homeX
Duration
@self.shakeStepSeconds
7Stop Recurring Timer
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.

Logic ComponentSlow The MomentAttach to: Game Rules

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

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
slowPaceNumberConstant0.3
easeSecondsNumberConstant0.05
slowSecondsNumberConstant0.55
shakeStrengthNumberConstant0.008
shakeSecondsNumberConstant0.35
On Call Found
WhencallFoundCustom Event
1Set Time Flow
Speed
@self.slowPace
Ease Seconds
@self.easeSeconds
Hold Seconds
@self.slowSeconds
2Camera Shake
Intensity
@self.shakeStrength
Seconds
@self.shakeSeconds

6.4.7 Game Rules, Changed

ObjectGame RulesAddDelete
Data Structure
From LogicSound The Fall
Data StructureSound The Fall Data
Data Structure
From LogicRepeat The Call
Data StructureRepeat The Call Data
Data Structure
From LogicMake Way For The Notes
Data StructureMake Way For The Notes Data
Logic
Build The Stage
Sound The Fall
Slow The Moment
Watch The Towers Fall
Call A Note
Repeat The Call
Play The Place's Tune
Make Way For The Notes
Count The Shots
Ask Again
Shared Variables
singingNoteSinging Note

Attach 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

Data StructureMaterialAddDelete

One material: the blocks a floor of it is made of, and the Sound of its knock.

supportThing Objectdefault None
supportsHighNumberdefault 0
plankThing Objectdefault None
knockSoundThing Sounddefault None

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.

Data StructureBlock KindAddDelete

One kind of block: its size and its material.

widthNumberdefault 0
heightNumberdefault 0
materialTextdefault (empty)

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

Data TableMaterialsData Structure: MaterialAddDelete
Row 0wood
supportWood Post
supportsHigh1
plankWood Plank
knockSoundSound Wood
Row 1stone
supportStone Cube
supportsHigh2
plankStone Plank
knockSoundSound Stone
Row 2ice
supportIce Cube
supportsHigh2
plankWood Plank
knockSoundSound Ice

Each material’s new knockSound cell takes its Sound from the cell’s list: Sound Wood, Sound Stone, Sound Ice.

Data TableBlock KindsData Structure: Block KindAddDelete
Row 0Wood Post
width0.24
height1.12
materialwood
Row 1Wood Plank
width2.36
height0.22
materialwood
Row 2Stone Cube
width0.56
height0.56
materialstone
Row 3Stone Plank
width2.36
height0.22
materialstone
Row 4Ice Cube
width0.56
height0.56
materialice

Type each kind’s material into its new cell, all lowercase, as the Materials rows are named.

6.5.3 Block State and Tower

Data StructureBlock State

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.

towerNumberdefault 0
kindTextdefault (empty)
isFallingBooleandefault false

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.

Data StructureTower

One tower of the stage being played: the height it is cut at (meters).

cutNumberdefault 0

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

Data TableTowersData Structure: Tower
Row 0Tower 0
cut99
Row 1Tower 1
cut99
Row 2Tower 2
cut99

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

Data StructureCome Down When Struck Data

Object Data of Logic "Come Down When Struck" (its state that lasts between Events).

widthNumberdefault 0
heightNumberdefault 0
isStruckBooleandefault false
Logic ComponentCome Down When StruckAttach to: Wood Post, Wood Plank, Stone Cube, Stone Plank, Ice Cube

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.

Attach toAny Object
DATA STRUCTURES
Block Stateself.blockState
Come Down When Struck Dataself.comeDownWhenStruckData
COMPONENTS
self.colliderCollider
SHARED VARIABLES
NameTypeKindStarting Value
knockSpeedNumberConstant0.45
cutToleranceNumberConstant0.02
nearMetersNumberConstant0.33
lookAheadSecondsNumberConstant0.034
On Placed
WhenplacedCustom Event
1Change Component Value
Component Value
self.comeDownWhenStruckData.width
Value
data("Block Kinds", self.blockState.kind, "width")
2Change Component Value
Component Value
self.comeDownWhenStruckData.height
Value
data("Block Kinds", self.blockState.kind, "height")
When Starting to Hit Happy Note
WhenCollision EnterObject: Happy Note
1If$aimedPitch == $calledPitch
Then
2Trigger Custom Event on Object
Custom Event
struck
On Shot Near
WhenshotNearCustom Event
1Declare Variable
Name
gapY
Value
0
2Declare Variable
Name
gapX
Value
0
3Declare Variable
Name
reachAhead
Value
0
4Declare Variable
Name
otherSpeed
Value
0
5Change Variable
Variable
otherSpeed
Value
hypot(other.transform.velocity.x, other.transform.velocity.y)
6Change Variable
Variable
reachAhead
Value
@self.nearMeters + otherSpeed * @self.lookAheadSeconds
7Change Variable
Variable
gapX
Value
abs(other.transform.position.x - self.transform.position.x)
8Change Variable
Variable
gapY
Value
abs(other.transform.position.y - self.transform.position.y)
9IfgapX < (self.comeDownWhenStruckData.width / 2 + reachAhead) && gapY < (self.comeDownWhenStruckData.height / 2 + reachAhead)
Then
10Trigger Custom Event on Object
Custom Event
struck
When Starting to Hit Anything Tagged Block
WhenCollision EnterObject: tag:Block
1Declare Variable
Name
otherSpeed
Value
0
2Change Variable
Variable
otherSpeed
Value
hypot(other.transform.velocity.x, other.transform.velocity.y)
3If$towersFalling == true && otherSpeed > @self.knockSpeed
Then
4Trigger Custom Event on Object
Custom Event
struck
On Struck
WhenstruckCustom Event
1Declare Variable
Name
top
Value
0
2Change Component Value
Component Value
self.comeDownWhenStruckData.isStruck
Value
true
3Change Variable
Variable
top
Value
self.transform.position.y + self.comeDownWhenStruckData.height / 2
4Iftop < data("Towers", self.blockState.tower, "cut")
Then
5Set Data Cell
Table
Towers
Row
self.blockState.tower
Column
"cut"
Value
top
6Trigger Custom Event
Custom Event
towerCut
On Tower Cut
WhentowerCutCustom Event
1Declare Variable
Name
bottom
Value
0
2Change Variable
Variable
bottom
Value
self.transform.position.y - self.comeDownWhenStruckData.height / 2
3Ifself.comeDownWhenStruckData.isStruck == true || bottom >= (data("Towers", self.blockState.tower, "cut") - @self.cutTolerance)
Then
4Change Component Value
Component Value
self.blockState.isFalling
Value
true
5Change Component Value
Component Value
self.collider.fixed
Value
false
6Change Component Value
Component Value
self.transform.gravity
Value
true
7Trigger Custom Event on Object
Custom Event
cameLoose
8Set Logic State
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.

ObjectWood PostAddDelete
Transform
Bounding Box > SizeX 0.24, Y 1.12
Physics > Allow RotationOn
Physics > Spin Drag0.35
Collider
Shape and Transform > SizeX 0.24, Y 1.12
Physics > Bounciness (0–1)0.04
Physics > Friction (Grip)0.75
Fixed (no editor field)On
Sprite
Scene Depth Draw Order30
SpriteWood Post Art
Transform > SizeX 0.24, Y 1.12
Tags
Object TagsBlock
Data Structure
From LogicCome Down When Struck, Fall With A Knock
Data StructureBlock State
Data Structure
From LogicCome Down When Struck
Data StructureCome Down When Struck Data
Data Structure
From LogicFall With A Knock
Data StructureFall With A Knock Data
Logic
Come Down When Struck
Fall With A Knock
Tell Of A Strike

For 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

FunctionPlace A BlockAddDelete

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.

PARAMETERS
NameTypeStarts at
builderObject
kindThing ObjectWood Post
xNumber0
yNumber0
towerNumber0
ACTIONS
1Create Instance
Applies to
builder
Object to Create
kind
X
x
Y
y
Variable Name
newBlock
2Change Variable
Variable
newBlock.tower
Value
tower
3Change Variable
Variable
newBlock.kind
Value
kind
4Trigger Custom Event on Object
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

FunctionBuild A FloorAddDelete

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

PARAMETERS
NameTypeStarts at
builderObject
towerNumber0
towerXNumber0
floorBottomNumber0
materialTextwood
floorHeightNumber1.34
supportSpreadNumber0.94
ACTIONS
1Declare Variable
Type
Text
Name
supportKind
2Declare Variable
Name
supportHeight
Value
0
3Declare Variable
Type
Text
Name
plankKind
4Declare Variable
Name
plankHeight
Value
0
5Change Variable
Variable
supportKind
Value
data("Materials", material, "support")
6Change Variable
Variable
supportHeight
Value
data("Block Kinds", supportKind, "height")
7Repeatdata("Materials", material, "supportsHigh")times
Each time
8Place A BlockFunction
x
towerX - supportSpread
y
floorBottom + supportHeight * (local.index + 0.5)
tower
tower
builder
builder
kind
supportKind
9Repeatdata("Materials", material, "supportsHigh")times
Each time
10Place A BlockFunction
x
towerX + supportSpread
y
floorBottom + supportHeight * (local.index + 0.5)
tower
tower
builder
builder
kind
supportKind
11Change Variable
Variable
plankKind
Value
data("Materials", material, "plank")
12Change Variable
Variable
plankHeight
Value
data("Block Kinds", plankKind, "height")
13Place A BlockFunction
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.

Logic ComponentBuild The StageAttach to: Game RulesAddDelete

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.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
singingNoteObjectConstantNone
groundHeightNumberConstant1.3
floorHeightNumberConstant1.34
supportSpreadNumberConstant0.94
roofSpreadNumberConstant0.6
noteAboveFloorNumberConstant0.56
When the Scene Starts
WhenScene Start
1Declare Variable
Name
tower
Value
0
2Declare Variable
Name
towerRow
Value
0
3Declare Variable
Name
towerX
Value
0
4Declare Variable
Name
floorRow
Value
0
5Declare Variable
Name
floorBottom
Value
0
6Declare Variable
Type
Text
Name
roofKind
7Declare Variable
Name
row
Value
0
8Reset Data Table
Table
Towers
9Region
Label
"Towers"
Color
#C27A3A
10Repeatdata("Stages", $stage - 1, "towerCount")times
Each time
11Change Variable
Variable
tower
Value
local.index
12Change Variable
Variable
towerRow
Value
data("Stages", $stage - 1, "firstTower") + tower
13Change Variable
Variable
towerX
Value
data("Tower Plans", towerRow, "x")
14Change Variable
Variable
floorBottom
Value
@self.groundHeight
15Repeatdata("Tower Plans", towerRow, "floorCount")times
Each time
16Change Variable
Variable
floorRow
Value
data("Tower Plans", towerRow, "firstFloor") + local.index
17Build A FloorFunction
tower
tower
towerX
towerX
floorBottom
floorBottom
floorHeight
@self.floorHeight
supportSpread
@self.supportSpread
builder
self
material
data("Floors", floorRow, "material")
18Change Variable
Variable
floorBottom
Operator
+=
Value
@self.floorHeight
19Check Data Row Exists
Action Name
roofFound
Table
Materials
Row
data("Tower Plans", towerRow, "roof")
20IfroofFound.value == true
Then
21Change Variable
Variable
roofKind
Value
data("Materials", data("Tower Plans", towerRow, "roof"), "support")
22Place A BlockFunction
x
towerX - @self.roofSpread
y
floorBottom + data("Block Kinds", roofKind, "height") / 2
tower
tower
builder
self
kind
roofKind
23Place A BlockFunction
x
towerX + @self.roofSpread
y
floorBottom + data("Block Kinds", roofKind, "height") / 2
tower
tower
builder
self
kind
roofKind
24Region
Label
"Singing Notes"
Color
#E8B923
25Repeatdata("Stages", $stage - 1, "noteCount")times
Each time
26Change Variable
Variable
row
Value
data("Stages", $stage - 1, "firstNote") + local.index
27Where A Note SitsFunction
Action Name
noteSpot
noteRow
row
groundHeight
@self.groundHeight
floorHeight
@self.floorHeight
noteAboveFloor
@self.noteAboveFloor
28Set Data Cell
Table
Notes
Row
row
Column
"x"
Value
noteSpot.x
29Set Data Cell
Table
Notes
Row
row
Column
"y"
Value
noteSpot.y
30Create Instance
Object to Create
@self.singingNote
X
noteSpot.x
Y
noteSpot.y
Variable Name
newNote
31Change Variable
Variable
newNote.noteRow
Value
data("Stages", $stage - 1, "firstNote") + local.index
32Change Variable
Variable
newNote.turn
Value
local.index
33Trigger Custom Event on Object
Applies to
newNote
Custom Event
placed
34Trigger Custom Event
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

Logic ComponentFlyAttach to: Happy NoteAddDelete

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.

Attach toAny Object
DATA STRUCTURES
Shot Sumsself.shotSums
Fly Dataself.flyData
COMPONENTS
self.colliderCollider
SHARED VARIABLES
NameTypeKindStarting Value
stillSpeedNumberConstant0.25
stillSecondsNumberConstant0.8
longestFlightSecondsNumberConstant9
rollingHeightNumberConstant1.7
rollingSlowdownNumberConstant2.5
fieldLeftNumberConstant-1.2
fieldRightNumberConstant20.4
fieldBottomNumberConstant-1.2
checkSecondsNumberConstant0.05
groundHeightNumberConstant1.3
screenLeftNumberConstant0.4
screenRightNumberConstant18.8
dustBelowNoteNumberConstant0.2
dustLayerNumberConstant60
hitBounceNumberConstant0.15
springBackBounceNumberConstant0.4
nearRadiusNumberConstant1.2
On Happy Note Launched
WhenhappyNoteLaunchedCustom Event
1Set Logic State
Logic Component
Pull Back
State
Disable
2Change Component Value
Component Value
self.collider.fixed
Value
false
3Change Component Value
Component Value
self.transform.gravity
Value
true
4Set Velocity
X Velocity
$launchVX
Y Velocity
$launchVY
5Judge The ShotFunction
groundHeight
@self.groundHeight
fieldLeft
@self.fieldLeft
fieldRight
@self.fieldRight
shot
self
6If$aimedPitch == $calledPitch
Then
7Set Bounciness
Value
@self.hitBounce
8Switch Mode
Event
Every Step
Mode
Hard
9Otherwise
10Set Bounciness
Value
@self.springBackBounce
11Control Sound
Sound
Sound Launch
12Start Recurring Timer
Timer Name
"watchFlight"
Repeat Every (s)
@self.checkSeconds
First Delay (s)
@self.checkSeconds
Every Step
WhenStep
ModeGentle (Starting Mode)
(no Actions)
Every Step
WhenStep
ModeHard
1Fire Event In Radius
Event
shotNear
Radius
@self.nearRadius
On the Watch Flight Timer
WhenRecurring TimerTimer name: watchFlight
1Declare Variable
Name
speed
Value
0
2Change Component Value
Component Value
self.flyData.flightSeconds
Operator
+=
Value
@self.checkSeconds
3Change Variable
Variable
speed
Value
hypot(self.transform.velocity.x, self.transform.velocity.y)
4Ifspeed < @self.stillSpeed
Then
5Change Component Value
Component Value
self.flyData.stillFor
Operator
+=
Value
@self.checkSeconds
6Otherwise
7Change Component Value
Component Value
self.flyData.stillFor
Value
0
8Ifself.transform.position.y < @self.rollingHeight
Then
9Set Drag (Slow Down)
Amount
@self.rollingSlowdown
10Ifself.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
Then
11Stop Recurring Timer
Timer Name
"watchFlight"
12Burst Particles
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
13Trigger Custom Event
Custom Event
shotEnded
14Destroy Instance

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.

Logic ComponentWatch The Towers FallAttach to: Game Rules

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.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
settleSecondsNumberConstant2.5
When the Scene Starts
WhenScene Start
1Change Variable
Variable
$towersFalling
Value
false
On Tower Cut
WhentowerCutCustom Event
1Change Variable
Variable
$towersFalling
Value
true
2Stop Recurring Timer
Timer Name
"settle"
On Shot Ended
WhenshotEndedCustom Event
1If$towersFalling == true
Then
2Start Recurring Timer
Timer Name
"settle"
Repeat Every (s)
@self.settleSeconds
Ring Once
Yes
On the Settle Timer
WhenRecurring TimerTimer name: settle
1Change Variable
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

Particle EffectWood Splinters
Emitter 1
Emitter
Emission > Burst Count6
Emitter Shape > Emit Radius (m)0.192
Motion
Lifecycle > Each Particle Lives (ms)min 400, max 700
Motion > Speed (m/s)min 1.2, max 3
Motion > Angle (Deg)min 10, max 170
Forces > Gravityx 0, y -12
Appearance
Appearance > Particle SpriteSplinter Art
Appearance > Color#FFFFFF
Appearance > BlendNormal
Appearance Over Life > Scalestart 1.28, end 1.12
Appearance Over Life > Rotatestart 0, end -720
Emitter 2
Emitter
Emission > Burst Count3
Emitter Shape > Emit Radius (m)0.06
Motion
Lifecycle > Each Particle Lives (ms)min 350, max 550
Motion > Speed (m/s)min 0.2, max 0.7
Motion > Angle (Deg)min 20, max 160
Appearance
Appearance > Particle SpriteSmoke Puff
Appearance > Color#D9B98A
Appearance > BlendNormal
Appearance Over Life > Scalestart 1.92, end 3.84
Appearance Over Life > Alphastart 0.45, end 0

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

Particle EffectStone Dust
Emitter 1
Emitter
Emission > Burst Count6
Emitter Shape > Emit Radius (m)0.192
Motion
Lifecycle > Each Particle Lives (ms)min 400, max 700
Motion > Speed (m/s)min 1.1, max 2.6
Motion > Angle (Deg)min 10, max 170
Forces > Gravityx 0, y -13
Appearance
Appearance > Particle SpriteStone Chip Art
Appearance > Color#FFFFFF
Appearance > BlendNormal
Appearance Over Life > Scalestart 0.72, end 0.64
Appearance Over Life > Rotatestart 0, end -540
Emitter 2
Emitter
Emission > Burst Count5
Emitter Shape > Emit Radius (m)0.06
Motion
Lifecycle > Each Particle Lives (ms)min 450, max 700
Motion > Speed (m/s)min 0.2, max 0.8
Motion > Angle (Deg)min 10, max 170
Forces > Gravityx 0, y 0.15
Appearance
Appearance > Particle SpriteSmoke Puff
Appearance > Color#B9BEC6
Appearance > BlendNormal
Appearance Over Life > Scalestart 2.24, end 4.8
Appearance Over Life > Alphastart 0.5, end 0
Particle EffectIce Shatter
Emitter 1
Emitter
Emission > Burst Count9
Emitter Shape > Emit Radius (m)0.288
Motion
Lifecycle > Each Particle Lives (ms)min 500, max 850
Motion > Speed (m/s)min 1.5, max 4.2
Motion > Angle (Deg)min 0, max 180
Forces > Gravityx 0, y -11
Appearance
Appearance > Particle SpriteIce Shard Art
Appearance > Color#FFFFFF
Appearance > BlendNormal
Appearance Over Life > Scalestart 1.76, end 1.28
Appearance Over Life > Rotatestart 0, end -540
Emitter 2
Emitter
Emission > Burst Count10
Emitter Shape > Emit Radius (m)0.06
Motion
Lifecycle > Each Particle Lives (ms)min 200, max 380
Motion > Speed (m/s)min 1.2, max 3.2
Appearance
Appearance > Color#DFF6FF
Appearance Over Life > Scalestart 1.12, end 0.16

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

Data StructureFall With A Knock Data

Object Data of Logic "Fall With A Knock" (its state that lasts between Events).

materialTextdefault wood
speedNumberdefault 0
lastSpeedNumberdefault 0
Logic ComponentFall With A KnockAttach to: Wood Post, Wood Plank, Stone Cube, Stone Plank, Ice Cube

Once a block has come loose: it knocks when it lands (wood, stone or ice), throws splinters or dust when it lands hard, and a block of ice shatters when it is hit or lands hard while a tower is coming down. A block that falls out of the field is removed. It reads kind and isFalling (its Block State), set by Place A Block (kind, when it creates the block) and Come Down When Struck (isFalling), on the same block.

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

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.

Data StructureSound The Fall Data

Object Data of Logic "Sound The Fall" (its state that lasts between Events).

crashNumberdefault 0
crashLevelNumberdefault 0
Logic ComponentSound The FallAttach to: Game Rules

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.

Attach toAny Object
DATA STRUCTURES
Sound The Fall Dataself.soundTheFallData
SHARED VARIABLES
NameTypeKindStarting Value
knockGapSecondsNumberConstant0.045
fadePerSecondNumberConstant2.5
smallCrashKnocksNumberConstant3
bigCrashKnocksNumberConstant8
quietKnocksNumberConstant0.5
fadeStepSecondsNumberConstant0.05
On Block Knocked
WhenblockKnockedCustom Event
1Change Component Value
Component Value
self.soundTheFallData.crash
Operator
+=
Value
1
2Start Recurring Timer
Timer Name
"crashFade"
Repeat Every (s)
@self.fadeStepSeconds
First Delay (s)
@self.fadeStepSeconds
3Check Cooldown Ready
Action Name
knockReady
Cooldown Key
"knock"
4IfknockReady.value == true
Then
5Start Cooldown
Cooldown Key
"knock"
Seconds
@self.knockGapSeconds
6Check Data Row Exists
Action Name
materialFound
Table
Materials
Row
$knockMaterial
7IfmaterialFound.value == true
Then
8Control Sound
Sound
data("Materials", $knockMaterial, "knockSound")
On the Crash Fade Timer
WhenRecurring TimerTimer name: crashFade
1Change Component Value
Component Value
self.soundTheFallData.crash
Value
max(0, self.soundTheFallData.crash - @self.fadePerSecond * @self.fadeStepSeconds)
2Ifself.soundTheFallData.crash >= @self.bigCrashKnocks && self.soundTheFallData.crashLevel < 2
Then
3Change Component Value
Component Value
self.soundTheFallData.crashLevel
Value
2
4Control Sound
Sound
Sound Crash Big
5Ifself.soundTheFallData.crash >= @self.smallCrashKnocks && self.soundTheFallData.crashLevel < 1
Then
6Change Component Value
Component Value
self.soundTheFallData.crashLevel
Value
1
7Control Sound
Sound
Sound Crash Small
8Ifself.soundTheFallData.crash < @self.quietKnocks
Then
9Change Component Value
Component Value
self.soundTheFallData.crashLevel
Value
0
10Stop Recurring Timer
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.

Play it!

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

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.

Challenge

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.