You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
532 lines
11 KiB
532 lines
11 KiB
import Phaser from "phaser";
|
|
|
|
import bg from "./bg.jpg";
|
|
import tiles from "./tiles.png";
|
|
import sprites from "./tiles.json";
|
|
import title from "./title.png";
|
|
import instruction from "./instruction.png";
|
|
|
|
class PlayScene extends Phaser.Scene {
|
|
constructor() {
|
|
super({
|
|
key: "PlayScene",
|
|
});
|
|
this.allowClick = true;
|
|
|
|
this.gridBG;
|
|
|
|
this.instructions;
|
|
this.text1;
|
|
this.text2;
|
|
this.text3;
|
|
|
|
this.currentColor = "";
|
|
|
|
this.emitters = {};
|
|
|
|
this.grid = [];
|
|
this.matched = [];
|
|
|
|
this.moves = 25;
|
|
|
|
this.frames = [
|
|
"red.png",
|
|
"bu.png",
|
|
"grey.png",
|
|
"ye.png",
|
|
"pr.png",
|
|
"gr.png",
|
|
];
|
|
}
|
|
|
|
preload() {
|
|
this.load.image("bg", bg);
|
|
this.load.image("title", title);
|
|
this.load.atlas("tiles", tiles, sprites);
|
|
this.load.image("instruction", instruction);
|
|
}
|
|
|
|
create() {
|
|
const bg = this.add.image(0, 0, "bg");
|
|
bg.setOrigin(0);
|
|
bg.displayWidth = 1080;
|
|
bg.displayHeight = 1080;
|
|
this.createIcon(0, 0, 272);
|
|
this.createIcon(1, 0, 272 + 252);
|
|
this.createIcon(2, 0, 272 + 252 * 2);
|
|
this.createIcon(3, 1080 - 184, 272);
|
|
this.createIcon(4, 1080 - 184, 272 + 252);
|
|
this.createIcon(5, 1080 - 184, 272 + 252 * 2);
|
|
|
|
// The game is played in a 14x14 grid with 6 different colors
|
|
|
|
this.grid = [];
|
|
|
|
for (var x = 0; x < 14; x++) {
|
|
this.grid[x] = [];
|
|
|
|
for (var y = 0; y < 14; y++) {
|
|
var sx = 204 + x * 48;
|
|
var sy = 309 + y * 48;
|
|
var color = Phaser.Math.Between(0, 5);
|
|
|
|
var block = this.add.image(sx, -1032 + sy, "tiles", this.frames[color]);
|
|
block.setOrigin(0);
|
|
block.displayWidth = 48;
|
|
block.displayHeight = 48;
|
|
block.setData("oldColor", color);
|
|
block.setData("color", color);
|
|
block.setData("x", sx);
|
|
block.setData("y", sy);
|
|
|
|
this.grid[x][y] = block;
|
|
}
|
|
}
|
|
|
|
// Do a few floods just to make it a little easier starting off
|
|
this.helpFlood();
|
|
|
|
for (var i = 0; i < this.matched.length; i++) {
|
|
var blockk = this.matched[i];
|
|
|
|
blockk.setFrame(this.frames[blockk.getData("color")]);
|
|
}
|
|
|
|
this.currentColor = this.grid[0][0].getData("color");
|
|
|
|
this.particles = this.add.particles("tiles");
|
|
|
|
for (var i = 0; i < this.frames.length; i++) {
|
|
this.createEmitter(this.frames[i]);
|
|
}
|
|
const title = this.add.image(1080 / 2, 228, "title");
|
|
title.displayWidth = 222;
|
|
title.displayHeight = 50;
|
|
this.text1 = this.add
|
|
.text(440, 160, "MOVES:", {
|
|
fontSize: 40,
|
|
color: "#fff",
|
|
})
|
|
.setAlpha(0);
|
|
this.text2 = this.add
|
|
.text(590, 160, "00", { fontSize: 40, color: "#fff" })
|
|
.setAlpha(0);
|
|
this.text3 = this.add
|
|
.text(180, 200, "差一点!\n\n点击\n再试一次", 48)
|
|
.setAlpha(0);
|
|
|
|
this.instructions = this.add.image(500, 600, "instruction").setAlpha(0);
|
|
//初始化位置
|
|
this.revealGrid();
|
|
}
|
|
|
|
update() {}
|
|
|
|
helpFlood() {
|
|
for (var i = 0; i < 8; i++) {
|
|
var x = Phaser.Math.Between(0, 13);
|
|
var y = Phaser.Math.Between(0, 13);
|
|
|
|
var oldColor = this.grid[x][y].getData("color");
|
|
var newColor = oldColor + 1;
|
|
|
|
if (newColor === 6) {
|
|
newColor = 0;
|
|
}
|
|
|
|
this.floodFill(oldColor, newColor, x, y);
|
|
}
|
|
}
|
|
|
|
createIcon(color, x, y) {
|
|
const area = this.add.rectangle(x, y, 184, 758 / 3);
|
|
area.setOrigin(0);
|
|
area.setData("color", color);
|
|
area.setInteractive();
|
|
}
|
|
|
|
revealGrid() {
|
|
this.tweens.add({
|
|
targets: this.gridBG,
|
|
y: 300,
|
|
ease: "Power3",
|
|
});
|
|
|
|
var i = 500;
|
|
|
|
for (var y = 13; y >= 0; y--) {
|
|
for (var x = 0; x < 14; x++) {
|
|
var block = this.grid[x][y];
|
|
|
|
this.tweens.add({
|
|
targets: block,
|
|
|
|
y: block.getData("y"),
|
|
|
|
ease: "Power3",
|
|
duration: 500,
|
|
delay: i,
|
|
});
|
|
|
|
i += 20;
|
|
}
|
|
}
|
|
|
|
i -= 1000;
|
|
|
|
// Text
|
|
|
|
this.tweens.add({
|
|
targets: [this.text1, this.text2],
|
|
alpha: 1,
|
|
ease: "Power3",
|
|
delay: i,
|
|
});
|
|
|
|
i += 500;
|
|
|
|
var movesTween = this.tweens.addCounter({
|
|
from: 0,
|
|
to: 25,
|
|
ease: "Power1",
|
|
onUpdate: function (tween, targets, text) {
|
|
text.setText(
|
|
Phaser.Utils.String.Pad(tween.getValue().toFixed(), 2, "0", 1)
|
|
);
|
|
},
|
|
onUpdateParams: [this.text2],
|
|
delay: i,
|
|
});
|
|
|
|
i += 500;
|
|
|
|
this.tweens.add({
|
|
targets: [this.instructions],
|
|
alpha: 1,
|
|
ease: "Power3",
|
|
delay: i,
|
|
});
|
|
|
|
this.time.delayedCall(i, this.startInputEvents, [], this);
|
|
}
|
|
|
|
startInputEvents() {
|
|
this.input.on("gameobjectdown", this.onIconDown, this);
|
|
|
|
// Cheat mode :)
|
|
|
|
this.input.keyboard.on(
|
|
"keydown_M",
|
|
function () {
|
|
this.moves++;
|
|
this.text2.setText(Phaser.Utils.String.Pad(this.moves, 2, "0", 1));
|
|
},
|
|
this
|
|
);
|
|
|
|
this.input.keyboard.on(
|
|
"keydown_X",
|
|
function () {
|
|
this.moves--;
|
|
this.text2.setText(Phaser.Utils.String.Pad(this.moves, 2, "0", 1));
|
|
},
|
|
this
|
|
);
|
|
}
|
|
|
|
stopInputEvents() {
|
|
this.input.off("gameobjectdown", this.onIconDown);
|
|
}
|
|
|
|
onIconDown(pointer, gameObject) {
|
|
if (!this.allowClick) {
|
|
return;
|
|
}
|
|
var icon = gameObject;
|
|
|
|
var newColor = icon.getData("color");
|
|
|
|
// Valid color?
|
|
if (newColor === this.currentColor) {
|
|
return;
|
|
}
|
|
|
|
var oldColor = this.grid[0][0].getData("color");
|
|
// console.log('starting flood from', oldColor, this.frames[oldColor], 'to', newColor, this.frames[newColor]);
|
|
|
|
if (oldColor !== newColor) {
|
|
this.currentColor = newColor;
|
|
|
|
this.matched = [];
|
|
|
|
this.instructions.setVisible(false);
|
|
|
|
this.moves--;
|
|
|
|
this.text2.setText(Phaser.Utils.String.Pad(this.moves, 2, "0", 1));
|
|
|
|
this.floodFill(oldColor, newColor, 0, 0);
|
|
|
|
if (this.matched.length > 0) {
|
|
this.startFlow();
|
|
}
|
|
}
|
|
}
|
|
|
|
createEmitter(color) {
|
|
this.emitters[color] = this.particles.createEmitter({
|
|
frame: color,
|
|
lifespan: 1000,
|
|
speed: { min: 300, max: 400 },
|
|
alpha: { start: 1, end: 0 },
|
|
scale: { start: 0.5, end: 0 },
|
|
rotate: { start: 0, end: 360, ease: "Power2" },
|
|
blendMode: "ADD",
|
|
on: false,
|
|
});
|
|
}
|
|
|
|
startFlow() {
|
|
this.matched.sort(function (a, b) {
|
|
var aDistance = Phaser.Math.Distance.Between(a.x, a.y, 166, 66);
|
|
var bDistance = Phaser.Math.Distance.Between(b.x, b.y, 166, 66);
|
|
|
|
return aDistance - bDistance;
|
|
});
|
|
|
|
// Swap the sprites
|
|
|
|
var t = 0;
|
|
var inc = this.matched.length > 98 ? 6 : 12;
|
|
|
|
this.allowClick = false;
|
|
|
|
for (var i = 0; i < this.matched.length; i++) {
|
|
var block = this.matched[i];
|
|
|
|
var blockColor = this.frames[block.getData("color")];
|
|
var oldBlockColor = this.frames[block.getData("oldColor")];
|
|
|
|
var emitter = this.emitters[oldBlockColor];
|
|
|
|
this.time.delayedCall(
|
|
t,
|
|
function (block, blockColor) {
|
|
block.setFrame(blockColor);
|
|
emitter.explode(6, block.x, block.y);
|
|
},
|
|
[block, blockColor, emitter]
|
|
);
|
|
|
|
t += inc;
|
|
}
|
|
|
|
this.time.delayedCall(
|
|
t,
|
|
function () {
|
|
this.allowClick = true;
|
|
|
|
if (this.checkWon()) {
|
|
this.gameWon();
|
|
} else if (this.moves === 0) {
|
|
this.gameLost();
|
|
}
|
|
},
|
|
[],
|
|
this
|
|
);
|
|
}
|
|
|
|
checkWon() {
|
|
var topLeft = this.grid[0][0].getData("color");
|
|
|
|
for (var x = 0; x < 14; x++) {
|
|
for (var y = 0; y < 14; y++) {
|
|
if (this.grid[x][y].getData("color") !== topLeft) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
clearGrid() {
|
|
// Hide everything :)
|
|
|
|
var i = 500;
|
|
|
|
for (var y = 13; y >= 0; y--) {
|
|
for (var x = 0; x < 14; x++) {
|
|
var block = this.grid[x][y];
|
|
|
|
this.tweens.add({
|
|
targets: block,
|
|
|
|
scaleX: 0,
|
|
scaleY: 0,
|
|
|
|
ease: "Power3",
|
|
duration: 800,
|
|
delay: i,
|
|
});
|
|
|
|
i += 10;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
gameLost() {
|
|
this.stopInputEvents();
|
|
|
|
this.text1.setText("Lost!");
|
|
this.text2.setText(":(");
|
|
|
|
var i = this.clearGrid();
|
|
|
|
this.text3.setAlpha(0);
|
|
this.text3.setVisible(true);
|
|
|
|
this.tweens.add({
|
|
targets: this.text3,
|
|
alpha: 1,
|
|
duration: 1000,
|
|
delay: i,
|
|
});
|
|
this.game.onLose && this.game.onLose();
|
|
this.input.once("pointerdown", this.resetGame, this);
|
|
}
|
|
|
|
resetGame() {
|
|
this.text1.setText("Moves");
|
|
this.text2.setText("00");
|
|
this.text3.setVisible(false);
|
|
|
|
// Show everything :)
|
|
|
|
var i = 500;
|
|
|
|
for (var y = 13; y >= 0; y--) {
|
|
for (var x = 0; x < 14; x++) {
|
|
var block = this.grid[x][y];
|
|
|
|
// Set a new color
|
|
var color = Phaser.Math.Between(0, 5);
|
|
|
|
block.setFrame(this.frames[color]);
|
|
|
|
block.setData("oldColor", color);
|
|
block.setData("color", color);
|
|
|
|
this.tweens.add({
|
|
targets: block,
|
|
|
|
scaleX: 1,
|
|
scaleY: 1,
|
|
|
|
ease: "Power3",
|
|
duration: 800,
|
|
delay: i,
|
|
});
|
|
|
|
i += 10;
|
|
}
|
|
}
|
|
|
|
// Do a few floods just to make it a little easier starting off
|
|
this.helpFlood();
|
|
|
|
for (var i = 0; i < this.matched.length; i++) {
|
|
var block = this.matched[i];
|
|
|
|
block.setFrame(this.frames[block.getData("color")]);
|
|
}
|
|
|
|
this.currentColor = this.grid[0][0].getData("color");
|
|
|
|
this.tweens.addCounter({
|
|
from: 0,
|
|
to: 25,
|
|
ease: "Power1",
|
|
onUpdate: function (tween, targets, text) {
|
|
text.setText(
|
|
Phaser.Utils.String.Pad(tween.getValue().toFixed(), 2, "0", 1)
|
|
);
|
|
},
|
|
onUpdateParams: [this.text2],
|
|
delay: i,
|
|
});
|
|
|
|
this.moves = 25;
|
|
|
|
this.time.delayedCall(i, this.startInputEvents, [], this);
|
|
}
|
|
|
|
gameWon() {
|
|
this.game.onWon && this.game.onWon();
|
|
this.stopInputEvents();
|
|
|
|
this.text1.setText("Won!!");
|
|
this.text2.setText(":)");
|
|
|
|
var i = this.clearGrid();
|
|
|
|
this.time.delayedCall(2000, this.boom, [], this);
|
|
}
|
|
|
|
boom() {
|
|
var color = Phaser.Math.RND.pick(this.frames);
|
|
|
|
this.emitters[color].explode(
|
|
8,
|
|
Phaser.Math.Between(128, 672),
|
|
Phaser.Math.Between(28, 572)
|
|
);
|
|
|
|
color = Phaser.Math.RND.pick(this.frames);
|
|
|
|
this.emitters[color].explode(
|
|
8,
|
|
Phaser.Math.Between(128, 672),
|
|
Phaser.Math.Between(28, 572)
|
|
);
|
|
|
|
this.time.delayedCall(100, this.boom, [], this);
|
|
}
|
|
//调低难度中间放一些相同颜色的
|
|
floodFill(oldColor, newColor, x, y) {
|
|
if (
|
|
oldColor === newColor ||
|
|
this.grid[x][y].getData("color") !== oldColor
|
|
) {
|
|
return;
|
|
}
|
|
|
|
this.grid[x][y].setData("oldColor", oldColor);
|
|
this.grid[x][y].setData("color", newColor);
|
|
|
|
if (this.matched.indexOf(this.grid[x][y]) === -1) {
|
|
this.matched.push(this.grid[x][y]);
|
|
}
|
|
|
|
if (x > 0) {
|
|
this.floodFill(oldColor, newColor, x - 1, y);
|
|
}
|
|
|
|
if (x < 13) {
|
|
this.floodFill(oldColor, newColor, x + 1, y);
|
|
}
|
|
|
|
if (y > 0) {
|
|
this.floodFill(oldColor, newColor, x, y - 1);
|
|
}
|
|
|
|
if (y < 13) {
|
|
this.floodFill(oldColor, newColor, x, y + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
export default PlayScene;
|
|
|