Build a GameCreating Pitch Shot

3  Aiming

Goal

By the end of this chapter, the happy note arrives in the band. Pull it back with the mouse, a finger or the arrow keys, and the band stretches from both prongs while a line of white dots shows where it would fly. Let go, and it springs back into the band. It can’t fly yet.

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

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

3.1 The Idea

The opening sings, low to high. Listen… shows in the pill, and one note sings alone. And the slingshot stands there with nothing in it.

Now, as Listen… appears, a round blue note appears in the band, rosy cheeks and a wide smile, a little black flag on its head. That’s the happy note, the one you’ll fling.

Put the mouse near it, press, and pull back. The note comes with you. The band stretches from both prongs to hold it, and a curve of white dots runs out ahead of it, up and over toward the tower. Pull farther, and the dots reach farther. Let go, and the note springs back into the band.

Here’s the problem. The note hasn’t flown yet, and the dots already know where it will go. How?

Think of a flip-book. Each page is the same moment a little later, and flipping them shows the motion. The dots are the pages of a flight that hasn’t happened. The first dot is where the note will be a moment after you let go. The second is a moment after that, and so on, 33 moments in all. Each dot works out its own page from three things: where the note starts, how fast it will leave, and how the world pulls it down.

So the pull has to come up with two numbers. One is the aim, the spot you’ve pulled the note to. The other is the launch speed, how fast and which way the note would leave from there. Everything in this chapter reads those two.

3.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 opening ends -> openingFinished -> Load The Happy Note puts the Happy Note in the band -> happyNoteLoaded -> Band At Rest shows the band holding it.
  • You press near the band, or press an arrow key -> Pull Back moves the note back -> pullStarted.
  • Each time the pull moves -> Pull Back asks Aim At for the aim and the launch speed.
  • Once pullStarted goes out -> each Band Piece stretches from its prong to the note, and each Aim Dot shows its moment of the flight.
  • You let go -> Drop The Pull puts the note back -> pullEnded -> the band rests, and the dots hide.
  • The slingshot is made -> Make The Aiming Dots makes 33 dots, each told its number.
  • Repeat The Call hears pullStarted and pullEnded too, so the call never sings over your aim.

You’ll build it so it comes alive in that order: the note first, then the pull, the band and the dots.

3.3 Set Up

Set upWhat this chapter adds
ART (sprites, from the starter kit)
Happy Note Art
Band Art
Aim Dot Art
SOUNDS (from the starter kit)
Sound Stretch
COMMANDS (Game Settings > Input Manager)
Aim2D Axis
LeftLeft Arrow, A
RightRight Arrow, D
UpUp Arrow, W
DownDown Arrow, S
Let GoButton
KeybindingsEnter
INPUT CONTEXTS (Game Settings > Input Manager)
FieldActive at Start(changed)
CommandsHear It Again, Aim, Let Go
GLOBAL VARIABLES
$aimXNumberstarts at 0
$aimYNumberstarts at 0
$launchVXNumberstarts at 0
$launchVYNumberstarts at 0
$gravityNumberstarts at 9.8
SCENE Field: PLACE THESE
ObjectXY
Band Rest2.993.93
Band Piece2.593.93
Band Piece3.393.93

ART and SOUNDS name what the Starter Kit already brought: three pictures and Sound Stretch, the creak of the band.

COMMANDS has two new Commands. Aim is a 2D Axis Command, a direction rather than a single press, with two keys for each way: the arrows and W, A, S and D. Let Go is a Button, Enter. Keys: Commands and Input Contexts shows how. INPUT CONTEXTS marks Field (changed), because it’s the context you renamed in Chapter 2. It’s still Active at Start, and only its Commands are new. So select Field’s row first. A new 2D Axis Command comes with a row for each direction. For the second key of a direction, press Add Keybinding, pick that direction in the list at the front of the new row, and press the key.

GLOBAL VARIABLES has five. $aimX and $aimY are the aim, and $launchVX and $launchVY are the launch speed, across and up. They’re Globals because the note, the band and the dots all read them. $gravity starts at 9.8, the same pull your game’s world falls by. It’s the same number as your world’s Gravity Y field, -9.8, without the minus. That minus means down. The dots’ sum puts it back in by itself, as you’ll see in Show The Flight. If you ever change one, change the other. Make them from the Globals card, as you made $stage in Chapter 1, and give each the card’s Default.

