Build a GameCreating Pitch Shot

4  Flying

Goal

By the end of this chapter, the happy note flies. Let go, and it arcs along the dots, lands, rolls and comes to rest in a puff of dust, and the next one loads. The shot count climbs at the top. Every shot says “Missed! Listen again…” for now, and the call comes back.

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 5 from it.

Pitch Shot, Chapter 4 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.

4.1 The Idea

The note sits in the band. You pull it back, and the white dots arc out toward the tower. You let go, and it springs home. All that aiming, and the note never leaves the slingshot’s side.

This chapter lets it go. Pull, let go, and the note leaves the band with a whoosh. It rides up along the dots and over. It may knock against the tower. It drops to the grass, rolls and slows, and a little puff of dust rises where it stops. Then it’s gone, and a moment later a fresh note smiles in the band.

That’s a shot: one happy note, from the moment it leaves the band until it comes to rest.

Here’s the real question. While you pull, Pull Back puts the note exactly where your hand is, moment by moment. Once it flies, who moves it?

Think of a paper boat at the edge of a stream. While your hand holds it, it goes where you put it. Let go, and the water carries it, and nothing your hand does after that changes where it goes. In Myth, the water is physics. Give the note a Collider, a body of its own, and free it, and the world’s gravity pulls it down, the way it pulls anything that falls. Physics carries the note to the end of its flight, and Logic only watches.

So there are two kinds of note in this chapter: one held, and one free. Every piece here holds it, lets it go, or watches.

4.2 The Plan

Here’s how the pieces work together. A name in bold is a piece you’ll make or change, and a name in plain text is a Custom Event.

  • You let go -> Pull Back calls Let Go -> pullEnded, and, if the pull was long enough, happyNoteLaunched.
  • happyNoteLaunched -> Fly frees the note and throws it. Band At Rest hides the strip, Count The Shots adds one, and Repeat The Call knows the note has left the band.
  • The note comes to rest -> Landing Dust puffs -> shotEnded -> the note is gone.
  • After shotEnded, Load The Happy Note waits a moment -> Put A Note In The Band -> happyNoteLoaded.
  • Also after shotEnded, Ask Again says Missed! Listen again… -> callRequested -> the call plays.
  • All the while, the Shots Count at the top shows the shots.

You’ll build it in that order: the flight first, then the next note, then the count and the words.

4.3 Set Up

Before the first shot, there’s one editor gap to work around. Chapter 1’s blocks have no Fixed switch. The first shot pushes an unfixed tower apart, and its blocks float away without gravity.

Save your project first. Start this chapter from Chapter 3’s catch-up download, which keeps Fixed on every block. Unzip it and open the project. The aiming you built in Chapter 3 comes with it too. Keep yours for comparison.

Set upWhat this chapter adds
ART (sprites, from the starter kit)
Shots Pill Art
Shots Icon Art
SOUNDS (from the starter kit)
Sound Launch
GLOBAL VARIABLES
$shotsNumberstarts at 0
SCENE Field: PLACE THESE
ObjectXY
Shots Count1165-64
with Z-Index = 10010
Shots Icon1112-62
with Z-Index = 10011

ART and SOUNDS name what the Starter Kit already brought. Sound Launch is the whoosh of the band letting go. Shots Pill Art and Shots Icon Art are for the count at the top.

GLOBAL VARIABLES has one, $shots, a Number that starts at 0. Add it from the canvas’s Globals card, as you made the others.

PLACE THESE waits for the Shots Count and the Shots Icon, the count at the top, and the chapter comes back to it when they’re made.

4.4 Into the Air

First, the flight. Nothing flies until four things are in: the dust, a body for the note, Fly, and Let Go. So the first run comes at the end of this part.

4.4.1 Landing Dust

The puff where the note comes to rest is a particle effect, ten soft, sand-colored puffs that grow and fade.

Particle EffectLanding Dust
Emitter
Emission > Burst Count10
Emitter Shape > Emit Radius (m)0.288
Motion
Lifecycle > Each Particle Lives (ms)min 400, max 700
Motion > Speed (m/s)min 0.4, max 1.3
Motion > Angle (Deg)min 10, max 170
Forces > Gravityx 0, y 0.1
Appearance
Appearance > Particle SpriteSmoke Puff
Appearance > Color#CDBB99
Appearance > BlendNormal
Appearance Over Life > Scalestart 2.24, end 5.12
Appearance Over Life > Alphastart 0.6, end 0

A Particle Effect card uses this very card as its example, and tells you how its sections and boxes work.

4.4.2 The Happy Note, Changed

