MatchBoard
namespace Griff.Modules.Match3;
public sealed class MatchBoard : Component, IDrawable, IDrawsTilemap, INetStateA match-3 board, with no code: the gems are the tiles of its entity’s Tilemap, one kind per tile number in Gems. Drag a gem onto a neighbor (or click one, then the other) to swap them; three or more of a kind in a row or column clear, the gems above fall into the gaps, new ones drop in from the top, and whatever lines up as they land clears too, a chain. With keys or a gamepad, the arrows move a cursor, the PickAction (Space, the gamepad’s A) picks a gem up and an arrow swaps it that way.
The board is Columns by Rows cells from the tilemap’s top left. Empty cells are filled with gems as the game starts, and tiles that aren’t gems are fixed blocks: they don’t move or match, gems stop on them, and the cells under one are filled from just under it. Without a tileset image each gem kind draws as a square of its own color. The tiles are saved with the scene, so a rewind goes back to how the board was (the slides and falls under way start again from there). In a network game the host runs the board and any player can swap.
Each gem cleared scores Points, more along a chain. Give it Moves, a TargetScore or gems to Collect and it’s a level: met goals send the WinMessage, moves run out first the OutOfMovesMessage, and either way no more swaps are taken.
This is a component: its saved properties are on its component page.
Properties
Section titled “Properties”Background
Section titled “Background”public Color Background { get; set; }A color drawn behind the board’s cells. Transparent draws nothing.
public int Chain { get; }How many clears the last swap has set off so far: 1 for a plain match, more for a chain.
ChainMessage
Section titled “ChainMessage”public string? ChainMessage { get; set; }A message sent as a chain goes on (the second clear and on after one swap), with how long it is so far as its data.
ClearTime
Section titled “ClearTime”public float ClearTime { get; set; }Seconds matched gems take to shrink away.
Cleared
Section titled “Cleared”public string? Cleared { get; set; }How many of each kind have been cleared so far, as kind:count pairs like Collect. Saved with the scene, so a rewind takes them back.
Collect
Section titled “Collect”public string? Collect { get; set; }Gems to clear to win the level, as kind:count pairs, e.g. “0:20, 3:15” for 20 of tile 0 and 15 of tile 3. With a TargetScore too, both have to be done.
Columns
Section titled “Columns”public int Columns { get; set; }Cells across.
Controllable
Section titled “Controllable”public bool Controllable { get; set; }Untick it to have the board ignore the mouse, keys and gamepad while it plays on: another part of the scene has the player’s input (a platformer around an arcade cabinet). Its Focus action ticks it and unticks it on everything else the player plays, and the player’s own Focus gives the input back.
Cursor
Section titled “Cursor”public ValueTuple<int, int>? Cursor { get; }The cell this machine’s player’s keyboard cursor is on, or null while they use the mouse.
public bool Deal { get; set; }Deal a fresh board each time the game starts, with no matches already made, keeping the blocks where they are. Untick it to play the gems as painted: a level laid out by hand (empty cells are still filled). It unticks itself once the board is dealt, so a rewind keeps the board it goes back to.
FallTime
Section titled “FallTime”public float FallTime { get; set; }Seconds a gem takes to fall one cell. Longer falls speed up as they go, as things falling do.
public string? Gems { get; set; }The tile numbers that are gems, each its own kind: numbers and ranges, e.g. “0-5” for six kinds. Any other tile on the board is a fixed block.
GoalsMet
Section titled “GoalsMet”public bool GoalsMet { get; }True if the board has goals (a TargetScore or gems to Collect) and they’re all met.
Highlight
Section titled “Highlight”public Color Highlight { get; set; }The color of the picked-up gem’s cell and the keyboard cursor.
IsBusy
Section titled “IsBusy”public bool IsBusy { get; }True while gems are swapping, clearing or falling: swaps wait until it’s still.
IsOver
Section titled “IsOver”public bool IsOver { get; }True once the level is over, won or out of moves: no more swaps are taken.
MatchLength
Section titled “MatchLength”public int MatchLength { get; set; }How many of a kind in a row (or column) clear.
MatchMessage
Section titled “MatchMessage”public string? MatchMessage { get; set; }A message sent with Game.Send as gems clear, with how many as its data: a Counter listening for it adds them up.
MatchSound
Section titled “MatchSound”public string? MatchSound { get; set; }A sound played as gems clear (once however many lines go at once).
public int Moves { get; set; }How many swaps the player gets: once they’re used up and the board comes to rest, the level is over (won if its goals are met, lost if not). 0 is no limit.
MovesLeft
Section titled “MovesLeft”public int MovesLeft { get; }How many swaps are left of Moves: 0 once they’ve run out, -1 with no limit.
MovesTaken
Section titled “MovesTaken”public int MovesTaken { get; }The swaps made, as a puzzle’s moves are counted: the same as Swaps, out of Moves.
NoMatchSound
Section titled “NoMatchSound”public string? NoMatchSound { get; set; }A sound played when a swap lines nothing up and slides back.
OnlyMatchingSwaps
Section titled “OnlyMatchingSwaps”public bool OnlyMatchingSwaps { get; set; }A swap that lines nothing up slides back. Untick it to let any two neighbors swap.
OutOfMovesMessage
Section titled “OutOfMovesMessage”public string? OutOfMovesMessage { get; set; }A message sent once the moves run out with the goals not met and the board comes to rest, with this entity as its data. A board with no goals sends it too: the end of a game played for score.
OutOfMovesSound
Section titled “OutOfMovesSound”public string? OutOfMovesSound { get; set; }A sound played when the moves run out with the goals not met.
Paused
Section titled “Paused”public bool Paused { get; set; }True while it stands still: nothing moves, falls or clears, and it takes no swaps. Its Pause and Resume actions set it.
PickAction
Section titled “PickAction”public string? PickAction { get; set; }The input action that picks up the gem under the cursor: “Jump” is Space or the gamepad’s A. Empty only uses the mouse.
Picked
Section titled “Picked”public ValueTuple<int, int>? Picked { get; }The cell of the gem this machine’s player has picked up to swap, or null.
Points
Section titled “Points”public int Points { get; set; }Points for each gem cleared, times how far into the chain it clears: gems in the second clear after a swap score double, the third triple. 0 keeps no score.
Refill
Section titled “Refill”public bool Refill { get; set; }New gems drop in from the top to fill the gaps. Untick it for a board that empties out as it’s cleared.
public int Rows { get; set; }Cells down.
public int Score { get; set; }The points scored so far. Saved with the scene, so a rewind takes them back.
ScoreMessage
Section titled “ScoreMessage”public string? ScoreMessage { get; set; }A message sent as gems clear, with the points they scored as its data: a Counter listening for it (its Message is “Score” to begin with) shows the score.
public int Seed { get; set; }Picks the gems dealt and dropped in: the same number always gives the same ones. 0 is different each game.
SettledMessage
Section titled “SettledMessage”public string? SettledMessage { get; set; }A message sent once the board comes to rest after a swap, with how many clears the swap set off as its data.
ShuffleMessage
Section titled “ShuffleMessage”public string? ShuffleMessage { get; set; }A message sent as a stuck board is shuffled, with this entity as its data.
ShuffleSound
Section titled “ShuffleSound”public string? ShuffleSound { get; set; }A sound played as a stuck board is shuffled.
ShuffleWhenStuck
Section titled “ShuffleWhenStuck”public bool ShuffleWhenStuck { get; set; }When no swap would line anything up, the gems are shuffled (into a board with no matches but a move to make).
SwapSound
Section titled “SwapSound”public string? SwapSound { get; set; }A sound played as two gems swap, e.g. “Sounds/swap.wav”.
SwapTime
Section titled “SwapTime”public float SwapTime { get; set; }Seconds a swap takes.
public int Swaps { get; set; }How many swaps have been made (not counting those that slid back). Saved with the scene, so a rewind takes them back.
TargetScore
Section titled “TargetScore”public int TargetScore { get; set; }A score that wins the level once it’s reached. 0 is no score to reach.
WinMessage
Section titled “WinMessage”public string? WinMessage { get; set; }A message sent once the goals are met and the board comes to rest, with this entity as its data: a panel with this as its Open Message shows the level is won.
WinSound
Section titled “WinSound”public string? WinSound { get; set; }A sound played when the level is won: its goals met, once the board comes to rest.
Methods
Section titled “Methods”ClearedOf
Section titled “ClearedOf”public int ClearedOf(int kind)How many gems of kind kind (a tile number) have been cleared.
ClearedOfKind
Section titled “ClearedOfKind”public int ClearedOfKind(string kind)How many gems of the kind named kind (a tile number, e.g. “1”) have been cleared. Read as “Cleared:1”, out of how many Collect asks for.
DealInto
Section titled “DealInto”public void DealInto(Tilemap map, Random random)Deals a fresh board into map straight away, as the game does when it starts: every gem replaced, none lined up, and a move to make. Blocks stay where they are.
FindMove
Section titled “FindMove”public ValueTuple<ValueTuple<int, int>, ValueTuple<int, int>>? FindMove()A swap that would line something up, or null if there’s none: a hint to show the player.
public int GemAt(int x, int y)The tile in cell (x, y): a gem kind, a block, or -1 for empty or off the board.
public bool IsGem(int tile)True if tile is a gem rather than a block (or empty, -1).
public IReadOnlyList<int> Kinds()The gem kinds, lowest first.
public void Pause()Stops the board where it is: nothing moves, falls or clears, and no swaps are taken, until it’s resumed.
Restart
Section titled “Restart”public void Restart()Starts the board over at the start of the next frame: a fresh deal (blocks stay), no swaps, score or gems cleared.
Resume
Section titled “Resume”public void Resume()Lets a paused board go on.
Shuffle
Section titled “Shuffle”public void Shuffle()Shuffles the gems at the start of the next frame, as a stuck board is.
public bool Swap(int x1, int y1, int x2, int y2)Swaps the gems in two neighboring cells, as a player does (sliding back if it lines nothing up and OnlyMatchingSwaps is on), once the board is still. False if they aren’t two neighboring cells.
ToCollect
Section titled “ToCollect”public int ToCollect(int kind)How many gems of kind kind``Collect asks for: 0 if none.
Events
Section titled “Events”Finished
Section titled “Finished”public event Action<bool>? FinishedRaised once the board comes to rest with the level over: true if its goals were met, false if the moves ran out.
Matched
Section titled “Matched”public event Action<MatchInfo>? MatchedRaised as gems clear, once for each match (gems of one kind lined up together).
Settled
Section titled “Settled”public event Action<int>? SettledRaised once the board comes to rest after a swap, with the length of the chain it set off.
Swapped
Section titled “Swapped”public event Action<ValueTuple<int, int>, ValueTuple<int, int>>? SwappedRaised as two gems swap for good, with the two cells.