In puzzle games where we need to add borders to our puzzles and mark various zones, we can use Phase Path to draw those lines. For this example we are going to draw something like the image below
Let us start by creating our html file and including Phaser 3 js and our game js in it.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=no">
<script src="js/phaser.min.js"></script>
<script src="js/game.js"></script>
</head>
<body>
<div id="mygame"></div>
</body>
</html>
Now our JS code for would be
const BLANK= 0;
let launchGame = function () {
let config = {
type: Phaser.AUTO,
scale: {
parent: 'mygame',
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
width: window.innerWidth * window.devicePixelRatio,
height: window.innerHeight * window.devicePixelRatio
},
autoResize: true,
scene: [TheGame]
}
let game = new Phaser.Game(config);
};
class TheGame extends Phaser.Scene {
constructor() {
super("TheGame");
}
preload() {
this.load.image("blank", "images/blank.png");
}
init(config) {
}
create() {
this.cameras.main.setBackgroundColor('#004050');
this.size = 9;
this.board = [
[1, 1, 1, 1, 1, 1, 2, 2, 2],
[1, 3, 1, 3, 3, 3, 4, 2, 2],
[1, 3, 3, 3, 3, 5, 4, 4, 2],
[6, 6, 6, 3, 5, 5, 4, 2, 2],
[7, 6, 6, 5, 5, 5, 4, 4, 2],
[7, 7, 6, 5, 5, 8, 4, 4, 4],
[7, 6, 6, 5, 8, 8, 8, 8, 9],
[7, 7, 6, 8, 8, 8, 9, 8, 9],
[7, 7, 7, 9, 9, 9, 9, 9, 9]
];
this.gameArray = [];
for (let i = 0; i < this.size; i++) {
this.gameArray[i] = [];
for (let j = 0; j < this.size; j++) {
let v = this.board[i][j] ? this.board[i][j] : BLANK;
let tile = this.add.image(-900, -900, "blank").setOrigin(0);
this.gameArray[i][j] = {
sprite: tile
};
}
}
this.lines = this.add.graphics();
this.graphics = this.add.graphics();
this.scale.on('resize', this.resize, this);
this.positionControls(this.cameras.main.width, this.cameras.main.height);
}
getPosition(i, j) {
return { x: this.boardLeft + j * this.tileSize, y: this.boardTop + i * this.tileSize };
}
positionControls(width, height) {
let availableWidth = width, tileSize, scale, availableButtonSize;
tileSize = Math.min(availableWidth / (this.size + 1), height / (this.size + 3));
let tile = this.gameArray[0][0].sprite;
scale = scaleManager.scaleSprite(tile, tileSize, tileSize, 0, 1, true);
this.boardLeft = (width - tileSize * this.size) / 2;
this.boardTop = height * 0.5 - this.size * tileSize * 0.5;
this.tileSize = tileSize;
for (let x = 0; x < this.size; x++) {
for (let y = 0; y < this.size; y++) {
let pos = this.getPosition(x, y);
scaleManager.scaleSpriteTo(this.gameArray[x][y].sprite, scale);
this.gameArray[x][y].sprite.setPosition(pos.x, pos.y);
}
}
this.lines.clear();
this.lines.lineStyle(2, 0xC5C6C6, 1);
this.graphics.clear();
this.graphics.lineStyle(this.size > 10 ? 4 : 5, 0xE10C30, 1);
let path, lines;
for (let x = 0; x < this.size; x++) {
for (let y = 0; y < this.size; y++) {
let directions = { 'up': [-1, 0], 'down': [1, 0], 'left': [0, -1], 'right': [0, 1] }; // left, right, bottom, top
for (let key in directions) {
if (directions.hasOwnProperty(key)) {
let d = directions[key];
let dx = d[0];
let dy = d[1];
if (x + dx >= 0 && x + dx < this.size) {
if (y + dy >= 0 && y + dy < this.size) {
if (this.board[x + dx][y + dy] !== this.board[x][y]) {
switch (key) {
case 'left':
path = new Phaser.Curves.Path(this.gameArray[x][y].sprite.x, this.gameArray[x][y].sprite.y);
path.lineTo(this.gameArray[x][y].sprite.x, this.gameArray[x][y].sprite.y + tileSize);
break;
case 'right':
path = new Phaser.Curves.Path(this.gameArray[x + dx][y + dy].sprite.x, this.gameArray[x + dx][y + dy].sprite.y);
path.lineTo(this.gameArray[x + dx][y + dy].sprite.x, this.gameArray[x + dx][y + dy].sprite.y + tileSize);
break;
case 'down':
path = new Phaser.Curves.Path(this.gameArray[x + dx][y + dy].sprite.x, this.gameArray[x + dx][y + dy].sprite.y);
path.lineTo(this.gameArray[x + dx][y + dy].sprite.x + tileSize, this.gameArray[x + dx][y + dy].sprite.y);
break;
case 'up':
path = new Phaser.Curves.Path(this.gameArray[x][y].sprite.x, this.gameArray[x][y].sprite.y);
path.lineTo(this.gameArray[x][y].sprite.x + tileSize, this.gameArray[x][y].sprite.y);
break;
}
path.draw(this.graphics);
} else {
switch (key) {
case 'left':
lines = new Phaser.Curves.Path(this.gameArray[x][y].sprite.x, this.gameArray[x][y].sprite.y);
lines.lineTo(this.gameArray[x][y].sprite.x, this.gameArray[x][y].sprite.y + tileSize);
break;
case 'right':
lines = new Phaser.Curves.Path(this.gameArray[x + dx][y + dy].sprite.x, this.gameArray[x + dx][y + dy].sprite.y);
lines.lineTo(this.gameArray[x + dx][y + dy].sprite.x, this.gameArray[x + dx][y + dy].sprite.y + tileSize);
break;
case 'down':
lines = new Phaser.Curves.Path(this.gameArray[x + dx][y + dy].sprite.x, this.gameArray[x + dx][y + dy].sprite.y);
lines.lineTo(this.gameArray[x + dx][y + dy].sprite.x + tileSize, this.gameArray[x + dx][y + dy].sprite.y);
break;
case 'up':
lines = new Phaser.Curves.Path(this.gameArray[x][y].sprite.x, this.gameArray[x][y].sprite.y);
lines.lineTo(this.gameArray[x][y].sprite.x + tileSize, this.gameArray[x][y].sprite.y);
break;
}
lines.draw(this.lines);
}
}
}
}
}
}
}
// top
path = new Phaser.Curves.Path(this.gameArray[0][0].sprite.x, this.gameArray[0][0].sprite.y);
path.lineTo(this.gameArray[0][0].sprite.x + this.size * tileSize, this.gameArray[0][0].sprite.y);
path.draw(this.graphics);
// bottom
path = new Phaser.Curves.Path(this.gameArray[0][0].sprite.x, this.gameArray[0][0].sprite.y + this.size * tileSize);
path.lineTo(this.gameArray[0][0].sprite.x + this.size * tileSize, this.gameArray[this.size - 1][0].sprite.y + tileSize);
path.draw(this.graphics);
// left
path = new Phaser.Curves.Path(this.gameArray[0][0].sprite.x, this.gameArray[0][0].sprite.y);
path.lineTo(this.gameArray[0][0].sprite.x, this.gameArray[this.size - 1][0].sprite.y + tileSize);
path.draw(this.graphics);
// right
path = new Phaser.Curves.Path(this.gameArray[0][0].sprite.x + this.size * tileSize, this.gameArray[0][0].sprite.y);
path.lineTo(this.gameArray[0][0].sprite.x + this.size * tileSize, this.gameArray[this.size - 1][0].sprite.y + tileSize);
path.draw(this.graphics);
}
resize(gameSize, baseSize, displaySize, resolution) {
let width = gameSize.width;
let height = gameSize.height;
this.cameras.resize(width, height);
this.positionControls(width, height);
}
}
function LocalScaleManager() {
}
LocalScaleManager.prototype = {
scaleSprite: function (sprite, availableSpaceWidth, availableSpaceHeight, padding, scaleMultiplier, isFullScale) {
let scale = this.getSpriteScale(sprite.frame.width, sprite.frame.height, availableSpaceWidth, availableSpaceHeight, padding, isFullScale);
sprite.setScale(scale * scaleMultiplier);
return scale;
},
scaleSpriteTo: function (sprite, scale) {
sprite.setScale(scale);
},
getSpriteScale: function (spriteWidth, spriteHeight, availableSpaceWidth, availableSpaceHeight, minPadding, isFullScale) {
let ratio = 1;
let currentDevicePixelRatio = window.devicePixelRatio;
// Sprite needs to fit in either width or height
let widthRatio = (spriteWidth * currentDevicePixelRatio + 2 * minPadding) / availableSpaceWidth;
let heightRatio = (spriteHeight * currentDevicePixelRatio + 2 * minPadding) / availableSpaceHeight;
if (widthRatio > 1 || heightRatio > 1) {
ratio = 1 / Math.max(widthRatio, heightRatio);
} else {
if (isFullScale)
ratio = 1 / Math.max(widthRatio, heightRatio);
}
return ratio * currentDevicePixelRatio;
}
};
let scaleManager = new LocalScaleManager;
launchGame();
Try running this locally in your web server and you will see the result as shown in the screenshot.