ObjectHappy NoteAddDelete
Transform
Bounding Box > SizeX 0.65, Y 0.89
Physics > Mass6
Physics > Allow RotationOn
Physics > Spin Drag0.6
Collider
Shape and Transform > ShapeCircle
Shape and Transform > Radius0.3
Physics > Bounciness (0–1)0.22
Physics > Friction (Grip)0.55
Fixed (no editor field)On
Sprite
Scene Depth Draw Order40
SpriteHappy Note Art
Transform > SizeX 0.65, Y 0.89
Data Structure
From LogicPull Back, Fly
From LogicPull Back
Data StructureShot Sums
Data Structure
From LogicPull Back
Data StructurePull Back Data
Data Structure
From LogicFly
Data StructureFly Data
Logic
Pull Back
Fly

The note gets a body. A Collider gives it a shape that can touch the tower and the ground. Its Shape is a list: pick Circle, and a Radius box appears. Type 0.3 meters, about half the picture’s width. The card’s Fixed line has no switch, as on the blocks. Leave that line for now. The note waits without falling because Affected by Gravity stays Off until Fly turns it on.

The other numbers are how the note feels when it lands, and when it rolls. Bounciness 0.22 is a small hop off the tower. A rubber ball would be nearer 1. Friction 0.55 is how much the ground grips the note as it rolls, and Allow Rotation lets it tumble. Mass 6 gives it some weight, and Spin Drag 0.6 slows its spin. Those settings live on the Transform. Look under Physics. Turn Physics Object on there first, and leave Affected by Gravity off.

Stay Inside the Scene is Off. That lets the note fly past the edge of the screen, where Fly ends the shot.

Attach the Collider now, from Physics in the Component Toolbox, and fill in its lines. Any Object can take a Collider, so the Happy Note you made as an Empty Object in Chapter 3 is the right kind. Fly comes later, once you’ve made it, because Fly needs a Collider on its Object. Fly reads Shot Sums too, and the note has it already. It also brings Fly Data with it when attached, so both From Logic lines fill themselves in.

4.4.3 Fly

Fly does three jobs: it lets the note go, it watches the flight, and it ends the shot.

Letting go. When happyNoteLaunched comes, Fly first switches Pull Back off, with Set Logic State. A flying note can’t be pulled. Then it frees the body, through the collider in self: fixed becomes false. The Transform’s gravity becomes true. Last, Set Velocity gives it the launch speed, $launchVX across and $launchVY up. The dots have been showing that same launch speed since Chapter 3. So the note rides along them, dot by dot. Sound Launch plays, and a timer, watchFlight, starts to look in every 0.05 seconds.

Watching. Each time watchFlight rings, Fly adds the 0.05 seconds to flightSeconds. It finds the note’s speed from the Transform’s velocity, with hypot, the way Aim At found the pull’s length. Slower than stillSpeed, 0.25 meters a second, counts as still. While it’s still, stillFor grows. The moment it speeds up again, stillFor goes back to 0.

There’s one more look. Below rollingHeight, 1.7 meters, the note is down on the grass. There, Set Drag (Slow Down) gives it a little drag, rollingSlowdown, 2.5. It’s like rolling through long grass. So a shot that bounces off the tower doesn’t roll away forever. It stops at the tower’s foot.

Ending. Count the beats of a quiet landing. The note touches the grass. It rolls, slower and slower, and its speed falls under 0.25 meters a second. Still. Then 0.8 seconds of stillness, and the shot is over. A shot also ends if it has flown for 9 seconds, or left the field: left of -1.2 meters, right of 20.4, or below -1.2.

When it’s over, Fly stops its timer, and the Landing Dust puffs where the note is. It sends shotEnded, and the note removes itself.

The dust has rules of its own. It puffs a little below the note, but never under the grass, at groundHeight. A note that flew off the screen still puffs at the screen’s edge, between screenLeft and screenRight, where you can see it. And the puff is drawn in front of the notes, at dustLayer. Burst Particles’ X, Y and Z Index Offset hold those three rules.

Fly Data keeps both clocks. Make this form first, ready for Fly to watch the flight and the stillness between timer rings.

Data StructureFly Data

Object Data of Logic "Fly" (its state that lasts between Events).

flightSecondsNumberdefault 0
stillForNumberdefault 0
Logic ComponentFlyAttach to: Happy Note

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
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
5Control Sound
Sound
Sound Launch
6Start Recurring Timer
Timer Name
"watchFlight"
Repeat Every (s)
@self.checkSeconds
First Delay (s)
@self.checkSeconds
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

