Bejeweled
Introduction¶
Match3-like gameplay template.
- Author: Rex
- Template
Live demos¶
Usage¶
Install plugin¶
Load minify file¶
- Load rexBoard plugin (minify file) in preload stage
scene.load.scenePlugin('rexboardplugin', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexboardplugin.min.js', 'rexBoard', 'rexBoard'); scene.load.script('rexbejeweled', 'https://raw.githubusercontent.com/rexrainbow/phaser3-rex-notes/master/dist/rexbejeweled.min.js'); - Add bejeweled object
var bejeweled = new rexbejeweled(scene, config);
Import template¶
- Install rex plugins from npm
npm i phaser3-rex-plugins - Install rexBoard plugin in configuration of game
import BoardPlugin from 'phaser3-rex-plugins/plugins/board-plugin.js'; import Bejeweled from 'phaser3-rex-plugins/templates/bejeweled/Bejeweled.js'; var config = { // ... plugins: { scene: [{ key: 'rexBoard', plugin: BoardPlugin, mapping: 'rexBoard' }, // ... ] } // ... }; var game = new Phaser.Game(config); - Add bejeweled object
var bejeweled = new Bejeweled(scene, config);
Create bejeweled object¶
var bejeweled = new Bejeweled(scene, {
// rexBoard: 'rexBoard',
board: {
x: 0,
y: 0,
cellWidth: 60,
cellHeight: 60,
width: 10,
height: 10,
},
match: {
// wildcard: undefined
// dirMask: undefined
},
chess: {
// click random symbol from array, or a callback to return symbol
symbols: [0, 1, 2, 3, 4, 5],
// symbols: function(board, tileX, tileY, excluded) { return symbol; }
// User-defined chess game object
create: function (board) {
// Create Game object (Shape, Image, or Sprite)
var scene = board.scene;
var gameObject = scene.add.sprite(0, 0, textureKey, frame);
// Initial 'symbol' value
gameObject.setData('symbol', undefined);
// Add data changed event of 'symbol` key
gameObject.data.events.on('changedata_symbol', function (gameObject, value, previousValue) {
// Change the appearance of game object via new symbol value
gameObject.setFrame(newFrame);
});
return gameObject;
},
// scope for callbacks
scope: undefined,
// moveTo behavior
moveTo: {
speed: 400
},
// tileZ: 1,
},
// initSymbols: [[...], [...]],
match: {
accept: undefined,
ignore: undefined,
},
// mask: true,
placeAction: undefined,
select1Action: undefined,
select2Action: undefined,
swapAction: undefined,
undoSwapAction: undefined,
eliminatingAction: undefined,
fallingAction: undefined,
// input: true
})
Configurations
rexBoard: Key of 'rexBoard' plugin. Default is'rexBoard'.- Board properties
board.x,board.y: Top-left position of visible area.board.cellWidth,board.cellHeight: The width/height of the cell, in pixels.board.width,board.height: Visible area, in tiles.- Total board size is
board.width x (board.height*2), the upperboard.width x board.heightarea is for prepared chess.
- Total board size is
- Chess properties
chess.symbols: An array of possible symbols, or a callback to return a symbol. See Generate symbolchess.create,chess.scope: Callback of creating chess object.chess.moveTo.speed: Constant moving speed of chess, in pixel per-second.
initSymbols: Fill visible area with this 2d symbol array.- Match:
match.accept,match.ignore: A list of symbol, chess symbol inmatch.ignore, or not inmatch.acceptwill be ignored.
- Custom actions
placeAction: Custom place chess Actionselect1Action: Custom select first chess Actionselect2Action: Custom select second chess ActionclickAction: Custom click chess ActionswapAction: Custon swap actionundoSwapAction: Custon undo-swap actioneliminatingAction: Custon eliminating actionfallingAction: Custon falling action
- Touch input
input: Settrueto register default touch input logic.
mask:true: Mask of visible area. Default behavior.- Create an internal layer
- Mask this internal layer
false: No mask.
Board height¶
Board is separated into two parts: upper and bottom
- Bottom : Visible N rows, to swap chess and run matching.
- Upper : Invisible N rows, chess in these rows will move down, to fill bottom rows.
For example, if amount of visible rows is 10, board.height is set to 10, and total board height is board.height * 2
Generate symbol¶
Symbols are declared in property chess.symbols in a symbol array like [0, 1, 2, 3, 4, 5], or a callback to return a symbol. The callback also use chess.scope as the scope.
function(board, tileX, tileY, excluded) {
return symbol
}
excluded:undefinedor a symbol array. Don't return these symbols.
Create chess object¶
Return a game object from a callback.
function(board) {
// Create Game object (Image, Sprite, or Shape)
var scene = board.scene;
var gameObject = scene.add.sprite(0, 0, textureKey, frame);
// Initial 'symbol' value
gameObject.setData('symbol', undefined);
// Add data changed event of 'symbol` key
gameObject.data.events.on('changedata-symbol', function (gameObject, value, previousValue) {
// Change the appearance of game object via new symbol value
gameObject.setFrame(newFrame);
// Override swappable and clickable, default values: swappable=trur, clickable=false
gameObject
.setData('swappable', true)
.setData('clickable', false)
});
return gameObject;
}
- Each chess has a
symbolvalue stored in'symbol'key in private data. Add data changed event of'symbol'key to change the appearance of game object via new symbol value. - Each chess piece has
swappableandclickableproperties stored in its private data. These properties are assigned based on the piece's symbol.- Default values :
swappable = trueclickable = false
- Default values :
States¶
graph TD
Start((Start)) --> RESET --> PLACE
PLACE --> SELECT1START
subgraph Select 1 states
SELECT1START --> |Wait input<br>pointerdown<br>1st chess| SELECT1
end
SELECT1 --> SELECT2START
subgraph Select 2 states
SELECT2START --> |Wait input<br>pointermove<br>2nd chess| SELECT2
SELECT2START --> |Wait input<br>pointerup<br>1st chess| CLICK
end
SELECT2START --> SELECT1START
SELECT2 --> SWAP
SWAP --> MatchStart[start]
subgraph MATCH3 sub-state
MatchStart --> MATCH3
MATCH3 --> ELIMINATING
MATCH3 --> MatchEnd[end]
ELIMINATING --> FALLING
FALLING --> FILL
FILL --> MATCH3
MatchStart --> |Has eleminating chess<br>From CLICK state| ELIMINATING
end
CLICK --> MatchStart[start]
MatchEnd --> |No-matched-line and<br>From SWAP| UndoSwap[undo-swap] --> SELECT1START
MatchEnd --> |Has-matched-line and<br>Has-potational-matched-line| SELECT1START
MatchEnd --> |Has-matched-line and<br>No-potational-matched-line| RESET
MatchEnd --> |No-matched-line and<br>From CLICK| SELECT1START
Place chess Action¶
Fire 'place' event after filling chess, then run place action.
bejeweled.on('place', function(board, bejeweled) {
}, scope);
Default place action:
function (chessArray, board, bejeweled) {
const duration = 500; //ms
for (var i = 0, cnt = chessArray.length; i < cnt; i++) {
var fade = PopUp(chessArray[i], duration);
bejeweled.waitEvent(fade, 'complete');
}
}
chessArray: Chess array at lower board.
Select first chess¶
Fire 'select1' event
bejeweled.on('select1', function(board, bejeweled) {
}, scope);
board: Board object.bejeweled: This bejeweled object.
Custom Select first chess Action¶
Default select action:
function (chess, board, bejeweled) {
// Do nothing
}
Select second chess¶
Fire 'select2' event
bejeweled.on('select2', function(board, bejeweled) {
}, scope);
board: Board object.bejeweled: This bejeweled object.- Selected first chess :
var chess = bejeweled.getSelectedChess1();
- Selected first chess :
Custom Select second chess Action¶
Default select action: The same as Select first chess Action
Swap selected chess¶
Fire 'swap' event
bejeweled.on('swap', function(selectedChess1, selectedChess2, board, bejeweled) {
}, scope);
board: Board object.bejeweled: This bejeweled object.
Custom Swap Action¶
Default swap action:
function (chess1, chess2, board, bejeweled) {
var tileXYZ1 = board.chessToTileXYZ(chess1);
var tileXYZ2 = board.chessToTileXYZ(chess2);
var tileX1 = tileXYZ1.x,
tileY1 = tileXYZ1.y,
tileX2 = tileXYZ2.x,
tileY2 = tileXYZ2.y,
tileZ = tileXYZ1.z;
// TileZ of chess1 and chess2 are the same, change tileZ of chess2 to a different value
board.moveChess(chess2, tileX2, tileY2, `#${tileZ}`, false);
// Move chess1 to tileXYZ2, chess2 to tileXYZ1
var moveTo1 = bejeweled.getChessMoveTo(chess1);
var moveTo2 = bejeweled.getChessMoveTo(chess2);
moveTo1.moveTo(tileX2, tileY2);
moveTo2.moveTo(tileX1, tileY1);
// Change tileZ of chess2 back
board.moveChess(chess2, tileX1, tileY1, tileZ, false);
if (moveTo1.isRunning) {
bejeweled.waitEvent(moveTo1, 'complete');
}
if (moveTo2.isRunning) {
bejeweled.waitEvent(moveTo2, 'complete');
}
};
bejeweled.getChessMoveTo(chess): Get moveTo behavior of a chess.bejeweled.waitEvent(moveTo, 'complete'): Wait 'complete' event of this moveTo behavior.
Click¶
Pointerup on selected chess1, fire 'click' event
bejeweled.on('swap', function(chess1, board, bejeweled) {
}, scope);
board: Board object.bejeweled: This bejeweled object.
Custom Click chess Action¶
Default click action:
function (chess, board, bejeweled) {
// Do nothing
// var chessArray = [];
// bejeweled.getChessArrayAtTileX(tileX, chessArray);
// bejeweled.getChessArrayAtTileY(tileX, chessArray);
// bejeweled.getChessArrayAtTileXYInRange(tileX, tileY, rangeX, rangeY, chessArray);
// bejeweled.getChessArrayWithSymbol(symbol, chessArray);
// ...
// bejeweled.setEliminatingChess(chessArray);
}
Eliminating chess by using clicked item
- Get Chess array by
bejeweled.getChessArrayAtTileXbejeweled.getChessArrayAtTileYbejeweled.getChessArrayAtTileXYInRangebejeweled.getChessArrayWithSymbol.
- Invoke
bejeweled.setEliminatingChess
Match start¶
Fire 'match-start' event
bejeweled.on('match-start', function(board, bejeweled) {
}, scope);
board: Board object.bejeweled: This bejeweled object.
Match lines¶
Fire 'match' event
bejeweled.on('match', function(lines, board, bejeweled) {
}, scope);
lines: An array of matched lines, each line is a built-in Set object.- Length of each line (
lines[i].size) could be 5, 4, or 3. lines[i].entries: An array of chess (Game Object) in a matched line.- Get cross chess of two lines via
lineA.intersect(lineB). - All chess game objects in matched lines will be eliminated in next stage.
- Add/remove chess game object in a line.
- Add new line/remove a line in
linesarray to change the eliminated targets.
- Length of each line (
board: Board object.- Get tile position
{x,y,z}of a chess game object viavar tileXYZ = board.chessToTileXYZ(gameObject); //var tileXYZ = gameObject.rexChess.tileXYZ; - Get chess game object of a tile position
{x,y,z}viavar gameObject = board.tileXYZToChess(tileX, tileY, tileZ); - Get array of neighbor chess of a chess game object via
var gameObjects = board.getNeighborChess(chess, null);
- Get tile position
bejeweled: This bejeweled object.
Eliminating chess¶
Fire 'eliminate' event
bejeweled.on('eliminate', function(chessArray, board, bejeweled) {
}, scope);
chessArray: An array of chess (Game Object) to be eliminated.board: Board objectbejeweled: This bejeweled object.
Custom Eliminating Action¶
Default eliminating action:
function (chessArray, board, bejeweled) {
const duration = 500; //ms
for (var i = 0, cnt = chessArray.length; i < cnt; i++) {
var fade = FadeOutDestroy(chessArray[i], duration);
bejeweled.waitEvent(fade, 'complete');
}
}
bejeweled.waitEvent(fade, 'complete'): Wait 'complete' event of this fade-out-destroy behavior.
Falling chess¶
Fire 'fall' event
bejeweled.on('fall', function(board, bejeweled) {
}, scope);
board: Board objectbejeweled: This bejeweled object.
Custom Falling Action¶
Default falling action:
function (board, bejeweled) {
var tileZ = bejeweled.chessTileZ,
chess, moveTo;
for (var tileY = (board.height - 1); tileY >= 0; tileY--) { // bottom to top
for (var tileX = 0, cnt = board.width; tileX < cnt; tileX++) { // left to right
chess = board.tileXYZToChess(tileX, tileY, tileZ);
if (chess === null) {
continue;
}
moveTo = bejeweled.getChessMoveTo(chess);
do {
moveTo.moveToward(1);
} while (moveTo.lastMoveResult)
if (moveTo.isRunning) {
bejeweled.waitEvent(moveTo, 'complete');
}
}
}
}
bejeweled.getChessMoveTo(chess): Get moveTo behavior of a chess.bejeweled.waitEvent(moveTo, 'complete'): Wait 'complete' event of this moveTo behavior.
Fill chess¶
Fill upper board, fire 'fill' event
bejeweled.on('fill', function(board, bejeweled) {
}, scope);
board: Board object.bejeweled: This bejeweled object.
Match end¶
Fire 'match-end' event
bejeweled.on('match-end', function(board, bejeweled) {
}, scope);
board: Board object.bejeweled: This bejeweled object.
Undo-swap selected chess¶
Fire 'undo-swap' event
bejeweled.on('undo-swap', function(selectedChess1, selectedChess2, board, bejeweled) {
}, scope);
board: Board object.bejeweled: This bejeweled object.
Custom Undo-Swap Action¶
Default undo-swap action : Equal to Swap action
Start gameplay¶
bejeweled.start();
Input control¶
Default input¶
- Enable default input control
var bejeweled = new Bejeweled(scene, { // ... input: true }); - Enable/disable temporarily.
- Enable
bejeweled.setInputEnable(); - Disable
bejeweled.setInputEnable(false);
- Enable
Custom input¶
- Discard default input control
var bejeweled = new Bejeweled(scene, { // ... input: false }); - Add custom input logic like
scene.input .on('pointerdown', function (pointer) { var chess = bejeweled.worldXYToChess(pointer.worldX, pointer.worldY); if (chess) { bejeweled.selectChess1(chess); } }, scene) .on('pointermove', function (pointer) { if (!pointer.isDown) { return; } var chess = bejeweled.worldXYToChess(pointer.worldX, pointer.worldY); if (chess && (chess !== this.bejeweled.getSelectedChess1())) { bejeweled.selectChess2(chess); } }, scene) .on('pointerup', function(pointer){ var chess = bejeweled.worldXYToChess(pointer.worldX, pointer.worldY); if (chess && (chess === bejeweled.getSelectedChess1())) { bejeweled.clickChess(chess); } })- Invoke
bejeweled.selectChess1(chess), andbejeweled.selectChess2(chess),bejeweled.clickChess(chess)under custom logic.
- Invoke
Helper methods
- Get chess via worldXY position
var chess = bejeweled.worldXYToChess(worldX, worldY); - Get chess via tileXY position
var chess = bejeweled.tileXYToChess(tileX, tileY); - Get neighbor chess via angle
var chess2 = bejeweled.getNeighborChessAtAngle(chess1, angle);chess1: Chess object, or tileXY position{x, y}.angle: Angle in radius.
- Get neighbor chess via direction
var chess2 = bejeweled.getNeighborChessAtDirection(chess1, direction);chess1: Chess object, or tileXY position{x, y}.direction: Number, or string number.0~3: Quad grid in 4 directions mode.0~7: Quad grid in 8 directions mode.0~5: Hexagon grid.
- Get chess in visible area
var chessArray = bejeweled.getChessArray(); // bejeweled.getChessArray(out); - Get chess at row or column, in visible area
var chessArray = bejeweled.getChessArrayAtTileX(tileX); // bejeweled.getChessArrayAtTileX(tileX, out);var chessArray = bejeweled.getChessArrayAtTileY(tileY); // bejeweled.getChessArrayAtTileY(tileX, out); - Get chess at tile position with range, in visible area
var chessArray = bejeweled.getChessArrayAtTileXYInRange(tileX, tileY, rangeX, rangeY); // bejeweled.getChessArrayAtTileXYInRange(tileX, tileY, rangeX, rangeY, out); - Get all chess with symobl, in visible area
var chessArray = bejeweled.getChessArrayWithSymbol(symbol); bejeweled.getChessArrayWithSymbol(symbol, out);
Await input¶
isAwaitingInput = bejeweled.isAwaitingInput();
- Return
trueonly when state is atSELECT1START
Save / load board¶
- Save board
var symbols = bejeweled.dumpSymbols();symbols: A 2d symbol array of whole board including prepared rows and visible area.- Invoke this method when
bejeweled.isAwaitingInput()returntrue
- Load board
bejeweled.loadSymbols(symbols);symbols: A 2d symbols array, return valeu ofbejeweled.dumpSymbols()- Invoke this method when
bejeweled.isAwaitingInput()returntrue - Chage state to
RESET->PLACE->SELECT1START-> ...
Data¶
- Get
var value = bejeweled.getData(key); var values = bejeweled.getData(keys); // keys: an array of keys var value = bejeweled.data.values[key]; - Set
bejeweled.setData(key, value); bejeweled.setData(obj); // obj: {key0:value0, key1:value1, ...} bejeweled.data.values[key] = value; bejeweled.data.values[key] += inc; - Enable
bejeweled.setDataEnabled(); - Events :
- Set data evant
bejeweled.on('setdata', function(bejeweled, key, value){ /* ... */ }); - Change data event
bejeweled.on('changedata', function(bejeweled, key, value, previousValue){ /* ... */ });bejeweled.on('changedata-' + key, function(bejeweled, value, previousValue){ /* ... */ });
- Set data evant
See data manager
Note
Ensure data manager is created before binding any data-changed events.