PLACE THESE waits for the band, and the chapter comes back to it when the band is made.

3.4 A Note in the Band

First, the note itself.

ObjectHappy Note
Transform
Bounding Box > SizeX 0.65, Y 0.89
Sprite
Scene Depth Draw Order40
SpriteHappy Note Art
Transform > SizeX 0.65, Y 0.89
Data Structure
From LogicPull Back
Data StructureShot Sums
Data Structure
From LogicPull Back
Data StructurePull Back Data
Logic
Pull Back

Make the Happy Note as an Empty Object, with its Sprite, Happy Note Art, and its sizes. Its Scene Depth Draw Order of 40 draws it in front of the band. Leave its Data Structure and Logic for later in the chapter. Like the singing notes, it isn’t placed. The slingshot makes it.

3.4.1 Load The Happy Note

When the opening ends, the slingshot puts a note in its band. Load The Happy Note hears openingFinished. It asks one question first: is there already a Happy Note? instanceCount counts the Objects of one kind in the game, so instanceCount("Happy Note") == 0 means none. Only then does Create Instance make one, and the game never has two waiting.

Where does it go? The band hangs a little above the slingshot’s middle. So the note goes at the slingshot’s own position, moved by bandOffsetX across and by bandOffsetY up. The second is 1.01 meters, and the first is almost nothing. Then it sends happyNoteLoaded.

Logic ComponentLoad The Happy NoteAttach to: Slingshot

Puts a happy note in the band when the opening is over, if none is waiting there.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
happyNoteObjectConstantNone
bandOffsetXNumberConstant-0.01
bandOffsetYNumberConstant1.01
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

Make it with Add > Logic Component. Its happyNote is a Shared Variable, an Object, like Build The Stage’s singingNote, and the Slingshot’s card sets it to Happy Note. The slingshot’s own x is self.transform.position.x, and it goes into Create Instance’s X box with the rest of the formula.

3.4.2 The Slingshot, Changed

ObjectSlingshotAddDelete
Transform
Bounding Box > SizeX 1.52, Y 3
Sprite
Scene Depth Draw Order20
SpriteSlingshot Art
Transform > SizeX 1.52, Y 3
Logic
Load The Happy Note
Make The Aiming Dots
Shared Variables
happyNoteHappy Note
aimDotAim Dot

The slingshot gets two Logic Components this chapter. Attach Load The Happy Note now. Make The Aiming Dots comes with the dots, at the end.

Run the game. The opening sings, Listen… shows, and in the same moment the happy note appears in the band, smiling. It doesn’t move, and it can’t yet.

3.4.3 Band At Rest

The note needs something to sit in. The Band Rest is a short, straight strip of band, drawn from prong to prong behind the note.

ObjectBand Rest
Transform
Bounding Box > SizeX 0.8, Y 0.09
Sprite
Scene Depth Draw Order38
SpriteBand Art
Transform > SizeX 0.8, Y 0.09
Logic
Band At Rest

The band has two pictures, and only one may show at a time. At rest, it’s this straight strip. Pulled, it’s two stretched pieces, which come later in the chapter. Band At Rest looks after the first. It keeps the strip hidden until a note arrives, and hides it again the moment a pull starts, with the creak of Sound Stretch. When the pull ends, the strip is back.

Logic ComponentBand At RestAttach to: Band Rest

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

Make the Band Rest the way you made the Happy Note, and then Band At Rest, and attach it. Its Set Visible blocks have the Yes or No list, as Show The Message’s did. Pick the card’s. Control Sound plays, as in Play Piano Note, and its Sound list takes Sound Stretch.

Now PLACE THESE. Drag the Band Rest onto the Field, and type its X and Y, 2.99 and 3.93.

Run the game. The note arrives, and the band is there behind it, straight across the prongs.

3.5 The Pull

Now the hand on the band.

3.5.1 Shot Sums