Make it with Add > Logic Component. Its Data Structures card names Shot Sums and Fly Data. Add the Collider on its Components card, under self.collider, as A Logic Component card shows. Its Shared Variables are the flight’s limits and timing, kept on the Happy Note. Add them in the card’s order.

A few blocks are new:

  • Set Logic State’s Logic Component list takes Pull Back. Its State is a list, and the card’s is Disable.
  • Set Velocity has two boxes, X Velocity and Y Velocity, across and up.
  • Set Drag (Slow Down) has one box, Amount.
  • In watchFlight’s Event, the If at step 8 comes after the If at step 4, outside it. Join their round handles. The If at step 10 follows step 8, on the Event’s main line too.
  • Step 10’s If joins its checks with || in one Condition box. Any one of them can end the shot.
  • Burst Particles takes Landing Dust in its Particles list, and its Position Relative To is a list, World.
  • Destroy Instance has no lines. With no Applies To, it removes the note it’s on.

Now attach Fly to the Happy Note, which already has its Collider, as the card above shows.

4.4.4 Let Go

Fly listens for happyNoteLaunched, but nothing sends it yet. That’s Let Go’s job.

Let Go is how a pull ends, from the mouse or from Enter. Chapter 3 gave you a Command named Let Go, on the Enter key. This Let Go is a Function. First, it sends pullEnded, so the band and the dots can rest, whatever happens next. Then it asks how long the pull was. Shorter than shortestShot, 0.24 meters, isn’t a shot. Maybe your hand slipped. The note goes back home, as it did in Chapter 3. Longer, and Let Go sends happyNoteLaunched.

FunctionLet Go

Ends a pull: a pull shorter than shortestShot puts the happy note back in the band; a longer one launches it.

PARAMETERS
NameTypeStarts at
happyNoteObject
homeXNumber0
homeYNumber0
shortestShotNumber0.24
ACTIONS
1Trigger Custom Event
Custom Event
pullEnded
2Ifhypot($aimX - homeX, $aimY - homeY) < shortestShot
Then
3Set Position
Applies to
happyNote
X
homeX
Y
homeY
4Otherwise
5Trigger Custom Event
Custom Event
happyNoteLaunched

Make it with Add > Function, as you made Drop The Pull. Its happyNote is the same kind, Structural, with nothing under it. The If’s line is the pull’s length, worked out by hypot as Aim At worked it out, from home to the aim.

4.4.5 Pull Back, Changed

The card is long, but only a few of its lines are marked. Look for the green ones.

Logic ComponentPull BackAttach to: Happy NoteAddDelete

Lets the player pull the waiting happy note back from the band, with the mouse (or a finger) or with the keys, and let go. A pull cut off by another window or by the device is dropped.

