- Learn
- Build a Game
- Crystal Cascade
- 4 The Queue and Hold
4 The Queue and Hold
Pieces come from shuffled bags of seven. The Next panel shows you the five on the way, and Hold keeps one piece aside for when you need it.
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 5 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.
4.1 The Idea
A piece appears at the rim, and until now it could be any kind at all. You plan a gap for a long piece and wait for one. This chapter gives you a little more to plan with.
The Next panel shows pieces on their way, while Hold keeps one aside until you need it. Behind those pictures, the game deals from a shuffled bag containing every kind once. It finishes that bag before starting the next.
Think of shuffling a small deck of cards, then dealing from the top until the deck is gone. The order changes, but no card can be dealt twice before that deck runs out. A new bag begins with the same set, shuffled again.
The Queue is the line of pieces waiting. Its entries are numbers. The Next panel reads those numbers and draws the shapes. Hold can change that choice.
4.2 The Plan
- Add A Shuffled Bag writes each kind once, then swaps their places randomly.
- Deal Pieces fills Queue ->
nextPieceDuegives Falling Piece its first kind ->pieceDealtmoves the line forward. queueChangedmakes Preview Crystals read their slots and redraw.- Hold swaps the moving and held kinds. An empty Hold takes the next queued kind instead.
- A lock clears the used-Hold flag, so the next piece can be held once again.
4.3 Set Up
Continue from Chapter 3 or its catch-up project. Add Hold with both of its keybindings, so either key can ask the same Logic to keep a piece. The panels and Hold Swish are already in the Starter Kit.
The setup list has three visits. Place Piece Queue when dealing works, then Next Panel and its previews, then the Hold previews and panel. The chapter will bring you back each time. A Preview Crystal’s placement values choose its slot and which crystal of that shape it shows.
C, Shift4.4 A Place for the Held Kind
Make room in the piece’s memory first.
Add heldKind and hasHeld to Piece. heldKind starts empty; hasHeld remembers whether this moving piece has already used Hold. The Boolean field’s false default leaves its switch off.
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. heldKind: the piece put away with Hold (0 none); hasHeld: Hold was used for this piece.
Hold Piece Data keeps comingBack, the kind waiting to return during a swap. Make that structure and a Hold Piece Logic resource by name. Supply the structure on that Logic, then attach Hold Piece to Falling Piece after Show Drop Streaks. Its Actions come in the Hold part below.
Object Data of Logic "Hold Piece" (its state that lasts between Events). comingBack: the kind of the held piece that comes back into play.
OnPieceRead The Move KeysRead The Move Keys DataSoft Drop PieceSoft Drop Piece DataHold PieceHold Piece DataDrop StreakThe other Logics can now name Hold Piece when they enable or disable it. Nothing needs to listen to the Hold key yet.
4.5 Pieces From a Bag
Choose the next kind in advance.
Make Queue Entry, with its piece field, then the Queue table. Download its CSV and use Import CSV to make the waiting rows, with their default empty values, before any bags are dealt. Their starting values are empty places, ready for the first bags.
One row of the "Queue" Data Table. Its fields are the columns of the table. The pieces to come, in order: row 0 is the next one. piece: a kind (Piece Kinds), 0 for an empty row. Two bags of seven fit; Deal Pieces adds a bag whenever fewer than seven are left.
000The waiting places are ready.
4.5.1 Add A Shuffled Bag
First lay out each kind once in a run of Queue rows. The run starts at firstPlace, so the Function can add a bag after pieces already waiting. Then shuffle only that run.
Imagine a finger on its last place. Pick a random place from there back to the beginning, and swap the two kinds. Move the finger back one place and repeat. The part behind the finger is finished; the remaining places can still trade.
The swap needs to remember both kinds before writing either one. swapKind and otherKind hold them while their rows exchange values. Otherwise the first write would lose the kind needed for the second.
Writes one bag into the Queue table from place firstPlace on: one piece of each kind (Piece Kinds), shuffled so every order is equally likely.
- Name
placeRow- Value
0
- Name
placeKind- Value
0
- Name
shuffleIndex- Value
0
- Name
swapRow- Value
0
- Name
otherRow- Value
0
- Name
swapKind- Value
0
- Name
otherKind- Value
0
dataCount('Piece Kinds')times- Variable
placeRow- Value
firstPlace + local.index
- Variable
placeKind- Value
local.index + 1
- Table
Queue- Row
placeRow- Column
"piece"- Value
placeKind
- Variable
shuffleIndex- Value
dataCount('Piece Kinds') - 1
shuffleIndex > 0- Action Name
swapPlace- Maximum
shuffleIndex
- Variable
swapRow- Value
firstPlace + shuffleIndex
- Variable
otherRow- Value
firstPlace + swapPlace.value
- Variable
swapKind- Value
data('Queue', swapRow, 'piece')
- Variable
otherKind- Value
data('Queue', otherRow, 'piece')
- Table
Queue- Row
swapRow- Column
"piece"- Value
otherKind
- Table
Queue- Row
otherRow- Column
"piece"- Value
swapKind
- Variable
shuffleIndex- Operator
-=- Value
1
4.5.2 Deal Pieces
Make Deal Pieces Data before this Logic. Its queueLength remembers how many places hold waiting pieces. Supply it through the Logic’s Data Structures block, then attach Deal Pieces to a new Piece Queue Object.
Object Data of Logic "Deal Pieces" (its state that lasts between Events). queueLength: how many pieces wait in the Queue. placeRow: the place of the Queue being filled.
OnDeal PiecesDeal Pieces DataDeal Pieces starts by adding bags, keeping the waiting line long enough for the preview. It asks for the first piece only after filling Queue. There’s no picture on Piece Queue, but without this quiet work nothing would arrive.
Keeps the Queue table full of pieces from shuffled bags of seven: deals two bags at the start, takes the first piece off whenever one is dealt, and adds a bag whenever fewer than seven are left.
Any Objectself.dealPiecesData- Component Value
self.dealPiecesData.queueLength- Value
0
2times- firstPlace
self.dealPiecesData.queueLength
- Component Value
self.dealPiecesData.queueLength- Operator
+=- Value
dataCount('Piece Kinds')
- Custom Event
nextPieceDue
When a piece is dealt, every kind behind it moves one place forward. The last occupied place empties, and queueLength shrinks. If the line runs short, Add A Shuffled Bag fills its tail again before queueChanged redraws the preview.
The shift starts at the front and reads the next place before replacing the current one. That direction preserves the rest of the line until its turn comes.
- Name
placeRow- Value
0
- Variable
placeRow- Value
0
placeRow < (self.dealPiecesData.queueLength - 1)- Name
placeKind- Value
data('Queue', placeRow + 1, 'piece')
- Table
Queue- Row
placeRow- Column
"piece"- Value
placeKind
- Variable
placeRow- Operator
+=- Value
1
- Table
Queue- Row
placeRow- Column
"piece"
- Component Value
self.dealPiecesData.queueLength- Operator
-=- Value
1
self.dealPiecesData.queueLength < dataCount('Piece Kinds')- firstPlace
self.dealPiecesData.queueLength
- Component Value
self.dealPiecesData.queueLength- Operator
+=- Value
dataCount('Piece Kinds')
- Custom Event
queueChanged
4.5.3 Spawn Piece
Spawn Piece stops choosing a random kind for itself. On nextPieceDue, it takes the first kind from Queue, tells Deal Pieces that one was dealt, and prepares the falling shape.
Its old When Created sender comes out. Piece Queue now asks for the first piece when its bags are ready. The ordinary spawnPiece response still places and draws whatever kind the piece already holds, so Hold can use it later without taking from Queue.
Puts a new piece at the top of the well: the next kind from the Queue (or the held piece, from Hold Piece), rotation up, its box in the hidden rows, then one row down into view if it fits.
Any Objectself.piece- Custom Event
spawnPiece
- Logic Component
Hold Piece
- Logic Component
Piece Gravity
- Logic Component
Move Piece
- Logic Component
Rotate Piece
- Logic Component
Hard Drop Piece
- 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
- Component Value
self.piece.kind- Value
data('Queue', 0, 'piece')
- Custom Event
pieceDealt
- Custom Event
spawnPiece
4.5.4 Clear Full Lines
The well’s entry delay now asks for nextPieceDue. Look at the card’s last Event. A new piece comes through Queue before Spawn Piece prepares it.
The line clear: after each lock it marks the full lines (they shatter); clearSeconds later every row that stays moves down into the lowest free row and the rows left at the top are emptied; entrySeconds after that (or after a lock with no full line) it asks for the next piece.
Any Objectself.wellState- Component Value
self.wellState.linesCleared- Value
0
data('Well Layout', 0, 'rows')times- Name
checkRow- Value
local.index
- Action Name
checkedRow- row
checkRow
checkedRow.value == true- Component Value
self.wellState.linesCleared- Value
self.wellState.linesCleared + 1
- row
checkRow
- Action Name
fullRowMiddle- column
(data('Well Layout', 0, 'columns') - 1) / 2- row
checkRow
- Particles
Row Sparkle- Position Relative To
World- X
fullRowMiddle.x- Y
fullRowMiddle.y- Z Index Offset
6
self.wellState.linesCleared > 0- Custom Event
wellChanged
- Intensity
@self.shakePerLine * self.wellState.linesCleared- Seconds
@self.shakeSeconds
self.wellState.linesCleared == 4- Color
@self.flashColor- Seconds
@self.flashSeconds
- Custom Event
linesCleared
- Timer Name
"clearDelay"- Repeat Every (s)
@self.clearSeconds- Ring Once
Yes
- Timer Name
"entryDelay"- Repeat Every (s)
@self.entrySeconds- Ring Once
Yes
- Name
targetRow- Value
0
- Variable
targetRow- Value
0
data('Well Layout', 0, 'rows')times- Name
checkRow- Value
local.index
- Action Name
rowStart- column
0- row
checkRow
data('Well', rowStart.value, 'isClearing') == false- fromRow
checkRow- toRow
targetRow
- Variable
targetRow- Operator
+=- Value
1
targetRow < data('Well Layout', 0, 'rows')- row
targetRow
- Variable
targetRow- Operator
+=- Value
1
- Custom Event
wellChanged
- Timer Name
"entryDelay"- Repeat Every (s)
@self.entrySeconds- Ring Once
Yes
- Custom Event
nextPieceDue- Custom Event
spawnPiece
Place Piece Queue using its setup line. Run the game and play through a bag. Every kind arrives before that bag’s next copy, though equal kinds can meet where one bag ends and another starts.
4.6 Seeing What Comes Next
The waiting numbers get small pictures beside the well.
Each shape needs a middle for its preview box. Add the marked fields to Piece Kinds Entry, then their values to Piece Kinds. middleColumn and middleRow describe the whole shape’s center, rather than its turning point.
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. middleColumn, middleRow: the middle of the piece in its box (rotation up), to center it in a preview.
1#3cd6ff1.51.540#7fe6ff22.52#ffce3a1.52.50#ffe27a233#b268ff120#cf9dff1.534#60e25c120#9ff09a1.535#ff486e120#ff8aa41.536#3a74ff120#83a8ff1.537#ff9232120#ffbd7a1.53Make Preview Slots Entry and Preview Slots. Each slot has a middle. It also names a crystal size and which waiting place its box shows. The Hold slot asks for the held kind instead of Queue.
One row of the "Preview Slots" Data Table. Its fields are the columns of the table. The preview boxes: isHold marks the Hold box (it shows the held piece); otherwise queuePlace is the Queue row the box shows (0 = the next piece); middleX, middleY the box's middle (meters); cellSize the size of one crystal there.
No043.26562528.68751.3125No143.26562524.843750.984375No243.26562521.281250.984375No343.26562517.718750.984375No443.26562514.156250.984375Yes016.73437528.68751.31254.6.1 Preview Crystal Place
A long piece and a square should both sit in the middle of their boxes. Preview Crystal Place asks Crystal Cell for one crystal’s place, then measures its offset from the shape’s middle. It scales that offset to the slot’s crystal size and adds the slot’s middle.
Make Preview Crystal Place Result first, holding the answer’s X and Y. Then build the Function with that Return Type. The same calculation serves both Next and Hold.
What the Function "Preview Crystal Place" returns (its former Outputs). x, y: where one crystal of the piece in a Next or Hold box is drawn, in meters.
Answers where one crystal of a piece (rotation up) sits in a preview box (Preview Slots), the piece centered in the box at the box's cell size: x and y in meters.
- Name
offsetColumns- Value
0
- Name
offsetRows- Value
0
- Action Name
previewCell- kind
kind- rotation
0- crystal
crystal
- Variable
offsetColumns- Value
previewCell.column + 0.5
- Variable
offsetColumns- Operator
-=- Value
data('Piece Kinds', kind - 1, 'middleColumn')
- Variable
offsetRows- Value
previewCell.row + 0.5
- Variable
offsetRows- Operator
-=- Value
data('Piece Kinds', kind - 1, 'middleRow')
- Variable
offsetColumns- Operator
*=- Value
data('Preview Slots', slot, 'cellSize')
- Variable
offsetRows- Operator
*=- Value
data('Preview Slots', slot, 'cellSize')
- x
data('Preview Slots', slot, 'middleX') + offsetColumns- y
data('Preview Slots', slot, 'middleY') + offsetRows
4.6.2 Show Preview Crystal
Make Show Preview Crystal Data, which remembers the kind this picture is about to draw. Supply it on this Logic’s Data Structures block. Declare its Shared and Per Instance Variables as the card shows before building the Events.
Object Data of Logic "Show Preview Crystal" (its state that lasts between Events). previewKind: the kind of piece its box shows (0: none).
When created, a Preview Crystal scales itself for its slot. It then asks its own queueChanged listener to draw the first picture. Both Events use the same drawing.
One crystal of a preview: the piece in its Queue place, or the held piece in the Hold box (Preview Slots), centered in its box at that box's cell size; hidden while there is no piece; the held piece dims once Hold was used for the falling piece.
Any Objectself.showPreviewCrystalDataFalling Piece- Name
boxScale- Value
0
- Variable
boxScale- Value
data('Preview Slots', self.slot, 'cellSize')
- Variable
boxScale- Operator
/=- Value
data('Well Layout', 0, 'cellSize')
- Horizontal Scale
boxScale- Vertical Scale
boxScale
- Custom Event
queueChanged
On an update, the slot chooses between Queue and heldKind. A nonempty kind gets a position from Preview Crystal Place and a frame from Piece Kinds. An empty kind hides its crystals, leaving the Hold box bare at the start.
A used Hold dims. That makes the rule visible before the player tries the key again. A holdChanged message reaches this same drawing path.
data('Preview Slots', self.slot, 'isHold') == true- Component Value
self.showPreviewCrystalData.previewKind- Value
@self.piece.piece.heldKind
- Component Value
self.showPreviewCrystalData.previewKind- Value
data('Queue', data('Preview Slots', self.slot, 'queuePlace'), 'piece')
self.showPreviewCrystalData.previewKind > 0- Action Name
myPlace- kind
self.showPreviewCrystalData.previewKind- crystal
self.crystal- slot
self.slot
- X
myPlace.x- Y
myPlace.y
- Frame
data('Piece Kinds', self.showPreviewCrystalData.previewKind - 1, 'frame')
data('Preview Slots', self.slot, 'isHold') == true && @self.piece.piece.hasHeld == true- Alpha
@self.usedAlpha
- Visible
Yes
- Visible
No
- Custom Event
queueChanged
Make Preview Crystal and attach the drawing Logic, then choose Falling Piece for its piece Shared Variable. That supplies the Object it watches. Every preview reads that same moving piece’s heldKind and hasHeld fields, while each placement keeps its own slot and crystal number.
On8Crystal BlocksX 1.5, Y 1.5Show Preview CrystalShow Preview Crystal DataFalling PieceMake Next Panel and place it from the setup list. The previews have two placement routes. Their Logic is the same either way.
The Preview Layout pack places all the Next and Hold crystals for you. Build the resources above first. With Gameplay open, use File > Install Myth Pack to choose this layout download. Install it before placing any Preview Crystals by hand, so the pack supplies the only set of previews.
It adds named Object copies with slot and crystal already set. The copies share Show Preview Crystal, so later changes to that Logic reach them too. Their Physics Object switch is already Off. Continue building Hold below, but skip the setup card’s Preview Crystal placements for both panels. You still place Next Panel and Hold Panel yourself.
For the card route, place the Next previews from the setup list. They start together low on the panel. Their Logic moves them into the slots when the game begins.
-20Next PanelX 8.9063, Y 22.0313Duplicate a placed Preview Crystal to keep its values, then change the slot and crystal values where the card does. Work through each slot’s crystals before starting another slot. Run the game and watch the front shape leave Next as the next one takes its place.
4.7 Keeping a Piece for Later
The empty Hold box can now accept a shape.
4.7.1 Hold Piece
Press Hold for the first time. The moving kind goes into heldKind, and the next kind comes from Queue. After that, Hold trades two numbers: the moving kind and the kind kept aside.
comingBack keeps the returning kind safe. Spawn Piece puts the returning shape upright at the top, giving it the same start as a newly dealt piece. It resets the shape’s place without dealing another piece.
The hasHeld field guards the whole request. Hold now waits. Once that field is true, another press during this piece does nothing. Its dimmed box explains the wait.
When Hold is pressed (once per piece): puts the falling piece away; the first time the next piece comes from the Queue, after that the held piece comes back in its place.
Any Objectself.pieceself.holdPieceDataself.piece.hasHeld == false- Component Value
self.piece.hasHeld- Value
true
- Sound
Hold Swish
- Timer Name
"gravity"
- Timer Name
"lockDelay"
self.piece.heldKind == 0- Component Value
self.piece.heldKind- Value
self.piece.kind
- Custom Event
nextPieceDue
- Component Value
self.holdPieceData.comingBack- Value
self.piece.heldKind
- Component Value
self.piece.heldKind- Value
self.piece.kind
- Component Value
self.piece.kind- Value
self.holdPieceData.comingBack
- Custom Event
spawnPiece
- Custom Event
holdChanged
4.7.2 Lock Piece
A lock makes the next turn ready for Hold again. The new changes disable Hold while the well checks its lines, clear hasHeld, and send holdChanged. Spawn Piece’s new Enable Action wakes Hold when the next piece arrives.
Writes the falling piece's four crystals into the Well, takes the piece out of play, tells the well it locked and the Well Cells that crystals joined.
Any Objectself.piece- Name
lockedKind- Value
0
- Timer Name
"gravity"
- Timer Name
"lockDelay"
- Variable
lockedKind- Value
self.piece.kind
4times- Action Name
lockedCrystal- kind
self.piece.kind- rotation
self.piece.rotation- crystal
local.index + 1
- Action Name
lockedCell- column
self.piece.column + lockedCrystal.column- row
self.piece.row + lockedCrystal.row
- Name
cellIndex- Value
lockedCell.value
- Table
Well- Row
cellIndex- Column
"kind"- Value
lockedKind
- Sound
Lock Click
- Logic Component
Piece Gravity- State
Disable
- Logic Component
Move Piece- State
Disable
- Logic Component
Rotate Piece- State
Disable
- Logic Component
Hard Drop Piece- State
Disable
- Logic Component
Hold Piece- State
Disable
- Component Value
self.piece.hasHeld- Value
false
- Custom Event
holdChanged
- Custom Event
pieceLocked
- Custom Event
crystalsLocked
Make Hold Panel now. Place it from the setup list. Add the Hold preview placements too if you chose the card route; the layout pack already placed them. The same Preview Crystal Object serves both panels. The slot values decide which panel each placement serves, using the same calculation with a different place and kind.
-20Hold PanelX 8.9063, Y 7.0313Run the game, keep a piece, then lock the replacement. Hold can now bring that saved shape back when you need it.
4.8 Play It!
- Watch Next before locking. Its front shape becomes the next falling piece, and the other previews advance.
- Play through several bags. Each bag contains every kind once, though a bag boundary can bring two equal kinds together.
- Use Hold with an empty box. The moving kind appears there and the next queued piece arrives.
- Press Hold again before locking. Watch the dimmed preview keep the same shape, refusing another trade until the current piece has locked.
- Lock a piece, then use Hold. The saved kind returns upright, and the moving kind takes its place in the box.
- Compare the previews for different shapes. Each sits centered in its own slot, with the first Next slot larger.
4.9 How It Works
The bag is numbers in an order. Queue keeps that order, Next draws it, and Hold keeps one number aside. Spawn Piece gives any chosen kind its starting place.
Drawing a piece and deciding which piece comes next are separate jobs. That separation lets the same drawing serve a moving shape, a waiting shape and a shape put away for later.
4.10 If It Didn’t Work
Nothing arrives at the start? Check Piece Queue is placed and Deal Pieces is attached. Spawn Piece no longer starts itself in When Created; it waits for the queue. Restore that Object, then run again.
The same kind stays at the front? Compare the pieceDealt sender in Spawn Piece with Deal Pieces’ listener. That message advances the waiting line. Match its spelling in the sender and listener, then lock again and watch the next kind take its place.
All the preview crystals cover each other? Compare their slot and crystal Per Instance Variables with PLACE THESE. The first placement values can stay at their defaults, but the copies need the card’s changes. Correct one slot, run it, then finish the others.
Hold works only once for the whole game? Look at Lock Piece. Then check Spawn Piece enables Hold Piece. Those changes give the next piece its own chance to trade. Run it again.
Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).
4.11 Challenge
What would your own game keep beside the well? Try moving a preview slot or changing its cellSize, keeping a working copy to compare. You could arrange the next pieces in an arc, or give Hold more room. The pictures can change without changing Queue’s order.
The next piece waits beside the well, and one more can wait in Hold until you choose it. Next, it learns to count more than lines.