Aim At, coming next, is a Function, and a Function has no Object of its own. Remember Place A Block’s builder? Aim At works the same way. It’s handed the happy note, and it keeps its working numbers on that note, in a Data Structure called Shot Sums.

Data StructureShot Sums

The working values of the Functions that aim the happy note: the pull (Aim At).

pullXNumberdefault 0
pullYNumberdefault 0
pullLengthNumberdefault 0
pullScaleNumberdefault 0

Make it, four Numbers.

3.5.2 Aim At

Picture the pull. Home is where the note sits in the band. The pointer is somewhere behind it. The pull is the line from home to the pointer: pullX across, pullY up. Its length is pullLength, worked out by hypot, which finds a line’s length from its two sides.

A pull can’t go on forever. The longest is maxPull, 1.75 meters, a little more than the slingshot is wide. So pullScale says how much of the pull counts. It’s 1, all of it, while the pull is short enough, and less once it’s longer. A formula gets the smaller of two numbers with min. So the card’s min never lets pullScale go over 1, however far you pull. The aim is home, plus the pull times pullScale. Pull far past the limit, and the note stops at 1.75 meters, in the same direction you’re pulling.

Last comes the launch speed. A slingshot throws the opposite way from the pull, and farther back means faster. So $launchVX and $launchVY are the line from the aim back to home, times launchPower, 9.2. Each meter of pull adds 9.2 meters a second.

Look on the card for happyNote, which needs a Shot Sums under the Slot Name shotSums. And look at the paths: happyNote.shotSums.pullX is the note, its Shot Sums, the field pullX.

FunctionAim At

Sets the aim ($aimX, $aimY): the point the happy note is pulled to, towards a point but never more than maxPull from home (the band point); and the speed it will leave at ($launchVX, $launchVY): launchPower times the pull, back towards home.

PARAMETERS
NameTypeStarts at
happyNoteObjectStructural
Required Components
Slot NameshotSums
Component TypeData Structure
Required SchemaShot Sums
homeXNumber0
homeYNumber0
pointXNumber0
pointYNumber0
maxPullNumber1.75
launchPowerNumber9.2
ACTIONS
1Change Component Value
Component Value
happyNote.shotSums.pullX
Value
pointX - homeX
2Change Component Value
Component Value
happyNote.shotSums.pullY
Value
pointY - homeY
3Change Component Value
Component Value
happyNote.shotSums.pullLength
Value
hypot(happyNote.shotSums.pullX, happyNote.shotSums.pullY)
4Change Component Value
Component Value
happyNote.shotSums.pullScale
Value
min(1, maxPull / happyNote.shotSums.pullLength)
5Change Variable
Variable
$aimX
Value
homeX + happyNote.shotSums.pullX * happyNote.shotSums.pullScale
6Change Variable
Variable
$aimY
Value
homeY + happyNote.shotSums.pullY * happyNote.shotSums.pullScale
7Change Variable
Variable
$launchVX
Value
(homeX - $aimX) * launchPower
8Change Variable
Variable
$launchVY
Value
(homeY - $aimY) * launchPower

Make it with Add > Function. Its happyNote is the Object at the very end of the Type list, like builder. Then it needs Shot Sums under its Required Components, and A Function card shows where they are: the pencil beside happyNote, on the Start block. Its Compatibility Mode starts at Structural, as the card shows, so leave it there. Steps 1 to 4 are Change Component Value, through happyNote, so their paths go in as the card spells them. Steps 5 to 8 set Globals, so their Variable box takes $aimX and the rest, $ and all.

3.5.3 Drop The Pull

Drop The Pull is the small one. It puts the note back at home, and sends pullEnded, so the band and the dots can rest.

FunctionDrop The Pull

Ends a pull with no shot: the happy note goes back to the band (home), and the band and the aiming dots rest.

PARAMETERS
NameTypeStarts at
happyNoteObject
homeXNumber0
homeYNumber0
ACTIONS
1Set Position
Applies to
happyNote
X
homeX
Y
homeY
2Trigger Custom Event
Custom Event
pullEnded

Make it the same way. Its happyNote is an Object like Aim At’s, with nothing listed under it. That’s why the card leaves its last column blank. Set Position’s Applies To is a list, so pick happyNote.