Attach toAny Object
DATA STRUCTURES
Shot Sumsself.shotSums
Pull Back Dataself.pullBackData
SHARED VARIABLES
NameTypeKindStarting Value
maxPullNumberConstant1.75
launchPowerNumberConstant9.2
reachNumberConstant2.6
reachLeftOfNumberConstant7
aimAreaTopNumberConstant8.8
startAngleNumberConstant40
startPullNumberConstant0.75
degreesPerSecondNumberConstant50
pullPerSecondNumberConstant0.5
lowestAngleNumberConstant-10
highestAngleNumberConstant80
leastPullNumberConstant0.2
stepSecondsNumberConstant0.02
shortestShotNumberConstant0.24
When Created
WhenCreate
1Change Component Value
Component Value
self.pullBackData.homeX
Value
self.transform.position.x
2Change Component Value
Component Value
self.pullBackData.homeY
Value
self.transform.position.y
When the Left Mouse Button Is Pressed
WhenMouse PressMouse button: left
1Declare Variable
Name
pointerDistance
Value
0
2Declare Variable
Name
pointerX
Value
0
3Declare Variable
Name
pointerY
Value
0
4Declare Variable
Type
Boolean
Name
isInReach
Value
false
5Change Variable
Variable
pointerX
Value
eventValue("position.x", 0)
6Change Variable
Variable
pointerY
Value
eventValue("position.y", 0)
7Change Variable
Variable
pointerDistance
Value
hypot(pointerX - self.pullBackData.homeX, pointerY - self.pullBackData.homeY)
8IfpointerDistance <= @self.reach || pointerX <= @self.reachLeftOf
Then
9Change Variable
Variable
isInReach
Value
true
10Otherwise
11Change Variable
Variable
isInReach
Value
false
12IfpointerY < @self.aimAreaTop && isInReach == true
Then
13Change Component Value
Component Value
self.pullBackData.isKeyAiming
Value
false
14Stop Recurring Timer
Timer Name
"steerAim"
15Change Component Value
Component Value
self.pullBackData.isPulled
Value
true
16Aim AtFunction
homeX
self.pullBackData.homeX
homeY
self.pullBackData.homeY
pointX
pointerX
pointY
pointerY
maxPull
@self.maxPull
launchPower
@self.launchPower
happyNote
self
17Set Position
X
$aimX
Y
$aimY
18Trigger Custom Event
Custom Event
pullStarted
While the Left Mouse Button Is Held
WhenMouse HeldMouse button: left
1Declare Variable
Name
pointerX
Value
0
2Declare Variable
Name
pointerY
Value
0
3Ifself.pullBackData.isPulled == true
Then
4Change Variable
Variable
pointerX
Value
eventValue("position.x", 0)
5Change Variable
Variable
pointerY
Value
eventValue("position.y", 0)
6Aim AtFunction
homeX
self.pullBackData.homeX
homeY
self.pullBackData.homeY
pointX
pointerX
pointY
pointerY
maxPull
@self.maxPull
launchPower
@self.launchPower
happyNote
self
7Set Position
X
$aimX
Y
$aimY
Aim 2D Axis Input
When2D Axis InputCommand: Aim
1Change Component Value
Component Value
self.pullBackData.turnKeys
Value
eventValue("axisVector.y", 0)
2Change Component Value
Component Value
self.pullBackData.pullKeys
Value
eventValue("axisVector.x", 0)
3Ifself.pullBackData.turnKeys != 0 || self.pullBackData.pullKeys != 0
Then
4Change Component Value
Component Value
self.pullBackData.isKeyHeld
Value
true
5Otherwise
6Change Component Value
Component Value
self.pullBackData.isKeyHeld
Value
false
7Ifself.pullBackData.isPulled == false && self.pullBackData.isKeyAiming == false && self.pullBackData.isKeyHeld == true
Then
8Change Component Value
Component Value
self.pullBackData.isKeyAiming
Value
true
9Change Component Value
Component Value
self.pullBackData.keyAngle
Value
@self.startAngle
10Change Component Value
Component Value
self.pullBackData.keyPull
Value
@self.startPull
11Trigger Custom Event
Custom Event
pullStarted
12Start Recurring Timer
Timer Name
"steerAim"
Repeat Every (s)
@self.stepSeconds
First Delay (s)
0
On the Steer Aim Timer
WhenRecurring TimerTimer name: steerAim
1Change Component Value
Component Value
self.pullBackData.keyAngle
Operator
+=
Value
self.pullBackData.turnKeys * @self.degreesPerSecond * @self.stepSeconds
2Change Component Value
Component Value
self.pullBackData.keyAngle
Value
clamp(self.pullBackData.keyAngle, @self.lowestAngle, @self.highestAngle)
3Change Component Value
Component Value
self.pullBackData.keyPull
Operator
+=
Value
self.pullBackData.pullKeys * @self.pullPerSecond * @self.stepSeconds
4Change Component Value
Component Value
self.pullBackData.keyPull
Value
clamp(self.pullBackData.keyPull, @self.leastPull, 1)
5Aim AtFunction
homeX
self.pullBackData.homeX
homeY
self.pullBackData.homeY
pointX
self.pullBackData.homeX - cos(degtorad(self.pullBackData.keyAngle)) * @self.maxPull * self.pullBackData.keyPull
pointY
self.pullBackData.homeY - sin(degtorad(self.pullBackData.keyAngle)) * @self.maxPull * self.pullBackData.keyPull
maxPull
@self.maxPull
launchPower
@self.launchPower
happyNote
self
6Set Position
X
$aimX
Y
$aimY
When Letgo Is Pressed
WhenCommand PressedCommand: Let Go
1Ifself.pullBackData.isKeyAiming == true
Then
2Change Component Value
Component Value
self.pullBackData.isKeyAiming
Value
false
3Stop Recurring Timer
Timer Name
"steerAim"
4Drop The PullFunction
4Let GoFunction
homeX
self.pullBackData.homeX
homeY
self.pullBackData.homeY
shortestShot
@self.shortestShot
happyNote
self
When the Left Mouse Button Is Released
WhenMouse ReleaseMouse button: left
1Ifself.pullBackData.isPulled == true
Then
2Change Component Value
Component Value
self.pullBackData.isPulled
Value
false
3Drop The PullFunction
3Let GoFunction
homeX
self.pullBackData.homeX
homeY
self.pullBackData.homeY
shortestShot
@self.shortestShot
happyNote
self
On Focus Lost
WhenFocus Lost
1Ifself.pullBackData.isPulled == true
Then
2Change Component Value
Component Value
self.pullBackData.isPulled
Value
false
3Drop The PullFunction
homeX
self.pullBackData.homeX
homeY
self.pullBackData.homeY
happyNote
self
On Touch Cancelled
WhenTouch Cancelled
1Ifself.pullBackData.isPulled == true
Then
2Change Component Value
Component Value
self.pullBackData.isPulled
Value
false
3Drop The PullFunction
homeX
self.pullBackData.homeX
homeY
self.pullBackData.homeY
happyNote
self

