- Learn
- Build a Game
- Crystal Cascade
- 1 Falling Crystals
1 Falling Crystals
Pieces fall into your well, one at a time. You slide them left and right, and each one locks where it comes to rest, crystal on crystal.
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 2 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.
1.1 The Idea
The well stands empty under a bright sky. Green hills roll behind it, and nothing moves inside the glass yet. By the end, your first piece will fall.
You can slide it left or right. When it reaches the floor, it waits before locking in place. Another piece follows. Nothing breaks yet; the well only fills.
Where does a crystal go when it locks? You might picture a new Object left behind for every crystal. The game keeps a number instead. Its Well table holds one entry for each cell, a place at a column and a row. An empty cell holds 0. A filled one holds the piece’s kind, from 1 to 7.
Pictures and numbers have separate jobs. Well Cell Objects stand over the visible cells like small windows onto that table. Each one shows the crystal its entry holds, without knowing anything about moving pieces or where they came from.
The Falling Piece keeps the moving shape’s numbers. Four Falling Crystals read them and draw the shape. That invisible piece can move while the visible crystals follow, without leaving new Objects behind each time it locks.
Most of this chapter is quiet work. You’ll see the empty well after its first part. The moving piece stays invisible until the last part gives it pictures. The game’s own catch-up download is there if you need a comparison along the way.
1.2 The Plan
Each line follows a message through the game. Read it now for the picture, then come back as the names become familiar.
- The Scene starts -> Build The Well empties the table and creates its Well Cells.
- The Falling Piece is created -> Spawn Piece sends
spawnPiece-> a new kind appears and Piece Gravity starts its timer. - Gravity rings -> a clear space below means fall and send
pieceMoved. A filled space means rest and start the lock delay. - The delay ends ->
lockPiecewrites the crystals into Well ->wellChangedredraws the stack -> the next piece appears. - Move Left or Move Right -> Read The Move Keys sends
moveOnce-> Move Piece tests the next column before moving.
That question about room belongs to a Function, Piece Fits. Gravity and movement both ask it, so neither has to keep another copy of the well’s rules.
1.3 Set Up
Begin with a new Blank 2D project. Install the Starter Kit through File > Install Myth Pack. The button above supplies every picture, sound and background, together with the game’s font.
Turn on Advanced Mode in Settings. Then follow the setup card’s Game Settings from top to bottom. Make the Gameplay Scene after those sizes are set, and choose Start Scene after the Scene exists. Give the Scene its Aero Sky background.
Those Objects don’t exist yet. We’ll return to it at the end of each part. The card gives the camera’s whole window in meters and pixels, so its view can match the game.
Left ArrowRight ArrowCrystal Cascade1920108019201080#06213D#1A78C8Gameplay · 2D1.4 The Well
First, give the empty glass a place to stand.
Every part needs the same measurements. Well Layout Entry gives names to those values, and Well Layout keeps them together in one table row. Make the structure before the table. The table’s Entry Type is that structure, so its fields become the columns.
One row of the "Well Layout" Data Table. Its fields are the columns of the table. Where the well is on screen and how big it is. One row. columns: the cells across (10); rows: the cells up, the two hidden rows above the top included (22); visibleRows: the rows the player sees (20); cellSize: the size of one cell in meters (1.5); left and bottom: the outer edges of the bottom-left cell, in meters.
1022201.522.51.875The well has 22 rows, though only 20 show. The hidden rows give a new piece somewhere to arrive, above the crystals the player can see. Row 0 is at the bottom. Y counts upward, while a cell’s column starts at the left.
Well Entry describes a different kind of row: one cell of the stack. Its kind is the number of the crystal there. Give Well that Entry Type. Leave the table empty, because the game will make its rows when the Scene starts.
One row of the "Well" Data Table. Its fields are the columns of the table. The stack, one row per cell: row index = row * columns + column (Well Layout), row 0 at the bottom; rows 20 and 21 are hidden above the top. kind: 0 empty, 1-7 a crystal of that kind (Piece Kinds). Its rows are made, all empty, by Empty The Well when a game begins.
1.4.1 Empty The Well
A new game needs a clean well. Empty The Well makes one empty entry for every cell, using the measurements from Well Layout. There’s no long list to enter by hand.
This is a Function, a piece of Logic called by another piece when it needs a job done. Empty The Well has no answer to return. It does its work and finishes.
Makes the Well table one empty row per cell of the well (Well Layout: columns x rows), or empties the rows it already has.
data('Well Layout', 0, 'columns') * data('Well Layout', 0, 'rows')times- Action Name
addedRow- Table
Well- Row
blockLocal("index")- Values
{"kind":"0"}
1.4.2 Well Cell Index
A cell has a column and a row, but the table is one long list. Well Cell Index joins those two ways of counting. It counts every whole row below the cell, then the cells to its left.
Its Return Type is Number. One Return Action gives the caller that cell’s place in the table. You’ll use this answer both to draw a crystal and to write one down.
Answers which row of the Well table holds the well cell at this column and row (row * columns + column, Well Layout).
- Value
row * data('Well Layout', 0, 'columns') + column
1.4.3 Cell At
A Well Cell knows where it stands on the screen. Cell At works backward from that point to its column and row in the well. It counts whole cells from the lower-left edge.
The answer needs both a column and a row, so make Cell At Result first. Choose it as the Function’s Return Type. The Return Action then has a field for each part of that answer.
What the Function "Cell At" returns (its former Outputs). column, row: the cell of the well a point is in (0, 0 is the bottom-left cell).
Answers which well cell is under a point in the world (meters): its column from the left and row from the bottom (Well Layout).
- Name
cellSize- Value
0
- Name
column- Value
0
- Name
row- Value
0
- Variable
cellSize- Value
data('Well Layout', 0, 'cellSize')
- Variable
column- Value
floor((x - data('Well Layout', 0, 'left')) / cellSize)
- Variable
row- Value
floor((y - data('Well Layout', 0, 'bottom')) / cellSize)
- column
column- row
row
1.4.4 Cell Middle
Now go the other way. Give Cell Middle a cell’s column and row, and it finds the middle of that cell on screen. Build The Well will use those points to place its windows.
Cell Middle Result holds the answer’s X and Y. Make it before the Function. The half cell in the sum takes a point on the edge into the middle.
What the Function "Cell Middle" returns (its former Outputs). x, y: the middle of a cell of the well, in meters.
Answers where the middle of a well cell is in the world (meters), from its column and row (Well Layout).
- Name
cellSize- Value
0
- Name
x- Value
0
- Name
y- Value
0
- Variable
cellSize- Value
data('Well Layout', 0, 'cellSize')
- Variable
x- Value
data('Well Layout', 0, 'left') + (column + 0.5) * cellSize
- Variable
y- Value
data('Well Layout', 0, 'bottom') + (row + 0.5) * cellSize
- x
x- y
y
1.4.5 Show Well Cell
Each small window needs to remember which entry it watches. Show Well Cell Data holds that place, cellIndex. Make the structure, then add it through the + on this Logic’s Data Structures block. The Object receives it when the Logic is attached.
Object Data of Logic "Show Well Cell" (its state that lasts between Events). cellIndex: the row of the Well table this cell shows. shownKind: the crystal kind it shows now (0 empty, -1 before the first). isShowingShatter: its crystal is bursting in a full line.
Show Well Cell finds its cell when created, using its position to ask Cell At and then Well Cell Index. After that, wellChanged means look again at the same table entry. The entry’s kind chooses the Sprite’s frame. An empty entry shows the faint outline of an empty cell, waiting for a colored crystal to take its place.
Each Function call has an Action Name. The call named myCell gives column and row back; thisCellIndex gives one number through its value. The card shows how the next Action reads each answer.
One cell of the well: it finds its cell from where it is, then shows the crystal stored there whenever the well changes.
Any Objectself.showWellCellData- Action Name
myCell- x
self.transform.position.x- y
self.transform.position.y
- Action Name
thisCellIndex- column
myCell.column- row
myCell.row
- Component Value
self.showWellCellData.cellIndex- Value
thisCellIndex.value
- Frame
data('Well', self.showWellCellData.cellIndex, 'kind')
- Frame
data('Well', self.showWellCellData.cellIndex, 'kind')
Attach it to Well Cell now. Its Data Structure is marked From Logic, so attaching brings it along. There’s no second copy to add. Its picture is Crystal Blocks, which contains the empty cell and each colored crystal.
OnCrystal BlocksX 1.5, Y 1.5Show Well CellShow Well Cell Data1.4.6 Build The Well
The well gets its windows when the Scene begins. Build The Well moves along each visible row, creating a Well Cell in every column. Then it goes up to the next row.
The outer Repeat remembers its row as cellRow. The inner Repeat counts columns, so its own local.index means a different count. Keeping cellRow lets the two loops work together. Cell Middle supplies each new window’s position.
Declare the Shared Variable wellCell on the Logic’s block before building its Actions. Choose Object, Constant and the Well Cell kind. The Glass Well will give that Variable its actual Object resource.
When a game begins, empties the Well table (Empty The Well) and makes a Well Cell on every visible cell of the well, row by row from the bottom.
Any ObjectWell Celldata('Well Layout', 0, 'visibleRows')times- Name
cellRow- Value
local.index
data('Well Layout', 0, 'columns')times- Action Name
newCellMiddle- column
local.index- row
cellRow
- Object to Create
@self.wellCell- X
newCellMiddle.x- Y
newCellMiddle.y
Make Glass Well, attach Build The Well, and set its wellCell Shared Variable to Well Cell. That’s the kind of Object Create Instance will make. There is no Well Cell to place by hand; Build The Well makes every one when the game begins.
On-10Glass WellX 16.5, Y 31.5Well CellPlace Glass Well using the first PLACE THESE line. Run the game. The empty windows fill the glass, row by row in position, although they appear together when the Scene starts. Nothing falls yet.
1.5 The Shape Inside a Piece
The well is ready for something to fall into it.
Make Piece Kinds Entry first. The Piece Kinds table gives those values to every shape. Its first row is I, followed by the other kinds in the card’s order.
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).
1#3cd6ff1.51.52#ffce3a1.52.53#b268ff124#60e25c125#ff486e126#3a74ff127#ff923212The shape itself lives in Piece Shapes, whose rows name each crystal’s place inside the piece’s box. Make Piece Shapes Entry, then the table. Download the card’s CSV and use Import CSV in the table editor. Its first rows let you compare the result.
One row of the "Piece Shapes" Data Table. Its fields are the columns of the table. The four crystals of each kind of piece as it appears (rotation up). Row index = (kind - 1) * 4 + crystal - 1; column and row are the crystal's cell in the piece's 4 x 4 box, row 0 at the bottom. The other rotations are this shape rotated clockwise about the kind's rotation point (Piece Kinds; Crystal Cell).
0212221.5.1 Crystal Cell
Imagine carrying a little square box around the well, with the piece’s crystals always sitting at particular places inside it. Crystal Cell finds one crystal there, then rotates that place around the kind’s turning point when needed.
It returns Crystal Cell Result, a column and row inside the box. Make that structure first. The well’s column and row aren’t added here; callers add them when they need a cell in the well.
What the Function "Crystal Cell" returns (its former Outputs). column, row: where one crystal of a piece sits, counted from the corner of the piece, for its kind and rotation.
Answers where crystal 1-4 of a piece of this kind and rotation sits in its 4 x 4 box: its column and row from the box's bottom-left corner (Piece Shapes).
- Name
shapeRow- Value
0
- Name
column- Value
0
- Name
row- Value
0
- Name
rotationColumn- Value
0
- Name
rotationRow- Value
0
- Name
rotatedColumn- Value
0
- Variable
shapeRow- Value
(kind - 1) * 4 + crystal - 1
- Variable
column- Value
data('Piece Shapes', shapeRow, 'column')
- Variable
row- Value
data('Piece Shapes', shapeRow, 'row')
- Variable
rotationColumn- Value
data('Piece Kinds', kind - 1, 'rotationColumn')
- Variable
rotationRow- Value
data('Piece Kinds', kind - 1, 'rotationRow')
rotationtimes- Variable
rotatedColumn- Value
rotationColumn + (row - rotationRow)
- Variable
row- Value
rotationRow - (column - rotationColumn)
- Variable
column- Value
rotatedColumn
- column
column- row
row
1.5.2 Well Cell Filled
A piece must stop at the floor, even though there is no crystal underneath it. Well Cell Filled treats the floor and side walls as taken. Inside, the table answers.
Its Boolean answer is true for taken and false for free, including cells that fall outside the glass. Look at its first If. The || joins alternative ways to be outside, while && inside the other path requires both tests. The card-reading page explains how those expressions go into one Condition field.
Answers whether a cell is taken: outside the well's sides or below its floor counts as taken, above its top as free, otherwise a crystal there.
- Type
Boolean- Name
isFilled- Value
false
- Variable
isFilled- Value
false
column < 0 || column >= data('Well Layout', 0, 'columns') || row < 0- Variable
isFilled- Value
true
- Action Name
testedCellIndex- column
column- row
row
row < data('Well Layout', 0, 'rows') && data('Well', testedCellIndex.value, 'kind') != 0- Variable
isFilled- Value
true
- Value
isFilled
1.5.3 Piece Fits
One crystal can fit while the rest hit a wall. Piece Fits checks the whole shape before saying yes. It asks Crystal Cell for each crystal, adds the box’s position, and asks whether that well cell is filled.
One filled cell makes fits false. Checking the remaining crystals can never turn it true again during that call. In its Repeat, local.index starts at 0, while the crystals start at 1. That’s why the card adds one.
Answers whether a piece of this kind and rotation fits with its 4 x 4 box's bottom-left corner at this cell: each of its four crystals on a free cell of the well (Well Cell Filled).
- Type
Boolean- Name
fits- Value
true
- Name
crystalColumn- Value
0
- Name
crystalRow- Value
0
- Variable
fits- Value
true
4times- Action Name
testedCrystal- kind
kind- rotation
rotation- crystal
local.index + 1
- Variable
crystalColumn- Value
column + testedCrystal.column
- Variable
crystalRow- Value
row + testedCrystal.row
- Action Name
testedCell- column
crystalColumn- row
crystalRow
testedCell.value == true- Variable
fits- Value
false
- Value
fits
1.5.4 Piece Fits Here
Gravity doesn’t need to describe a whole piece every time it looks below. Piece Fits Here takes the Falling Piece and a shift, then asks Piece Fits about that nearby place. Nothing moves during the question.
The piece Parameter needs a Piece Data Structure. Make Piece now, with the fields on its card, before setting that requirement. We’ll put it on Falling Piece in the next part. That structure is the moving piece’s memory, from its kind to its lock delay.
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).
Answers whether the falling piece, as it is turned now, would fit columnShift columns to the right and rowShift rows up from where it is (Piece Fits); 0 and 0 asks about where it is.
pieceData StructurePiece- Type
Boolean- Name
fits- Value
true
- Action Name
shiftedPiece- kind
piece.piece.kind- rotation
piece.piece.rotation- column
piece.piece.column + columnShift- row
piece.piece.row + rowShift
- Variable
fits- Value
shiftedPiece.value
- Value
fits
Run again if you’d like. The well still waits. Those questions are ready, but nothing has asked them yet.
1.6 The Falling Piece
Now give the invisible piece its work.
Make Spawn Piece, Piece Gravity, Lock Piece, Read The Move Keys and Move Piece by name. Their Actions come below. Add the Piece Data Structure to Falling Piece, then attach those Logics in its card’s order. Spawn Piece comes first so it sets the new piece before gravity reads it.
Read The Move Keys Data belongs to its Logic. Make it now and supply it through that Logic’s Data Structures block. Its two switches remember which keys are held. The Object receives this structure when that Logic is attached.
Object Data of Logic "Read The Move Keys" (its state that lasts between Events). isLeftHeld, isRightHeld: Move Left / Move Right is held down.
OnPieceRead The Move KeysRead The Move Keys DataWhere a Logic card names Piece under DATA STRUCTURES, choose the existing structure there. Keep the name piece, so self.piece reaches the same fields. Attach the Logic before completing its self expressions, and check the Object has one Piece component.
1.6.1 Spawn Piece
A new piece arrives upright, near the middle of the well. Spawn Piece chooses its kind and places its box in the hidden rows. If there’s room below, it brings the piece down before telling the pictures to show it.
Its startColumn Shared Variable names where the box begins. spawnPiece is sent on this Object; pieceMoved tells every picture that listens. Nothing draws the piece yet, but that second message will have listeners soon.
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
- Custom Event
pieceMoved
1.6.2 Piece Gravity
The piece falls one row each second. Near the floor, gravity asks whether another step would fit. If it won’t, the piece lands and rests before it locks.
That pause has a name, lock delay. For half a second you can still slide the piece. If it finds room below by the end of the delay, it falls again. Otherwise it locks where it rests.
Make the gravity timer Event with its Falling and Resting Modes before building the Switch Mode Actions. Falling starts on. Resting has no Actions, so the timer can ring without pulling the piece through the floor.
pieceLanded makes that change and starts the lockDelay timer. Its lockDelayStarted message will dim the crystals later. Make the Modes first, then follow each Event’s card with that landing in mind.
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
- Action Name
roomBelowOnGravity- piece
self- columnShift
0- rowShift
-1
roomBelowOnGravity.value == true- Component Value
self.piece.row- Value
self.piece.row - 1
- 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
- Action Name
roomBelowAfterDelay- piece
self- columnShift
0- rowShift
-1
roomBelowAfterDelay.value == true- Event
On the Gravity Timer- Mode
Falling
- Custom Event
lockPiece
1.6.3 Lock Piece
Once the delay ends, the moving shape becomes part of the stack. Lock Piece writes the kind into each crystal’s table entry. The old moving piece is still the same Object; it can now carry a new kind at the top.
Only after all its crystals are written does wellChanged go out. Every Well Cell reads the finished piece before the last message asks for a new one at the top. There is no half-written shape for the pictures to catch.
Writes the falling piece's four crystals into the Well, tells everyone the well changed, and asks for the next piece.
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
- Custom Event
wellChanged
- Custom Event
spawnPiece
1.6.4 Read The Move Keys
Tap Left, and you want a single step. Hold it, and you want more. Read The Move Keys gives that tap a little room, then asks for repeated moves while the key stays down.
Its autoRepeatDelaySeconds is 0.15 seconds, a pause that keeps a tap from becoming several moves. The repeats then come 0.03 seconds apart. Those Shared Variables belong on the Logic’s block and arrive on Falling Piece with it.
Both keys can be down together. The newest press chooses the direction. Let it go while the other is held, and the older key takes over. Those two switches remember the keys.
Hears Move Left and Move Right: keeps the piece's moveDirection (the key pressed last wins) and asks for one move (moveOnce) at a press and again and again while the key is held (auto-repeat). It stays on between pieces, so a key let go then is heard too.
Any Objectself.pieceself.readTheMoveKeysData- Component Value
self.readTheMoveKeysData.isLeftHeld- Value
true
- Component Value
self.piece.moveDirection- Value
-1
- Custom Event
moveOnce
- Timer Name
"autoRepeat"- Repeat Every (s)
@self.autoRepeatSeconds- First Delay (s)
@self.autoRepeatDelaySeconds
- Component Value
self.readTheMoveKeysData.isRightHeld- Value
true
- Component Value
self.piece.moveDirection- Value
1
- Custom Event
moveOnce
- Timer Name
"autoRepeat"- Repeat Every (s)
@self.autoRepeatSeconds- First Delay (s)
@self.autoRepeatDelaySeconds
- Component Value
self.readTheMoveKeysData.isLeftHeld- Value
false
self.piece.moveDirection == (-1) && self.readTheMoveKeysData.isRightHeld == true- Component Value
self.piece.moveDirection- Value
1
- Timer Name
"autoRepeat"- Repeat Every (s)
@self.autoRepeatSeconds- First Delay (s)
@self.autoRepeatDelaySeconds
self.piece.moveDirection == (-1)- Component Value
self.piece.moveDirection- Value
0
- Timer Name
"autoRepeat"
- Component Value
self.readTheMoveKeysData.isRightHeld- Value
false
self.piece.moveDirection == 1 && self.readTheMoveKeysData.isLeftHeld == true- Component Value
self.piece.moveDirection- Value
-1
- Timer Name
"autoRepeat"- Repeat Every (s)
@self.autoRepeatSeconds- First Delay (s)
@self.autoRepeatDelaySeconds
self.piece.moveDirection == 1- Component Value
self.piece.moveDirection- Value
0
- Timer Name
"autoRepeat"
- Custom Event
moveOnce
1.6.5 Move Piece
A key can ask for a move, but it can’t push the piece through a wall. Move Piece asks Piece Fits Here about the neighboring column first. Only a yes changes the column and sends 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
- Custom Event
pieceMoved
Place Falling Piece now, using its setup line under PLACE THESE, and leave the remaining placements until the next part. There’s still no picture to see.
1.7 The Crystals You See
Now the moving numbers get their pictures.
1.7.1 Show Piece Crystal
Each Falling Crystal knows which crystal it is, and which Falling Piece to watch. The crystal Per Instance Variable gives it that number. The piece Shared Variable names the Falling Piece Object, whose Piece structure holds the moving shape.
On pieceMoved, Show Piece Crystal finds its crystal inside the shape. It adds the piece’s place in the well and asks Cell Middle where to stand. Then it shows the kind’s colored frame, hiding crystals above the visible rows.
The landing message does a smaller job. Over the lock delay, Alpha fades toward dimmedAlpha, making the resting piece look different from a moving one. A new move stops that fade and brings it back to full brightness.
One crystal of the falling piece: whenever the piece moves, it goes to its cell and shows the piece's color; it hides in the two hidden rows above the well.
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
- 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
Make Falling Crystal and attach the Logic. In its Shared Variables, set piece to the Falling Piece resource. All its placements watch that same kind of Object, and this Scene has one Falling Piece.
On8Crystal BlocksX 1.5, Y 1.5Falling PiecePlace the Falling Crystals using the remaining setup lines. They start together below the window. Change each placement’s crystal Per Instance Variable as the card shows, so each watches a different crystal of the shape.
Run the game. The first piece appears near the rim, falls into view, and answers the arrow keys. The empty well is waiting underneath it.
1.8 Play It!
- Watch a piece fall without touching a key, moving down a row at a time until it meets the floor.
- Tap Left or Right. It slides, and a short tick marks a move that fits.
- Hold a direction. After its small pause, the piece keeps stepping toward the wall.
- Hold both directions together. Release the newer key and watch the piece move the other way, answering the older key still held down.
- Let a piece reach the floor. Its crystals dim during the delay, then become part of the stack with Lock Click.
- Wait for the next piece. The locked crystals stay where they landed, and the new piece comes from above.
1.9 How It Works
The piece moves. Its crystals show the new place. A lock writes numbers, and the well’s windows show those instead. The pictures change because the numbers did.
That separation lets every part use the same questions, even though they put the answers to different work. Piece Fits knows about room, without drawing anything. Show Well Cell knows about drawing, without deciding where a piece belongs.
1.10 If It Didn’t Work
The empty well has no windows? Look at Glass Well’s Build The Well attachment, then its wellCell Shared Variable. It should name Well Cell. Run again, and the Scene creates the windows for you.
Only one crystal seems to move? The placements may all have the same crystal value. Change their Per Instance Variables to match the setup card, then run again. Each picture now has its own place in the shape.
A self name stays red? Read it from the left, and compare its Data Structure name and field. Attach the Logic to its Object and give it the structures its card lists. Retype the expression once those names exist.
Does the piece pass through a wall? Compare Piece Fits Here with Well Cell Filled, watching which answer each Function returns to its caller. A true answer means filled in one Function and fits in the other. Match the If tests with their cards, then try the wall again.
The piece won’t fall? In Piece Gravity, check the gravity Event starts in Falling. Make both Modes first. Choose them in Switch Mode, then run again and watch the next piece begin to fall.
Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).
1.11 Challenge
What kind of fall would suit your own game? Try changing fallSeconds in Piece, then run it and watch the piece cross the well. You could give a slow piece more time for planning, or make a quick one demand earlier choices. Keep a copy of the working project while you explore.
Your well can fill now. Next, its pieces learn to rotate and drop.