3.5.4 Pull Back

Pull Back is the longest card in the chapter, because there are three ways to pull, and two ways a pull can be cut short. Take it one Event at a time.

When the note is made, it remembers home, its own x and y.

With the mouse, or a finger. You press. Pull Back reads where, from the Event, with eventValue("position.x", 0). Is the press near the note, within reach, 2.6 meters? Or anywhere in the left part of the screen, left of 7 meters across? Either one is in reach, and that’s what the If with an Or asks. One more thing: the press must be below aimAreaTop, 8.8 meters up, because the top of the screen belongs to buttons such as Hear it again. Then the pull begins. A key pull, if one was going, stops, and the mouse takes over. Now isPulled is true, Aim At works out the aim, Set Position moves the note there, and pullStarted goes out.

While you hold, Mouse Held does the same each moment, so the note follows your hand. When you let go, Mouse Release calls Drop The Pull, and the note springs home.

With the keys. The arrows don’t give a spot on the screen, so the keys aim by an angle and a length. Aim’s Event brings axisVector, the way the keys are pressing. Its y is 1 for Up and -1 for Down, and its x is 1 for Right and -1 for Left. So turnKeys holds Up and Down. And pullKeys holds Right and Left. The first press starts at startAngle, 40 degrees above level, with three quarters of the longest pull. Hold Up or Down, and the angle turns 50 degrees a second, from -10 up to 80. Hold Right, and the pull grows. Hold Left, and it shrinks. A timer, steerAim, moves them every 0.02 seconds, and clamp keeps each one between its limits. Then the angle and the length become a point behind home, and Aim At takes it from there, as it does for the mouse. Enter lets go.

Cut short. Two Events run by themselves, with nobody pressing anything. Focus Lost comes when you switch to another window in the middle of a pull, and the mouse button is let go over there, where the game never hears it. Touch Cancelled comes when the device cuts a touch off. Both drop the pull, so no note is left hanging in a stretched band.

Logic ComponentPull BackAttach to: Happy Note

Lets the player pull the waiting happy note back from the band, with the mouse (or a finger) or with the keys. Letting go puts it back for now; step 04 makes it fly. 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
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
homeX
self.pullBackData.homeX
homeY
self.pullBackData.homeY
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
homeX
self.pullBackData.homeX
homeY
self.pullBackData.homeY
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

The card’s description says step 04 makes it fly, and that’s Chapter 4 of this book.

Pull Back keeps nine things from one Event to the next: where home is, whether a pull is going, whether a key is held, and the keys’ angle and length. They live in its own Data Structure, Pull Back Data. The values of one moment, such as pointerX, are Declare Variables at the top of their Event.

Data StructurePull Back Data

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

homeXNumberdefault 0
homeYNumberdefault 0
isPulledBooleandefault false
isKeyHeldBooleandefault false
isKeyAimingBooleandefault false
keyAngleNumberdefault 40
keyPullNumberdefault 0.75
turnKeysNumberdefault 0
pullKeysNumberdefault 0

Make Pull Back Data first. Three of its fields are Booleans, and keyAngle and keyPull start at 40 and 0.75. Then make the Logic with Add > Logic Component. Its Data Structures card takes two: Shot Sums and Pull Back Data. Its fourteen Shared Variables are Constant Numbers.

The When bars are in Events: the When bars, in the paragraph that begins From Chapter 3, and eventValue is at the end of Values, quote marks and all. A few more things are new:

  • An If’s condition is one box. The press’s second If joins two tests with &&, and both go into that one box, as the card spells them.
  • On Aim At’s block, happyNote is a list, so pick self. The keys’ pointX and pointY are the longest formulas in the chapter. Type them as the card spells them. cos and sin turn an angle into across and up, and degtorad turns degrees into the measure they use.
  • Two headings, Aim 2D Axis Input and When Letgo Is Pressed, are Myth’s own names, as in Chapter 2.
  • The last three Events that drop the pull hold the same few steps each. They go quickly once the first is done.