Pull Back gets one new Shared Variable, shortestShot. You’ll find it on the Happy Note, under Variables, once the Logic is attached. Two of its Events also change who they call. In When Letgo Is Pressed and When the Left Mouse Button Is Released, Drop The Pull becomes Let Go. The heading When Letgo Is Pressed is Myth’s own name for the Let Go Command’s Event.

Each of those is the last step inside its If. So delete the Drop The Pull block there, and add Let Go in its place, after the step before it. The struck-through rows show no lines under them, because they’re going. Your Drop The Pull from Chapter 3 had its own lines. They go with it. Let Go’s happyNote list takes self, and its shortestShot box takes @self.shortestShot.

The call lists its boxes in its own order, which isn’t always the order on Let Go’s own card. That’s the editor’s way, and nothing to fix.

On Focus Lost and On Touch Cancelled keep Drop The Pull, because a pull that’s cut short was never a shot.

4.4.6 Band At Rest, Changed

Logic ComponentBand At RestAttach to: Band RestAddDelete

The band hangs between the prongs while a happy note waits in it and nobody is pulling; it makes the stretching sound when a pull starts.

Attach toAny Object
When Created
WhenCreate
1Set Visible
Visible
No
On Happy Note Loaded
WhenhappyNoteLoadedCustom Event
1Set Visible
Visible
Yes
On Pull Started
WhenpullStartedCustom Event
1Set Visible
Visible
No
2Control Sound
Sound
Sound Stretch
On Pull Ended
WhenpullEndedCustom Event
1Set Visible
Visible
Yes
On Happy Note Launched
WhenhappyNoteLaunchedCustom Event
1Set Visible
Visible
No

Let Go sends pullEnded before it launches anything, and Band At Rest shows the straight strip again when it hears that. But the note is flying away, not sitting in the band. So Band At Rest gets one more Event. When happyNoteLaunched comes, the strip hides again, a moment after it showed. Set Visible’s list says No, as it did in Chapter 3.

Run the game. Pull the note back and let go. It leaves with a whoosh and rides up along the dots, and if they cross the tower, it knocks against it. It drops to the grass, rolls, and stops. A moment later, a puff of dust, and it’s gone.

And then nothing. The band stays empty, waiting for a note that never comes. That’s next.

4.5 The Next Note

A slingshot with one note isn’t much of a game. After each shot, the band needs a new one.

4.5.1 Put A Note In The Band

In Chapter 3, Load The Happy Note made the note itself, with an If and a Create Instance. Now two different moments need those same steps: the end of the opening, and the end of every shot. So they move into a Function, and both moments call it.

You’ve built this before. It’s Load The Happy Note’s three steps again, and most of their lines are the same. What changes is who makes the note, and how the Function learns where the band is.

A Function has no Object of its own, so it’s handed two. The slingshot is the one that makes the note, so Create Instance’s Applies To is slingshot. The happyNote Parameter holds the kind of Object to make. Its Type is Object among the plain values, with Happy Note as its Default. The slingshot is a plain Object, Structural, with nothing under it. And the band’s spot comes in as bandX and bandY, so the Function doesn’t need to know where the band is.

FunctionPut A Note In The Band

Puts a happy note at the band point, unless one is already there or flying.

PARAMETERS
NameTypeStarts at
slingshotObject
happyNoteObjectHappy Note
bandXNumber0
bandYNumber0
ACTIONS
1IfinstanceCount("Happy Note") == 0
Then
2Create Instance
Applies to
slingshot
Object to Create
happyNote
X
bandX
Y
bandY
Variable Name
newHappyNote
3Trigger Custom Event
Custom Event
happyNoteLoaded

4.5.2 Load The Happy Note, Changed

Logic ComponentLoad The Happy NoteAttach to: SlingshotAddDelete

