- Learn
- Build a Game
- Crystal Cascade
- 2 Rotating and Dropping
2 Rotating and Dropping
Your pieces rotate, and against a wall they step aside to do it. A ghost waits where each piece will land. Down hurries the piece there, and Space sends it there at once.
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. Open it and compare, or start Chapter 3 from it.
To open it, right-click the downloaded file, choose Extract All... and then Extract. Start Myth Engine, choose File › Open Project... and pick the .mythproj file in the new folder. Nunito-OFL.txt beside it comes with the game.
2.1 The Idea
A long piece falls beside the wall. You can slide it, but its shape stays the way it arrived. This chapter lets it rotate, and gives you a choice about how it falls.
An outline waits on the floor below it. That’s the ghost, showing where the piece would land if it fell straight down. Press Space and the piece lands there, leaving pale streaks of light behind. Hold Down and it falls faster instead.
Rotation needs a little room. A piece beside a wall may fit after a small step away, even when it won’t fit where it stands. That step is a kick. The game tries this rotation’s nearby places in order and keeps the first that fits.
You already have the question it needs, Piece Fits. The new parts ask that question about possible places before changing the real piece. The ghost asks it all the way down to the floor. Rotation asks it about the shape after its turn.
2.2 The Plan
- A piece spawns or slides -> Find Landing Row searches downward ->
pieceMovedredraws both the piece and its ghost. - A rotation key -> Rotate With Kicks tests nearby places. A successful turn updates the landing row and sends
pieceShifted. - Down -> a shorter gravity timer. Let go, and that same timer returns to its normal pace.
- Space ->
hardDroppeddraws the streaks -> Hard Drop Piece moves to landingRow and locks at once. - A slide or rotation after touchdown -> Piece Gravity can restart its lock delay, up to its limit.
2.3 Set Up
Continue from Chapter 1, or open its catch-up project. The art and sounds are already in the Starter Kit. Add the Commands on this setup card, keeping both keys where a Command has two bindings.
Leave PLACE THESE for the ghost part below. No other pictures need placing: the hard drop creates its own streaks when you play.
Up Arrow, XZ, ControlADown ArrowSpace2.4 Where the Piece Will Land
The outline needs an answer before it can draw anything.
Add the fields marked green in Piece. landingRow will hold the answer, while hardDropRows remembers the distance of a hard drop. The remaining fields remember touchdown and lock-delay history; we’ll use them in the last part.
The falling piece: its kind (1-7, Piece Kinds), rotation (0 up, 1 right, 2 down, 3 left), the well cell of its 4 x 4 box's bottom-left corner (column from the left, row from the bottom), how long it rests before it locks (lockDelaySeconds, read by the piece and its crystals), how long it takes to fall one row (fallSeconds), and which way the player is moving it (moveDirection: -1 left, 1 right, 0 still). landingRow: the lowest row its box can fall to (Find Landing Row). hardDropRows: the rows its last hard drop crossed. lowestRow: the lowest row it has reached; lockResets: the moves or rotations that restarted its lock delay since touching down there; hasTouchedDown: whether it has touched down since reaching that lowest row; isLockDelayOver: whether its lock delay ran out before it landed again.
2.4.1 Find Landing Row
Find Landing Row moves an imaginary copy downward, leaving the falling piece where it is. It begins at the piece’s current row. A step fits? It lowers landingRow and asks again.
The floor counts as filled, as it did in Chapter 1. Eventually the next step fails and canFall becomes false. That ends the While. The answer stays in the Piece structure, ready for several parts to share.
Its piece Parameter needs the existing Piece structure. It can then read the piece passed into the Function, without being attached to an Object itself.
Works out the lowest row the falling piece's box can fall to, straight down from where it is, and keeps it in the Piece's landingRow.
pieceData StructurePiece- Type
Boolean- Name
canFall- Value
true
- Component Value
piece.piece.landingRow- Value
piece.piece.row
- Variable
canFall- Value
true
canFall == true- Action Name
roomUnderLanding- kind
piece.piece.kind- rotation
piece.piece.rotation- column
piece.piece.column- row
piece.piece.landingRow - 1
roomUnderLanding.value == true- Component Value
piece.piece.landingRow- Value
piece.piece.landingRow - 1
- Variable
canFall- Value
false
2.4.2 Spawn Piece and Move Piece
A landing row becomes stale when the piece changes columns. Add the green Find Landing Row call to Spawn Piece and Move Piece. Each finds the new answer before sending pieceMoved, so pictures receive a whole update.
Move Piece also sends pieceShifted on its own Object. Nothing listens yet. Piece Gravity will hear it at the end of this chapter, when sliding earns more time before a lock.
Puts a new piece at the top of the well: a random kind, rotation up, its box in the hidden rows, then one row down into view if it fits.
Any Objectself.piece- Custom Event
spawnPiece
- Action Name
randomKind- Minimum
1- Maximum
dataCount('Piece Kinds')
- Component Value
self.piece.kind- Value
randomKind.value
- Component Value
self.piece.rotation- Value
0
- Component Value
self.piece.column- Value
@self.startColumn
- Component Value
self.piece.row- Value
data('Well Layout', 0, 'rows') - 4
- Action Name
roomBelowNewPiece- piece
self- columnShift
0- rowShift
-1
roomBelowNewPiece.value == true- Component Value
self.piece.row- Value
self.piece.row - 1
- piece
self
- Custom Event
pieceMoved
Moves the falling piece one column in its moveDirection when asked (moveOnce, from Read The Move Keys), if the piece fits there.
Any Objectself.piece- Action Name
roomBeside- piece
self- columnShift
self.piece.moveDirection- rowShift
0
roomBeside.value == true- Component Value
self.piece.column- Value
self.piece.column + self.piece.moveDirection
- Sound
Move Tick
- piece
self
- Custom Event
pieceMoved
- Custom Event
pieceShifted
The invisible search is ready. Now give it an outline.
2.5 The Ghost
The ghost uses the same drawing Logic as the falling crystals. It changes which row that Logic reads.
2.5.1 Show Piece Crystal
Add showsLanding as a Per Instance Variable on this Logic’s block before changing the Event that reads it. An ordinary Falling Crystal leaves it off. A Ghost Crystal turns it on, making cellRow start from landingRow instead of the moving piece’s row.
The ghost keeps its color when the piece rests. Only the falling crystals fade, and only while the space below is filled. Check the changed lockDelayStarted Event.
One crystal of the falling piece: whenever the piece moves, it goes to its cell (or, with showsLanding, to its cell in the landing row: the ghost) and shows the piece's color; it hides in the two hidden rows, and the piece's own crystals fade while it rests before locking.
Any ObjectFalling Piece- Which
Value- Value
self.transform.opacity
- Action Name
thisCrystal- kind
@self.piece.piece.kind- rotation
@self.piece.piece.rotation- crystal
self.crystal
- Name
cellColumn- Value
@self.piece.piece.column + thisCrystal.column
- Name
cellRow- Value
@self.piece.piece.row + thisCrystal.row
self.showsLanding == true- Variable
cellRow- Value
@self.piece.piece.landingRow + thisCrystal.row
- Action Name
thisCellMiddle- column
cellColumn- row
cellRow
- X
thisCellMiddle.x- Y
thisCellMiddle.y
- Frame
data('Piece Kinds', @self.piece.piece.kind - 1, 'frame')
cellRow < data('Well Layout', 0, 'visibleRows')- Visible
Yes
- Visible
No
- Value
self.transform.opacity- Target
@self.dimmedAlpha- Duration
@self.piece.piece.lockDelaySeconds
self.showsLanding == false- Action Name
roomBelowDimmingPiece- piece
@self.piece- columnShift
0- rowShift
-1
roomBelowDimmingPiece.value == false- Value
self.transform.opacity- Target
@self.dimmedAlpha- Duration
@self.piece.piece.lockDelaySeconds
Make Ghost Crystal with Ghost Blocks as its picture, and attach Show Piece Crystal. Choose Falling Piece for its piece Shared Variable. Its lower draw order keeps the outline behind the real crystals.
On4Ghost BlocksX 1.5, Y 1.5Falling PiecePlace the Ghost Crystals using PLACE THESE. Each has its own crystal number and showsLanding on. They start below the window, together, until the first message puts them at their cells.
Run the game and slide a piece. The ghost waits below the falling piece and follows it sideways, without needing any movement Logic of its own. It never starts its own gravity timer.
2.6 Rotating Beside a Wall
Now the shape can change under your fingers.
The new fields in Piece Kinds Entry name where each kind’s kicks begin and the color its hard-drop streaks will use. Add the fields first, then the values marked green in Piece Kinds.
One row of the "Piece Kinds" Data Table. Its fields are the columns of the table. The seven kinds of piece. Row index + 1 is the kind number the Well stores (1 = I ... 7 = L). frame: the crystal's frame in the sprite "Crystal Blocks". color: the kind's color, as a hex color (the color of the shards when one of its crystals shatters). rotationColumn, rotationRow: the point of its 4 x 4 box the kind rotates about (its rotations are its up shape rotated about it, Piece Shapes). firstKickRow: where this kind's quarter-rotation kicks start in the Kicks table. streakColor: the lighter tint of its hard-drop streak.
1#3cd6ff1.51.540#7fe6ff2#ffce3a1.52.50#ffe27a3#b268ff120#cf9dff4#60e25c120#9ff09a5#ff486e120#ff8aa46#3a74ff120#83a8ff7#ff9232120#ffbd7aKicks Entry gives each trial a columnShift and rowShift. Make that structure, then the Kicks table. Its CSV supplies the long list through Import CSV, with the first rows shown for comparison.
One row of the "Kicks" Data Table. Its fields are the columns of the table. Where to try a rotated piece, in order (the standard rotation system's wall kicks): columnShift to the right, rowShift up. Rows 0-39: J, L, S, T, Z and O quarter rotations; 40-79: I quarter rotations; 80-99: 180-degree rotations. Quarter rotation: row = firstKickRow (Piece Kinds) + (from rotation * 2, + 1 counterclockwise) * testsPerRotation + test. 180: row = firstHalfRotationRow + from rotation * testsPerRotation + test (Kicks Layout).
00-10-11Kicks Layout describes how that list is grouped. Make its entry structure before the table. The tests for each rotation sit together; the turning direction and current rotation tell the game which group to use.
One row of the "Kicks Layout" Data Table. Its fields are the columns of the table. How the Kicks table is laid out. One row. testsPerRotation: places tried for each rotation, in order; firstHalfRotationRow: where the 180-degree rotations start.
5802.6.1 First Kick Row
Picture the Kicks table as groups of nearby places, one group after another. First Kick Row finds where this turn’s group begins. It counts the earlier groups.
The half turn uses its own part of the table. The Function returns one number, firstTestRow, leaving the actual trials to the next Function. The long sum is counting rows, rather than moving the piece.
Answers where the five kicks of a rotation start in the Kicks table, for a piece of this kind rotating by rotateBy from this rotation (its layout is in Kicks Layout).
- Name
firstTestRow- Value
0
rotateBy == 2- Variable
firstTestRow- Value
data('Kicks Layout', 0, 'firstHalfRotationRow')
- Variable
firstTestRow- Operator
+=- Value
rotation * data('Kicks Layout', 0, 'testsPerRotation')
- Variable
firstTestRow- Value
data('Piece Kinds', kind - 1, 'firstKickRow')
- Variable
firstTestRow- Operator
+=- Value
rotation * 2 * data('Kicks Layout', 0, 'testsPerRotation')
rotateBy == (-1)- Variable
firstTestRow- Operator
+=- Value
data('Kicks Layout', 0, 'testsPerRotation')
- Value
firstTestRow
2.6.2 Rotate With Kicks
Start with the piece against the wall. The new rotation may fail in its present place. The first trial asks about that place anyway; the next asks about a shifted place. A later one might fit above or beside it.
Each time, Piece Fits sees the proposed rotation and the trial’s shifts. The real piece waits. Then its column, row and rotation change together, and hasRotated stops the search. If every trial fails, the piece stays as it was.
That is why the While has two tests. It continues only while there has been no success and untried places remain. After a success, Find Landing Row updates the ghost before the pictures redraw. It sends pieceShifted, as sliding does.
Rotates a piece by rotateBy quarter rotations (1 clockwise, -1 counterclockwise, 2 for 180 degrees) the standard way: it tries the five places of the Kicks table for this rotation in order and takes the first where the piece fits; where none fits, the piece does not rotate.
pieceData StructurePiece- Name
newRotation- Value
0
- Name
firstTestRow- Value
0
- Name
test- Value
0
- Type
Boolean- Name
hasRotated- Value
false
- Variable
newRotation- Value
mod(piece.piece.rotation + rotateBy + 4, 4)
- Action Name
kicksStart- kind
piece.piece.kind- rotation
piece.piece.rotation- rotateBy
rotateBy
- Variable
firstTestRow- Value
kicksStart.value
- Variable
test- Value
0
- Variable
hasRotated- Value
false
hasRotated == false && test < data('Kicks Layout', 0, 'testsPerRotation')- Action Name
roomAfterRotation- kind
piece.piece.kind- rotation
newRotation- column
piece.piece.column + data('Kicks', firstTestRow + test, 'columnShift')- row
piece.piece.row + data('Kicks', firstTestRow + test, 'rowShift')
roomAfterRotation.value == true- Component Value
piece.piece.column- Value
piece.piece.column + data('Kicks', firstTestRow + test, 'columnShift')
- Component Value
piece.piece.row- Value
piece.piece.row + data('Kicks', firstTestRow + test, 'rowShift')
- Component Value
piece.piece.rotation- Value
newRotation
- Variable
hasRotated- Value
true
- Variable
test- Operator
+=- Value
1
hasRotated == true- Sound
Rotate Chime
- piece
piece
- Custom Event
pieceMoved
- Applies to
piece- Custom Event
pieceShifted
2.6.3 Rotate Piece
The keys can all ask the same Function. Rotate Piece passes a clockwise, counterclockwise or half-turn request to Rotate With Kicks. The Function checks the room. These Events hear the keys.
Attach the new Logic to Falling Piece before building its Events, so it can read the Piece structure already there. We’ll attach the other new Logics below in the same order as the Object’s Changes card.
Rotates the falling piece when a Rotate key is pressed: clockwise, counterclockwise or 180 degrees (Rotate With Kicks).
Any Objectself.piece- piece
self- rotateBy
1
- piece
self- rotateBy
-1
- piece
self- rotateBy
2
Run again and rotate a piece in the middle, then near a wall. Watch the shape and ghost change together. A turn that finds no room leaves both where they were.
2.7 Choosing the Fall
Down hurries a fall. Space skips the wait altogether.
2.7.1 Soft Drop Piece
Both choices use the gravity timer from Chapter 1, with no new timer pulling against it. Soft Drop Piece restarts that timer with a shorter interval while Down is held. Releasing Down restores the ordinary fall time. Two different movers would fight over the same piece, so this Logic leaves movement to Piece Gravity.
Make Soft Drop Piece Data first. Supply it through the Logic’s Data Structures block. Its switch remembers whether Down is held between Events, including the arrival of another piece. Set the Shared Variables on the Logic, then attach it after Piece Gravity so its faster timer starts last.
Object Data of Logic "Soft Drop Piece" (its state that lasts between Events). isSoftDropping: Soft Drop is held, so the piece falls faster.
While Soft Drop is held, the falling piece falls speedUp times faster (at least one row every slowestSeconds); letting go returns it to its normal fall.
Any Objectself.pieceself.softDropPieceData- Component Value
self.softDropPieceData.isSoftDropping- Value
true
- Custom Event
fallFaster
- Custom Event
fallNormally
- Component Value
self.softDropPieceData.isSoftDropping- Value
false
- Timer Name
"gravity"- Repeat Every (s)
self.piece.fallSeconds
self.softDropPieceData.isSoftDropping == true- Custom Event
fallFaster
- Name
fasterSeconds- Value
0
- Variable
fasterSeconds- Value
self.piece.fallSeconds / @self.speedUp
fasterSeconds > @self.slowestSeconds- Variable
fasterSeconds- Value
@self.slowestSeconds
- Timer Name
"gravity"- Repeat Every (s)
fasterSeconds
The first new piece should hurry too when you keep Down held. Try releasing it midway through a fall. The same piece returns to its normal pace.
2.7.2 Fade Away
A hard drop leaves light behind, but the light shouldn’t fill the well forever. Make Drop Trail for the sparks. Its tiny pictures come from FX Soft Dot in the Starter Kit.
08rectanglew 1.219, h 0.281min 180, max 460min 2.813, max 15min 82, max 98FX Soft Dot#C8F4FFMultiple Colors#FFFFFF, #C8F4FF, #8FE3FF1.3start 0.45, end 0start 0.85, end 0Fade Away starts a streak bright, fades it, then removes that streak Object. The tween and removal timer have different jobs. The picture can become invisible before the Object leaves the game.
A short-lived effect: it fades out over fadeSeconds and removes itself after removeSeconds.
Any Object- Alpha
@self.startAlpha
- Value
self.transform.opacity- Target
0- Duration
@self.fadeSeconds- Ease
Quad in
- Timer Name
"remove"- Repeat Every (s)
@self.removeSeconds- Ring Once
Yes
Make Drop Streak and attach Fade Away. This is an Object you keep in Resources without placing it in the Scene. The game will create each streak when a drop needs it.
On7Drop Streak ArtX 1.5, Y 62.7.3 Hard Drop Piece
Press Space. The piece is at the ghost before you can watch it fall. Hard Drop Piece uses the answer already found by Find Landing Row, then sends lockPiece without waiting for the lock delay.
It measures hardDropRows before moving. Its hardDropped message also goes out first, while the old row and the landing row are both known. That gives the streaks enough information to cover the fall.
When Hard Drop is pressed, the falling piece drops straight to its landing row and locks at once, with a thud and a small shake.
Any Objectself.piece- Component Value
self.piece.hardDropRows- Value
self.piece.row - self.piece.landingRow
- Custom Event
hardDropped
- Component Value
self.piece.row- Value
self.piece.landingRow
- Custom Event
pieceMoved
- Sound
Hard Drop
- Intensity
@self.shakeStrength- Seconds
@self.shakeSeconds
- Custom Event
lockPiece
2.7.4 Show Drop Streaks
Each crystal gets a ribbon of pale light spanning the distance it crossed. Show Drop Streaks puts a new streak at the middle of that distance, stretches its height, and gives it the kind’s streakColor.
The Repeat creates one for each crystal, using Crystal Cell again. Its created target tells the scale and tint Actions which new Object to change. A small burst of Drop Trail marks the landing. Fade Away takes care of each streak afterward.
Declare streak on this Logic as a Constant Object Variable with the Drop Streak kind. Then attach Hard Drop Piece and Show Drop Streaks to Falling Piece. Choose Drop Streak for streak.
Draws a hard drop: a streak over the rows each crystal of the piece crosses, in a lighter tint of its color, and a puff of sparks where it lands.
Any Objectself.pieceDrop Streakself.piece.hardDropRows > 0- Name
streakScale- Value
self.piece.hardDropRows + 1
- Variable
streakScale- Operator
*=- Value
data('Well Layout', 0, 'cellSize') / @self.streakArtMeters
4times- Action Name
droppedCrystal- kind
self.piece.kind- rotation
self.piece.rotation- crystal
local.index + 1
- Name
crystalColumn- Value
self.piece.column + droppedCrystal.column
- Name
crystalRow- Value
self.piece.landingRow + droppedCrystal.row
- Action Name
streakMiddle- column
crystalColumn- row
crystalRow + self.piece.hardDropRows / 2
- Object to Create
@self.streak- X
streakMiddle.x- Y
streakMiddle.y
- Applies to
created- Vertical Scale
streakScale
- Applies to
created- Value
data('Piece Kinds', self.piece.kind - 1, 'streakColor')
- Applies to
created- Property
Enabled- Value
true
- Action Name
sparksPlace- column
crystalColumn- row
crystalRow + @self.sparksCellsAboveMiddle
- Particles
Drop Trail- Position Relative To
World- Z Index Offset
6- X
sparksPlace.x- Y
sparksPlace.y
Check the Object card next. Check their order, then set streak as its last block shows. The green lines join your existing attachments, keeping the old Logics in place as the new ones join them.
OnPieceRead The Move KeysRead The Move Keys DataSoft Drop PieceSoft Drop Piece DataDrop StreakRun it and press Space. The stack gains a piece immediately, and its ribbons fade above it. A drop already at the floor needs no ribbon.
2.8 More Time to Slide
The floor leaves room for another choice.
2.8.1 Note Lowest Row
A piece may touch down, slide off a ledge, and land lower. Note Lowest Row remembers that progress. A new lowest row clears the old touchdown history and stops the old delay, so the next landing starts fresh.
At the same height, nothing changes. A kick that raises the piece leaves this memory alone too, because lowestRow remembers progress downward.
When the falling piece reaches a new lowest row, remembers it, clears its touchdown and lock-delay history, and stops the old delay. Its next landing gets a fresh delay and restart count.
pieceData StructurePiecepiece.piece.row < piece.piece.lowestRow- Component Value
piece.piece.lowestRow- Value
piece.piece.row
- Component Value
piece.piece.lockResets- Value
0
- Component Value
piece.piece.hasTouchedDown- Value
false
- Component Value
piece.piece.isLockDelayOver- Value
false
- Applies to
piece- Timer Name
"lockDelay"
2.8.2 Piece Gravity
Remember pieceShifted from sliding and rotating? This is its listener. After touchdown, a successful shift can restart the lock delay. Piece Gravity counts those restarts in lockResets, up to lockResetLimit, so a piece eventually has to lock.
Landing and shifting ask different questions. A first landing starts the delay. A shift can restart it; reaching a lower row clears that history. Once the delay has run out, another landing at that same height locks instead of inventing a new pause.
Add Note Lowest Row before its new calls in the Changes card. Keep the Falling and Resting Modes you already have. The new On Piece Shifted Event decides whether to return to Falling or to send pieceLanded again.
Moves the falling piece down one row whenever the gravity timer comes due; when it cannot fall it rests, and locks after the lock delay.
Any Objectself.piece- Timer Name
"gravity"- Repeat Every (s)
self.piece.fallSeconds
- Event
On the Gravity Timer- Mode
Falling
- Component Value
self.piece.lowestRow- Value
self.piece.row
- Component Value
self.piece.lockResets- Value
0
- Component Value
self.piece.hasTouchedDown- Value
false
- Component Value
self.piece.isLockDelayOver- Value
false
- Action Name
roomBelowOnGravity- piece
self- columnShift
0- rowShift
-1
roomBelowOnGravity.value == true- Component Value
self.piece.row- Value
self.piece.row - 1
- piece
self
- Custom Event
pieceMoved
- Action Name
roomBelowAfterFall- piece
self- columnShift
0- rowShift
-1
roomBelowAfterFall.value == false- Custom Event
pieceLanded
- Custom Event
pieceLanded
- Event
On the Gravity Timer- Mode
Resting
- Timer Name
"lockDelay"- Repeat Every (s)
self.piece.lockDelaySeconds- Ring Once
Yes
- Custom Event
lockDelayStarted
self.piece.hasTouchedDown == false- Component Value
self.piece.hasTouchedDown- Value
true
- Timer Name
"lockDelay"- Repeat Every (s)
self.piece.lockDelaySeconds- Ring Once
Yes
- Custom Event
lockDelayStarted
self.piece.isLockDelayOver == true- Custom Event
lockPiece
- Component Value
self.piece.isLockDelayOver- Value
true
- Action Name
roomBelowAfterDelay- piece
self- columnShift
0- rowShift
-1
roomBelowAfterDelay.value == true- Event
On the Gravity Timer- Mode
Falling
- Custom Event
lockPiece
- piece
self
self.piece.hasTouchedDown == trueself.piece.lockResets < @self.lockResetLimit- Component Value
self.piece.lockResets- Value
self.piece.lockResets + 1
- Component Value
self.piece.isLockDelayOver- Value
false
- Timer Name
"lockDelay"- Repeat Every (s)
self.piece.lockDelaySeconds- Ring Once
Yes
- Custom Event
lockDelayStarted
- Action Name
roomBelowAfterShift- piece
self- columnShift
0- rowShift
-1
roomBelowAfterShift.value == true- Event
On the Gravity Timer- Mode
Falling
- Custom Event
pieceLanded
Run the game. Slide a piece along the floor before it locks, and its small pause begins again. The piece waits, within its limit.
2.9 Play It!
- Move a piece sideways and watch its ghost choose the new landing place.
- Try every rotation Command. Compare open space with a spot beside a wall, watching where the piece steps when it rotates. A successful kick shifts the piece and its ghost together.
- Hold Down through a lock. The next piece hurries too; releasing Down restores ordinary gravity.
- Press Space above the floor. The piece locks at the ghost, a thud sounds, and pale ribbons fade above the landing.
- Try a hard drop without any space beneath the piece. No tall trail should appear.
- Slide or rotate after touchdown, before the delay ends. The delay can restart, and moving lower gives the piece a fresh history.
2.10 How It Works
The ghost waits at landingRow. Hard drop uses that same answer. Rotation and sliding keep it current whenever the piece changes its place or shape.
The search happens before the movement. A trial can fail without leaving anything to undo, because the real piece stays where it was until the answer is yes.
2.11 If It Didn’t Work
The ghost sits on the piece? Check showsLanding on each Ghost Crystal placement. It belongs on those outlines, while the falling crystals leave it off. Turn it on there and run again; the outline can now read landingRow.
The ghost stays behind after a slide? Look for Find Landing Row in Move Piece, before pieceMoved. Restore that call, then slide again. The new answer reaches both sets of pictures, keeping the ghost beneath the piece as you move it.
Does holding Down slow the next piece? Check the Attached Logic order on Falling Piece. Soft Drop Piece must follow Piece Gravity, so it can restart the timer after gravity starts it normally.
A hard drop draws no light? Look at Falling Piece’s streak Shared Variable. Choose the Drop Streak resource, then try a drop with room beneath it. That gives Show Drop Streaks something to create.
No extra time after sliding? Check that sliding and successful rotation send pieceShifted on Falling Piece. The listener is in Piece Gravity. Bring back the missing message or listener, then try the same slide again.
Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).
2.12 Challenge
What should a hard drop leave behind in your own game? Try another streak color or a different fade. You could make a quick flash, or let the light linger long enough to show the last move. Keep a working copy while you try it.
Your pieces can now rotate beside a wall, hurry down, or drop straight to their ghost. Next, full rows finally break.