Pull Back is long, so here’s a place to stop and try it. Once When Created, the press, the hold and the release are built, attach Pull Back, as the next part shows, and run the game. The note follows the mouse. Then come back for the keys and the last two Events.

3.5.5 The Happy Note, Changed

Now the note can take its Logic. Its card is the one at the top of this chapter, with the Data Structure and the Logic you left for later. Open the Happy Note, switch to Logic, and click Pull Back under Logic Toolbox. It brings both its Data Structures with it, the two blocks on the note’s card.

Run the game. When the note arrives, press on it and pull back. It follows you, and it won’t go farther than 1.75 meters, however far you pull. You’ll hear the band creak. Let go, and it’s back in the band. Now press Right, and the note jumps back at an angle. Hold Up and watch it swing around. Press Enter, and it springs home.

The band behind it is gone while you pull, and nothing stretches. That’s next.

3.6 The Band Stretches

Each side of the band is one Band Piece, a short stub of band picture, 0.08 meters across.

ObjectBand Piece
Transform
Bounding Box > SizeX 0.08, Y 0.13
Sprite
Scene Depth Draw Order38
SpriteBand Art
Transform > SizeX 0.08, Y 0.13
Data Structure
From LogicStretch The Band
Data StructureStretch The Band Data
Logic
Stretch The Band

3.6.1 Stretch The Band

From the Band Piece’s point of view, there’s one job. Reach from my prong to the note. It’s placed on a prong, so it remembers that spot as its prong when it’s made. Then, while you pull, it does three things every moment. It moves halfway between the prong and the aim. It turns to face the aim. And it stretches sideways until it’s as long as the gap. That’s the gap divided by its own 0.08 across, the stub’s X size, which the card calls bandThickness: the band’s thickness before it’s stretched.

That “every moment” is a Step Event, which runs on every frame of the game. But the band should only stretch during a pull. So its Step has two Modes: Slack, which does nothing, and Stretched, which does the three things. pullStarted switches to Stretched, and pullEnded back to Slack. Most of the time, the Band Piece costs nothing at all.

Logic ComponentStretch The BandAttach to: Band Piece

One side of the band: while the happy note is pulled it stretches from its prong (where it is placed) to the note.

Attach toAny Object
DATA STRUCTURES
Stretch The Band Dataself.stretchTheBandData
SHARED VARIABLES
NameTypeKindStarting Value
bandThicknessNumberConstant0.08
When Created
WhenCreate
1Change Component Value
Component Value
self.stretchTheBandData.prongX
Value
self.transform.position.x
2Change Component Value
Component Value
self.stretchTheBandData.prongY
Value
self.transform.position.y
3Set Visible
Visible
No
On Pull Started
WhenpullStartedCustom Event
1Set Visible
Visible
Yes
2Switch Mode
Event
Every Step
Mode
Stretched
On Pull Ended
WhenpullEndedCustom Event
1Set Visible
Visible
No
2Switch Mode
Event
Every Step
Mode
Slack
Every Step
WhenStep
ModeSlack (Starting Mode)
(no Actions)
Every Step
WhenStep
ModeStretched
1Set Position
X
(self.stretchTheBandData.prongX + $aimX) / 2
Y
(self.stretchTheBandData.prongY + $aimY) / 2
2Set Rotation
Degrees
radtodeg(atan2($aimY - self.stretchTheBandData.prongY, $aimX - self.stretchTheBandData.prongX))
3Set Scale
Horizontal Scale
hypot($aimX - self.stretchTheBandData.prongX, $aimY - self.stretchTheBandData.prongY) / @self.bandThickness
Vertical Scale
1

The band remembers where its prong is. That takes a small Data Structure of its own, Stretch The Band Data.

Data StructureStretch The Band Data

Object Data of Logic "Stretch The Band" (its state that lasts between Events).

prongXNumberdefault 0
prongYNumberdefault 0

Make the Band Piece, then Stretch The Band Data, two Numbers, then Stretch The Band, and attach it. Modes shows how to make the two Modes and the Switch Mode that switches them. Name Mode 1 Slack, because it’s the one the Step starts with. The card lists On Pull Started above the Step, but build the Step and its two Modes first. Then Stretched is there to pick when you make the two Custom Events. The card draws the Step twice, once for each Mode, but it’s one Event, so add Step once. The function atan2 turns a gap into an angle, and radtodeg turns that angle into degrees, which Set Rotation takes.