Puts a happy note in the band when the opening is over, and again a moment after each shot ends, if none is there.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
happyNoteObjectConstantNone
bandOffsetXNumberConstant-0.01
bandOffsetYNumberConstant1.01
secondsAfterShotNumberConstant0.35
On Opening Finished
WhenopeningFinishedCustom Event
1IfinstanceCount("Happy Note") == 0
Then
2Create Instance
Object to Create
@self.happyNote
X
self.transform.position.x + @self.bandOffsetX
Y
self.transform.position.y + @self.bandOffsetY
Variable Name
newHappyNote
3Trigger Custom Event
Custom Event
happyNoteLoaded
1Put A Note In The BandFunction
bandX
self.transform.position.x + @self.bandOffsetX
bandY
self.transform.position.y + @self.bandOffsetY
happyNote
@self.happyNote
slingshot
self
On Shot Ended
WhenshotEndedCustom Event
1Start Recurring Timer
Timer Name
"loadNext"
Repeat Every (s)
@self.secondsAfterShot
Ring Once
Yes
On the Load Next Timer
WhenRecurring TimerTimer name: loadNext
1Put A Note In The BandFunction
bandX
self.transform.position.x + @self.bandOffsetX
bandY
self.transform.position.y + @self.bandOffsetY
happyNote
@self.happyNote
slingshot
self

The old steps in On Opening Finished go: delete the If, the Create Instance and the Trigger Custom Event. One call to Put A Note In The Band takes their place. With the Event’s line empty, add the call from the Action List. It lands Unattached. Connect to Sequence joins it at the start of the line, as a Function’s first Action joins after Start. Its bandX and bandY take the old X and Y formulas, the slingshot’s own spot plus the offsets. Its happyNote list takes @self.happyNote, and its slingshot list takes self.

Two Events are new. When shotEnded comes, a timer, loadNext, rings once, secondsAfterShot later, 0.35 seconds. When it rings, Put A Note In The Band runs again, with the same four values. Add secondsAfterShot to the Shared Variables too.

Its Start Recurring Timer has a Ring Once list, as the timers of Chapter 2 did. Pick Yes. The card gives no First Delay, so leave that box empty, and the one ring comes after Repeat Every, the 0.35 seconds.

The pause is short, about a third of a second. The dust puffs where the old note rested, and then the new one arrives.

Run the game. Shoot. The note lands and rests, the dust puffs, and a new note pops into the band. Shoot again, and again, as many times as you like.

4.6 Counting and Missing

Every shot lands now. But the game says nothing about it, and the call never comes back.

4.6.1 Count The Shots

Count The Shots is the smallest Logic in the chapter. When the Field starts, it sets $shots to 0. Each happyNoteLaunched adds one. Only a real shot counts. A pull shorter than 0.24 meters sends no happyNoteLaunched, so it never reaches this Logic.

Why set it to 0 at the start? $shots is a Global, like $message, and it keeps its value when the Field starts again. The next stage would start where this one left off.

Logic ComponentCount The ShotsAttach to: Game Rules

Counts the shots of this stage in $shots (the Shots Count at the top shows it).

Attach toAny Object
When the Scene Starts
WhenScene Start
1Change Variable
Variable
$shots
Value
0
On Happy Note Launched
WhenhappyNoteLaunchedCustom Event
1Change Variable
Variable
$shots
Operator
+=
Value
1

4.6.2 The Shots Count

The count needs somewhere to show. The Shots Count is a Screen UI Object, like the Message Pill: a white pill near the top of the screen, with a number in it.

ObjectShots Count
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 190, Y 84
Sprite
Z Index Within Object-1
SpriteShots Pill Art
Transform > SizeX 190, Y 84
Text / UI
Text{$shots}
FontNunito
Font Size34
EmphasisExtra Bold
Color#3A1D0B
AlignmentCenter
RenderingVector
Transform > PositionX 28, Y 1

Its Text is {$shots}, braces and all. No Logic ever copies the number to the screen. The words show $shots live, so they change the moment it does. The words sit 28 pixels right of the pill’s middle, to leave room for the icon on its left. Its card lists no UI Interaction, so remove the one it came with.

4.6.3 The Shots Icon

ObjectShots Icon
Transform
Presentation > SpaceWindow / UI
Presentation > Render LayerUI
Bounding Box > SizeX 34, Y 47
Sprite
Z Index Within Object-1
SpriteShots Icon Art
Transform > SizeX 34, Y 47

The Shots Icon is a little happy note, drawn on its own small Screen UI Object, so it can sit on the left end of the pill. It has no words and no Logic. Remove its UI Interaction too. Its Sprite’s Z Index Within Object is -1, as on the pill. With no words in front of it, you won’t see a difference, but type it so your Object matches the game’s.

