|
|
|
@ -1,23 +1,37 @@ |
|
|
|
import Phaser from "phaser"; |
|
|
|
import tiles from "../assets/a.png"; |
|
|
|
import tiles from "../assets/tiles.png"; |
|
|
|
import bg from "../assets/bg.jpg"; |
|
|
|
import gridTile from "../assets/gridTile.png"; |
|
|
|
/** |
|
|
|
*博客 |
|
|
|
* https://www.emanueleferonato.com/2020/04/17/html5-prototype-of-bricks-hyper-casual-game-with-phaser-and-arcade-physics-step-2-adding-score/
|
|
|
|
*/ |
|
|
|
const gameWidth = 927; |
|
|
|
|
|
|
|
const gameWidth = 924; |
|
|
|
const tileSpace = 3; |
|
|
|
const offsetLeft = (1080 - gameWidth) / 2; |
|
|
|
const offsetTop = 200; |
|
|
|
const columns = 9; |
|
|
|
const rows = 20; |
|
|
|
const getRandom = () => Math.floor(Math.random() * 5); |
|
|
|
const getValues = () => |
|
|
|
Phaser.Utils.Array.Shuffle([ |
|
|
|
0, |
|
|
|
1, |
|
|
|
2, |
|
|
|
3, |
|
|
|
4, |
|
|
|
getRandom(), |
|
|
|
getRandom(), |
|
|
|
getRandom(), |
|
|
|
getRandom(), |
|
|
|
]); |
|
|
|
const tileSize = (gameWidth - tileSpace * (columns - 1)) / columns; |
|
|
|
export default class Rect extends Phaser.Scene { |
|
|
|
constructor() { |
|
|
|
super({ key: "Rect" }); |
|
|
|
this.gameOptions = { |
|
|
|
// number of columns
|
|
|
|
columns: 9, |
|
|
|
columns, |
|
|
|
|
|
|
|
// number of rows, must be high enough to allow object pooling
|
|
|
|
rows: 20, |
|
|
|
rows, |
|
|
|
|
|
|
|
// tile speed in pixels per second
|
|
|
|
tileSpeed: 100, |
|
|
|
@ -27,8 +41,8 @@ export default class Rect extends Phaser.Scene { |
|
|
|
this.load.image("bg", bg); |
|
|
|
this.load.image("gridTile", gridTile); |
|
|
|
this.load.spritesheet("tiles", tiles, { |
|
|
|
frameWidth: 107, |
|
|
|
frameHeight: 107, |
|
|
|
frameWidth: 202, |
|
|
|
frameHeight: 202, |
|
|
|
}); |
|
|
|
} |
|
|
|
create() { |
|
|
|
@ -39,24 +53,37 @@ export default class Rect extends Phaser.Scene { |
|
|
|
); |
|
|
|
this.bg.displayWidth = this.game.config.width; |
|
|
|
this.bg.displayHeight = this.game.config.height; |
|
|
|
|
|
|
|
// physics group which manages all tiles in game
|
|
|
|
this.tileGroup = this.physics.add.group(); |
|
|
|
|
|
|
|
// determining tile size according to game width and columns
|
|
|
|
this.tileSize = |
|
|
|
(gameWidth - tileSpace * (this.gameOptions.columns - 1)) / |
|
|
|
this.gameOptions.columns; |
|
|
|
|
|
|
|
const shape = this.add.graphics(); |
|
|
|
shape.fillStyle = "#000"; |
|
|
|
shape.beginPath(); |
|
|
|
shape.moveTo(0, tileSize * 8 + tileSpace * 7 + offsetTop); |
|
|
|
shape.lineTo(1080, tileSize * 8 + tileSpace * 7 + offsetTop); |
|
|
|
shape.lineTo(1080, 1080); |
|
|
|
shape.lineTo(0, 1080); |
|
|
|
shape.lineTo(0, tileSize * 8 + tileSpace * 7 + offsetTop); |
|
|
|
shape.fillPath(); |
|
|
|
|
|
|
|
const mask = shape.createGeometryMask(); |
|
|
|
mask.setInvertAlpha(true); |
|
|
|
for (let i = 0; i < this.gameOptions.columns; i++) { |
|
|
|
for (let j = 0; j < 8; j++) { |
|
|
|
const defaultTile = this.add.image( |
|
|
|
offsetLeft + i * (tileSize + tileSpace), |
|
|
|
offsetTop + j * (tileSize + tileSpace), |
|
|
|
"gridTile" |
|
|
|
); |
|
|
|
defaultTile.setOrigin(0); |
|
|
|
defaultTile.displayWidth = tileSize; |
|
|
|
defaultTile.displayHeight = tileSize; |
|
|
|
} |
|
|
|
} |
|
|
|
// time to add tiles to the game
|
|
|
|
for (let i = 0; i < this.gameOptions.rows; i++) { |
|
|
|
// build an array with integers between 0 and this.gameOptions.columns - 1: [0, 1, 2, ..., this.gameOptions.columns - 1]
|
|
|
|
let values = Phaser.Utils.Array.NumberArray( |
|
|
|
0, |
|
|
|
this.gameOptions.columns - 1 |
|
|
|
); |
|
|
|
|
|
|
|
// then we shuffle the array
|
|
|
|
Phaser.Utils.Array.Shuffle(values); |
|
|
|
let values = getValues(); |
|
|
|
|
|
|
|
// save middle column color of first row
|
|
|
|
if (i == 0) { |
|
|
|
@ -67,34 +94,32 @@ export default class Rect extends Phaser.Scene { |
|
|
|
for (let j = 0; j < this.gameOptions.columns; j++) { |
|
|
|
// add a tile. Tile frame is set according to "values" shuffled array
|
|
|
|
let tile = this.tileGroup.create( |
|
|
|
offsetLeft + j * this.tileSize, |
|
|
|
i * this.tileSize + (this.game.config.height / 4) * 3, |
|
|
|
offsetLeft + j * (tileSize + tileSpace), |
|
|
|
i * (tileSize + tileSpace) + (this.game.config.height / 4) * 3, |
|
|
|
"tiles", |
|
|
|
values[j] |
|
|
|
); |
|
|
|
|
|
|
|
tile.setMask(mask); |
|
|
|
// call adjustTile method to adjust tile origin and display size
|
|
|
|
this.adjustTile(tile); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// let's build once again an array with integers between 0 and this.gameOptions.columns - 1
|
|
|
|
let values = Phaser.Utils.Array.NumberArray( |
|
|
|
0, |
|
|
|
this.gameOptions.columns - 1 |
|
|
|
); |
|
|
|
let values = getValues(); |
|
|
|
|
|
|
|
// remove the item at "middlecolor" position because we don't want it to be randomly selected
|
|
|
|
values.splice(middleColor, 1); |
|
|
|
values = values.filter((v) => middleColor != v); |
|
|
|
|
|
|
|
// add the player to the this.this.game. Player color is picked amoung "values" array items, which does not contain anymore "middlecolor" value
|
|
|
|
this.player = this.tileGroup.create( |
|
|
|
offsetLeft + this.tileSize * Math.floor(this.gameOptions.columns / 2), |
|
|
|
(this.game.config.height / 4) * 3 - this.tileSize, |
|
|
|
offsetLeft + |
|
|
|
(tileSize + tileSpace) * Math.floor(this.gameOptions.columns / 2), |
|
|
|
(this.game.config.height / 4) * 3 - (tileSize + tileSpace), |
|
|
|
"tiles", |
|
|
|
Phaser.Utils.Array.GetRandom(values) |
|
|
|
); |
|
|
|
|
|
|
|
this.player.setMask(mask); |
|
|
|
// adjust player origin and display size
|
|
|
|
this.adjustTile(this.player); |
|
|
|
|
|
|
|
@ -104,12 +129,11 @@ export default class Rect extends Phaser.Scene { |
|
|
|
// add score text
|
|
|
|
this.scoreText = this.add.text(0, 0, "0", { |
|
|
|
fontFamily: "Arial Black", |
|
|
|
fontSize: this.tileSize / 3, |
|
|
|
fontSize: tileSize / 2, |
|
|
|
color: "#ffffff", |
|
|
|
}); |
|
|
|
|
|
|
|
// set a stroke to score text
|
|
|
|
this.scoreText.setStroke("#000000", this.tileSize / 6); |
|
|
|
this.scoreText.setMask(mask); |
|
|
|
|
|
|
|
// method to adjust score position
|
|
|
|
this.adjustScorePosition(); |
|
|
|
@ -130,10 +154,9 @@ export default class Rect extends Phaser.Scene { |
|
|
|
// method to be executed at each frame
|
|
|
|
update() { |
|
|
|
// if the player touches the top of the screen...
|
|
|
|
if (this.player.y < 200) { |
|
|
|
if (this.player.y < offsetTop) { |
|
|
|
// gmae over man, restart the this.game
|
|
|
|
//this.scene.start("Rect");
|
|
|
|
//this.scene.stop
|
|
|
|
this.game.onLose && this.game.onLose(); |
|
|
|
this.tileGroup.setVelocityY(0); |
|
|
|
this.input.off("pointerdown"); |
|
|
|
this.input.on( |
|
|
|
@ -154,17 +177,17 @@ export default class Rect extends Phaser.Scene { |
|
|
|
sprite.setOrigin(0); |
|
|
|
|
|
|
|
// set display width and height to "tileSize" pixels
|
|
|
|
sprite.displayWidth = this.tileSize; |
|
|
|
sprite.displayHeight = this.tileSize; |
|
|
|
sprite.displayWidth = tileSize; |
|
|
|
sprite.displayHeight = tileSize; |
|
|
|
} |
|
|
|
|
|
|
|
// method to adjust score position
|
|
|
|
adjustScorePosition() { |
|
|
|
// adjust score position according to its bounding box and player position
|
|
|
|
this.scoreText.x = |
|
|
|
this.player.x + (this.tileSize - this.scoreText.getBounds().width) / 2; |
|
|
|
this.player.x + (tileSize - this.scoreText.getBounds().width) / 2; |
|
|
|
this.scoreText.y = |
|
|
|
this.player.y + (this.tileSize - this.scoreText.getBounds().height) / 2; |
|
|
|
this.player.y + (tileSize - this.scoreText.getBounds().height) / 2; |
|
|
|
} |
|
|
|
|
|
|
|
// method to move player tile
|
|
|
|
@ -172,11 +195,13 @@ export default class Rect extends Phaser.Scene { |
|
|
|
// if we can move...
|
|
|
|
if (this.canMove) { |
|
|
|
// determine column according to input coordinate and tile size
|
|
|
|
let column = Math.floor((pointer.x - offsetLeft) / this.tileSize); |
|
|
|
|
|
|
|
let column = Math.floor((pointer.x - offsetLeft) / tileSize); |
|
|
|
if (column > 8) column = 8; |
|
|
|
if (column < 0) column = 0; |
|
|
|
// get the ditance from current player tile and destination
|
|
|
|
let distance = Math.floor( |
|
|
|
Math.abs(column * this.tileSize - this.player.x) / this.tileSize |
|
|
|
Math.abs(offsetLeft + column * (tileSize + tileSpace) - this.player.x) / |
|
|
|
tileSize |
|
|
|
); |
|
|
|
|
|
|
|
// did we actually move?
|
|
|
|
@ -187,7 +212,7 @@ export default class Rect extends Phaser.Scene { |
|
|
|
// tween the player to destination tile
|
|
|
|
this.tweens.add({ |
|
|
|
targets: [this.player], |
|
|
|
x: offsetLeft + column * this.tileSize, |
|
|
|
x: offsetLeft + column * (tileSize + tileSpace), |
|
|
|
duration: distance * 30, |
|
|
|
callbackScope: this, |
|
|
|
onComplete: function () { |
|
|
|
@ -203,8 +228,8 @@ export default class Rect extends Phaser.Scene { |
|
|
|
checkMatch() { |
|
|
|
// get tile below player tile
|
|
|
|
let tileBelow = this.physics.overlapRect( |
|
|
|
this.player.x + this.tileSize / 2, |
|
|
|
this.player.y + this.tileSize * 1.5, |
|
|
|
this.player.x + tileSize / 2, |
|
|
|
this.player.y + tileSize * 1.5, |
|
|
|
1, |
|
|
|
1 |
|
|
|
); |
|
|
|
@ -217,7 +242,7 @@ export default class Rect extends Phaser.Scene { |
|
|
|
// check the whole row below player tile
|
|
|
|
let rowBelow = this.physics.overlapRect( |
|
|
|
0, |
|
|
|
this.player.y + this.tileSize * 1.5, |
|
|
|
this.player.y + tileSize * 1.5, |
|
|
|
gameWidth, |
|
|
|
1 |
|
|
|
); |
|
|
|
@ -242,18 +267,13 @@ export default class Rect extends Phaser.Scene { |
|
|
|
this.scoreText.setText(this.score); |
|
|
|
|
|
|
|
// the good old array with all integers from zero to this.gameOptions.columns - 1
|
|
|
|
let values = Phaser.Utils.Array.NumberArray( |
|
|
|
0, |
|
|
|
this.gameOptions.columns - 1 |
|
|
|
); |
|
|
|
|
|
|
|
// let's shuffle the array
|
|
|
|
Phaser.Utils.Array.Shuffle(values); |
|
|
|
let values = getValues(); |
|
|
|
|
|
|
|
// place all tiles below the lowest row
|
|
|
|
for (let i = 0; i < this.gameOptions.columns; i++) { |
|
|
|
rowBelow[i].gameObject.setFrame(values[i]); |
|
|
|
rowBelow[i].gameObject.y += this.tileSize * this.gameOptions.rows; |
|
|
|
rowBelow[i].gameObject.y += |
|
|
|
(tileSize + tileSpace) * this.gameOptions.rows; |
|
|
|
} |
|
|
|
|
|
|
|
// check for matches again, there could be a combo
|
|
|
|
@ -274,17 +294,14 @@ export default class Rect extends Phaser.Scene { |
|
|
|
|
|
|
|
// get the tile below the player
|
|
|
|
let tileBelow = this.physics.overlapRect( |
|
|
|
this.player.x + this.tileSize / 2, |
|
|
|
this.player.y + this.tileSize * 1.5, |
|
|
|
this.player.x + tileSize / 2, |
|
|
|
this.player.y + tileSize * 1.5, |
|
|
|
1, |
|
|
|
1 |
|
|
|
); |
|
|
|
|
|
|
|
// the good old array with all integers from zero to this.gameOptions.columns - 1
|
|
|
|
let values = Phaser.Utils.Array.NumberArray( |
|
|
|
0, |
|
|
|
this.gameOptions.columns - 1 |
|
|
|
); |
|
|
|
let values = [0, 1, 2, 3, 4]; |
|
|
|
|
|
|
|
// remove the item at "frame" value of tile below the player pbecause we don't want it to be randomly selected
|
|
|
|
values.splice(tileBelow[0].gameObject.frame.name, 1); |
|
|
|
|