Back to PLACE THESE. Drag the Band Piece onto the Field twice, once for each prong. The left one goes at 2.59 and 3.93, the right one at 3.39 and 3.93.

Run the game, and pull. Two red bands reach from the prongs to the note and follow it wherever it goes. One Object, placed twice, is both sides of the band.

3.7 Where It Would Fly

Now the flip-book. Each page is an Aim Dot, a small white circle, 0.16 meters across. Its Scene Depth Draw Order of 50 draws it in front of the note and the band, so no dot hides behind them.

ObjectAim Dot
Transform
Bounding Box > SizeX 0.16, Y 0.16
Sprite
Scene Depth Draw Order50
SpriteAim Dot Art
Transform > SizeX 0.16, Y 0.16
Data Structure
From LogicShow The Flight
Data StructureDot State
Logic
Show The Flight

Make it as an Empty Object, with its Sprite, Aim Dot Art, and its sizes, the way you made the Happy Note. Like the note, it isn’t placed. Leave its Data Structure and Logic for now, because Show The Flight brings both.

3.7.1 Dot State

Each dot needs to know one thing, which dot it is. Dot 1 is nearest the band. That number is the dot’s own, so it lives on the dot, in Dot State.

Data StructureDot State

What one aiming dot knows: which dot it is, 1 for the dot nearest the band.

dotNumberNumberdefault 1

Make it, one Number that starts at 1. Show The Flight brings it to the dot.

3.7.2 Show The Flight

Dot number n shows where the note will be n times secondsBetweenDots after you let go. That’s 0.055 seconds a page. So t, the dot’s own moment, is its dotNumber times 0.055.

Across is simple. The note will have gone the launch speed across times t, from the aim. Up is that too, minus what the world has pulled it down by then: $gravity / 2 * t * t. That’s why the dots curve. The fall grows faster than the climb, and the line bends down.

A dot that would sit under the grass, past the right side of the screen, or far above it, hides. The rest show, each at its own point.

Each dot is a little smaller than the one before, so the curve fades out as it goes. That size is worked out once, on placed, from the dot’s number. Create runs while the dot is being made, before it’s told its number, so the size waits for placed, as a note’s pitch did in Chapter 2.

The Step works like the band’s. Hidden does nothing, and Shown places the dot.

Logic ComponentShow The FlightAttach to: Aim Dot

One of the white dots that show where the happy note will fly while it is pulled: dot number n is where the note will be n x secondsBetweenDots after it is let go. It reads dotNumber (its Dot State), set by Make The Aiming Dots, when the slingshot makes the dot.

Attach toAny Object
DATA STRUCTURES
Dot Stateself.dotState
SHARED VARIABLES
NameTypeKindStarting Value
secondsBetweenDotsNumberConstant0.055
firstDotScaleNumberConstant0.875
shrinkPerDotNumberConstant0.018
smallestDotScaleNumberConstant0.313
groundHeightNumberConstant1.3
screenRightNumberConstant19.2
highestShownNumberConstant12.8
When Created
WhenCreate
1Set Visible
Visible
No
On Placed
WhenplacedCustom Event
1Declare Variable
Name
dotScale
Value
1
2Change Variable
Variable
dotScale
Value
@self.firstDotScale - self.dotState.dotNumber * @self.shrinkPerDot
3Change Variable
Variable
dotScale
Value
max(@self.smallestDotScale, dotScale)
4Set Scale
Horizontal Scale
dotScale
Vertical Scale
dotScale
On Pull Started
WhenpullStartedCustom Event
1Switch Mode
Event
Every Step
Mode
Shown
On Pull Ended
WhenpullEndedCustom Event
1Set Visible
Visible
No
2Switch Mode
Event
Every Step
Mode
Hidden
Every Step
WhenStep
ModeHidden (Starting Mode)
(no Actions)
Every Step
WhenStep
ModeShown
1Declare Variable
Name
dotY
Value
0
2Declare Variable
Name
dotX
Value
0
3Declare Variable
Name
t
Value
0
4Change Variable
Variable
t
Value
self.dotState.dotNumber * @self.secondsBetweenDots
5Change Variable
Variable
dotX
Value
$aimX + $launchVX * t
6Change Variable
Variable
dotY
Value
$aimY + $launchVY * t - $gravity / 2 * t * t
7IfdotY > @self.groundHeight && dotX < @self.screenRight && dotY < @self.highestShown
Then
8Set Visible
Visible
Yes
9Set Position
X
dotX
Y
dotY
10Otherwise
11Set Visible
Visible
No