Now PLACE THESE. Drag the Shots Count onto the Field, and leave its Screen Anchor on the top-left dot, as you did for the Message Pill in Chapter 2. Type 1165 and -64 into its Screen offset (px), and 10010 into its Z-Index. The -64 is 64 pixels down from the top, as the pill’s -150 was in Chapter 2. Then the Shots Icon, at 1112 and -62, with 10011, one more, so the icon is drawn in front of the pill.

4.6.4 The Messages Table, Changed

Data TableMessagesData Structure: MessageAddDelete
Row 0lowToHigh
wordsLow to high…
Row 1listen
wordsListen…
Row 2missed
wordsMissed! Listen again…

One new row, missed. Add it, and copy and paste its words from this page, … and all. That … is one character, not three dots.

4.6.5 Ask Again

A miss costs nothing. The game says so, and plays the call again, so you can listen once more.

Picture the dust settling. The pill says Missed! Listen again…, and you have a breath, 0.6 seconds, to get ready. Then the call sings, the same as when you press Space. Ask Again does no singing of its own: it sends callRequested, and Repeat The Call answers it.

Logic ComponentAsk AgainAttach to: Game Rules

When a shot reaches no singing note, says "Missed! Listen again…" and plays the call again a moment later.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
secondsAfterMissNumberConstant0.6
On Shot Ended
WhenshotEndedCustom Event
1Change Variable
Variable
$message
Value
'missed'
2Start Recurring Timer
Timer Name
"callAgain"
Repeat Every (s)
@self.secondsAfterMiss
Ring Once
Yes
On the Call Again Timer
WhenRecurring TimerTimer name: callAgain
1Trigger Custom Event
Custom Event
callRequested

For now, every shot is a miss. Even a shot that sails straight through the right note says Missed! The singing notes have no body yet, so nothing can tell which note a shot reached. That comes in Chapter 5. Ask Again is ready for it: it already has its message and its timer.

4.6.6 Game Rules, Changed

ObjectGame RulesAddDelete
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
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 Count The Shots and Ask Again to Game Rules. Both belong to the whole stage.

4.6.7 Repeat The Call, Changed

Logic ComponentRepeat The CallAttach to: Game RulesAddDelete

Plays the call whenever it is asked for, and again by itself after a quiet spell, at most twice in a row, never over the player's aim or a shot in the air.

Attach toAny Object
DATA STRUCTURES
Repeat The Call Dataself.repeatTheCallData
SHARED VARIABLES
NameTypeKindStarting Value
quietSecondsNumberConstant8
mostRepeatsNumberConstant2
waitSecondsNumberConstant1
On Call Requested
WhencallRequestedCustom Event
1If$calledPitch > 0
Then
2Play Piano NoteFunction
pitch
$calledPitch
3Change Component Value
Component Value
self.repeatTheCallData.repeatsLeft
Value
@self.mostRepeats
4Start Recurring Timer
Timer Name
"repeatTheCall"
Repeat Every (s)
@self.quietSeconds
First Delay (s)
@self.quietSeconds
On the Repeat the Call Timer
WhenRecurring TimerTimer name: repeatTheCall
1Ifself.repeatTheCallData.isNoteWaiting == true
Then
2Play Piano NoteFunction
pitch
$calledPitch
3Change Component Value
Component Value
self.repeatTheCallData.repeatsLeft
Operator
+=
Value
-1
4Ifself.repeatTheCallData.repeatsLeft <= 0
Then
5Stop Recurring Timer
Timer Name
"repeatTheCall"
6Otherwise
7Start Recurring Timer
Timer Name
"repeatTheCall"
Repeat Every (s)
@self.quietSeconds
First Delay (s)
@self.waitSeconds
On Happy Note Loaded
WhenhappyNoteLoadedCustom Event
1Change Component Value
Component Value
self.repeatTheCallData.isNoteWaiting
Value
true
On Pull Started
WhenpullStartedCustom Event
1Change Component Value
Component Value
self.repeatTheCallData.isNoteWaiting
Value
false
On Pull Ended
WhenpullEndedCustom Event
1Change Component Value
Component Value
self.repeatTheCallData.isNoteWaiting
Value
true
On Happy Note Launched
WhenhappyNoteLaunchedCustom Event
1Change Component Value
Component Value
self.repeatTheCallData.isNoteWaiting
Value
false
On Shot Ended
WhenshotEndedCustom Event
1Start Recurring Timer
Timer Name
"repeatTheCall"
Repeat Every (s)
@self.quietSeconds
First Delay (s)
@self.quietSeconds

In Chapter 3, the call learned to wait while you pull. Now it also waits while the note is in the air, until the shot is over. Two new Events. When happyNoteLaunched comes, isNoteWaiting becomes false, because the note is in the air, not in the band. When shotEnded comes, the quiet spell starts again from the top, quietSeconds, 8 seconds.

Notice who sets isNoteWaiting back to true after a shot. Neither of the two new Events does. When the next note is loaded, On Happy Note Loaded does it, the Event you made in Chapter 3.

Run the game. Shoot. The count at the top says 1. The note lands, the pill says Missed! Listen again…, and a moment later the call sings, while a fresh note waits in the band for your next try.

Play it!

Watch, and listen, for each of these.

  • Pull back and let go. You hear the whoosh, and the note leaves along the dots, following them closely.
  • Aim at the tower. The note knocks against it and drops, and the tower doesn’t move. It won’t until Chapter 6.
  • On the grass, it rolls, slows and stops. A small puff of sandy dust rises where it rests, and the note is gone.
  • A third of a second later, a new note.
  • The count at the top goes up by one with each shot. It starts at 0.
  • Pull back only a hair and let go. The note springs home. No whoosh, and the count stays the same.
  • After each shot, the pill says Missed! Listen again…, and the call sings 0.6 seconds after the dust puffs.
  • Shoot hard, high over the tower. The note sails off the right of the screen, and the shot ends there. The next note still comes.
  • Aim with the arrow keys and press Enter. The note flies the same way as from the mouse, along the dots.
How it works

You let go, and Let Go decides: a shot, or a slip. A shot sends happyNoteLaunched. Fly frees the note and throws it, and physics does the flying. Fly only watches. When the note is still, gone, or too long in the air, Fly says shotEnded. Three pieces answer: the slingshot loads, Ask Again speaks, and the call starts a new quiet spell.

Underneath it is one idea: one mover at a time. While the note waits, Logic moves it, with Set Position, and physics keeps its hands off, because gravity is Off, and the blocks around it are Fixed. Once it flies, physics moves it, and no Logic sets its position again. If two things move one note, they fight, and the note jitters.

That’s why the dots were right all along. The dots and the flight use the same launch speed and the same pull of gravity. The dots drew the flight before it happened. Physics flies it.

4.7 If It Didn’t Work

This chapter has a lot of hand-offs: Let Go to Fly, Fly to the slingshot. Most slips are one hand reaching for a message that never came. Watch what the note does, and you’ll know which hand.

You let go, and the note stays where you left it, in the air? So happyNoteLaunched went out, but Fly didn’t hear it. Is Fly on the Happy Note, and is its When bar spelled happyNoteLaunched, letter for letter?

The note drops straight down, or flies off the wrong way? Look at Set Velocity: its two boxes take $launchVX and $launchVY, in that order, across and then up.

The note springs home every time, as it did in Chapter 3? That means Let Go isn’t being called. Look at Pull Back’s When Letgo Is Pressed and When the Left Mouse Button Is Released. Each should end with Let Go, not Drop The Pull.

The note hangs in mid-air, frozen? Perhaps step 2 of Fly’s first Event says true where the card says false. A fixed body doesn’t fall.

The note rolls on and on, far across the meadow? Look at step 8, the If that asks about rollingHeight. It belongs after the first If.

The note stops, but never goes, and no new note comes? Look at step 10’s checks joined by ||. Then look at how shotEnded is spelled, in Fly’s step 13 and in Load The Happy Note’s When bar.

The new note drops out of the band the moment it appears? Look at Affected by Gravity under the Happy Note’s Transform. Leave it Off. Fly turns it on when the shot leaves.

The tower scatters when hit? Start from Chapter 3’s catch-up download, as Set up says. Its blocks have the Fixed setting that the editor has no switch for yet.

Two notes in the band, or none? Look at Put A Note In The Band’s If: instanceCount("Happy Note") == 0, with the Object’s exact name.

The count stays at 0? Look at the Shots Count’s Text, {$shots}, braces and all, and at Count The Shots on Game Rules.

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 flight is yours to tune. On the Happy Note, change the Collider’s Bounciness from 0.22 to 0.8, and watch the note bounce off the tower and across the meadow. Or change rollingSlowdown under the Happy Note’s Variables from 2.5 to 0.5, and the note rolls a long way before it rests. Or change launchPower under those Variables from 9.2 to 12, and the dots, and the note, reach much farther. Which one makes you want to shoot again? Put everything back before Chapter 5.

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

The note flies, lands and rests, and the next one waits. In Chapter 5, the game learns which note you reached.