Make Show The Flight, with Dot State on its Data Structures card and its seven Shared Variables. Its Step’s Modes are Hidden, first, and Shown, and as with the band, the Step comes before the Events that switch it. Set Scale has two boxes, Horizontal Scale and Vertical Scale, and both take dotScale. Set Visible is the Yes or No list again.

Then attach it to the Aim Dot, and it brings Dot State with it.

3.7.3 Make The Aiming Dots

Thirty-three dots, each with its own number. By hand, that would be 33 placements and 33 numbers typed. So the slingshot makes them.

Picture the slingshot counting them out as it’s made, one, two, three, all the way to dotCount. Each new dot starts hidden, sitting on the slingshot, and it’s told two things: its number, and that it’s in place. The number comes from the Repeat’s count, plus 1, because the dots count from 1 and local.index counts from 0.

Logic ComponentMake The Aiming DotsAttach to: Slingshot

When the slingshot is created, it makes its aiming dots: dotCount of them, numbered from 1 (nearest the band) out, each hidden until a pull starts.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
aimDotObjectConstantNone
dotCountNumberConstant33
When Created
WhenCreate
1Repeat@self.dotCounttimes
Each time
2Create Instance
Object to Create
@self.aimDot
X
self.transform.position.x
Y
self.transform.position.y
Variable Name
newDot
3Change Variable
Variable
newDot.dotNumber
Value
local.index + 1
4Trigger Custom Event on Object
Applies to
newDot
Custom Event
placed

This works like the second half of Build The Stage. You’ve built this before. aimDot is a Shared Variable, an Object, and the Slingshot’s card sets it to Aim Dot. The Variable box takes newDot.dotNumber, the new dot’s name, a dot, and the field, as newNote.noteRow did. There’s no dotState in the middle, and that’s not a typo. Change Variable reaches a new Object’s one Data Structure by the field’s plain name, as Chapter 2 showed. Show The Flight reads the number through self, so its path has dotState in it. Then attach it to the Slingshot, under Load The Happy Note.

Run the game, and pull. A curve of white dots runs out from the note, each a little smaller, arcing up and falling toward the tower. Pull a little farther, and the curve stretches. Turn with the keys, and watch it rise and fall as the angle changes under your hands. The dots stop where they’d reach the grass.

3.8 The Call Waits for You

One thing is still rough, and you can hear it. Pull and hold, and wait. After 8 quiet seconds, the call sings, right in the middle of your aim. A call is meant to be listened to, not talked over.

3.8.1 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
1Play Piano NoteFunction
pitch
$calledPitch
2Change Component Value
Component Value
self.repeatTheCallData.repeatsLeft
Operator
+=
Value
-1
3Ifself.repeatTheCallData.repeatsLeft <= 0
Then
4Stop Recurring Timer
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

Repeat The Call now keeps one Boolean, isNoteWaiting, in Repeat The Call Data beside repeatsLeft. It’s true while a note waits in the band. Two messages make it true, happyNoteLoaded and pullEnded, and pullStarted makes it false.

When the quiet spell is over, it looks. If a note is waiting, the call plays, as before. If not, it doesn’t play. It starts its timer again, with the first ring only waitSeconds away, 1 second, and it looks again then. So the call holds its breath while you aim, and sings a second after you let go.

In the timer’s Event, every old step changes. The new ones sit inside an If. So delete the four old steps, and build the Event again from the card, top to bottom. The Else at step 6 belongs to the first If, so right-click that If when you add it. Then add the three new Events. The Boolean goes into Repeat The Call Data with Add Field, and waitSeconds onto the Logic’s Shared Variables card.

Data StructureRepeat The Call DataAddDelete

Object Data of Logic "Repeat The Call" (its state that lasts between Events).

repeatsLeftNumberdefault 0
isNoteWaitingBooleandefault false

Run the game. Wait for the call, then pull and hold, and count slowly to ten. No call comes. Let go, and a moment later, it sings.

Play it!

Watch, and listen, for each of these.

  • As Listen… shows, the happy note appears in the band, with the straight band behind it.
  • Press on the note and pull back. The band creaks, the straight band goes, and two bands stretch from the prongs to the note.
  • Pull far, much farther than the slingshot. The note stops 1.75 meters from the band and keeps the direction you pull.
  • The white dots arc out ahead of the note, each a little smaller than the last. No dot shows under the grass.
  • Pull straight down and back, and the dots climb steeply. Pull level, and they run out low.
  • Let go. The note springs back into the band, and the dots and the stretched bands vanish.
  • Press an arrow key. The note jumps back at 40 degrees. Hold Up and it swings higher, up to 80. Hold Right and Left, and the pull grows and shrinks. Enter puts it back.
  • Press high in the sky above the slingshot. No pull starts, because that part of the screen is kept for buttons.
  • Hold a pull through a quiet spell. The call waits, and sings about a second after you let go.
How it works

The slingshot loads a note when the opening ends. You pull, and Aim At turns your pull into two things: the aim, and the launch speed. Three pieces read them. The note sits at the aim, and each half of the band stretches to it. Each dot works out its own moment of the flight.

Underneath it is one idea: the dots don’t follow the note. Nothing moves along the curve. Each dot asks, on its own, where will the note be at my moment, and goes there. Thirty-three pages of a flight, each drawn by itself.

When the note flies in Chapter 4, it will follow the curve the dots have been showing all along.

3.9 If It Didn’t Work

This chapter’s slips are mostly about who hears what. A message nobody listens for, a list left on the wrong Object, a Mode that never switches. Watch what the game does, and you’ll find it.

The note never arrives? Perhaps Load The Happy Note isn’t on the Slingshot, or its happyNote box is empty. Look at its Attached Logic. Or maybe its If reads instanceCount("Happy note"), with a small n. The name must match the Object’s exactly.

Pressing does nothing? Is Pull Back on the Happy Note? Are you pressing too high, above 8.8 meters, where the buttons live? Press right on the note.

The note follows, but it won’t stop at the limit? Look at Aim At’s step 4. Its formula is min(1, maxPull / happyNote.shotSums.pullLength), with min, not max.

No bands, or bands from the wrong place? Each Band Piece remembers where it’s placed, so look at their two placements, 2.59 and 3.39. And look at Stretch The Band’s Step. Does On Pull Started’s Switch Mode pick Stretched, in its Mode list?

The bands stretch the wrong way, short and fat? Look at Set Scale. Its Horizontal Scale takes the long formula, and its Vertical Scale stays 1.

No dots? Perhaps Make The Aiming Dots isn’t attached, or Show The Flight’s Shown Mode never comes. Or perhaps all 33 dots are there, the same size, sitting at the slingshot. Then the size never came, so look at step 4 of Make The Aiming Dots, which sends placed to newDot.

The dots curve up instead of down? Look at dotY. It ends - $gravity / 2 * t * t, a minus. And is $gravity 9.8?

The keys turn the wrong way? Look at the 2D Axis Command’s rows, where each key needs the direction the card gives it.

Each of these is one small fix. Make it, run the game, and pull again. And if you’d like to compare, the catch-up download in the Goal is this chapter’s game, finished.

Challenge

The aim is yours to tune. On the Aim Dot, change secondsBetweenDots in Show The Flight from 0.055 to 0.03, and the curve grows short and close. On the Slingshot, change dotCount to 12, and only the start of the flight shows. Or on the Happy Note, change maxPull in Pull Back to 2.5, and pull deeper. Which feels best in your hand? Put everything back before Chapter 4.

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

The note waits in the band, and the dots know the way. In Chapter 4, it flies.