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.
 
 

493 lines
14 KiB

import Phaser from "phaser";
import tile_default from "../assets/sprites/tile_default.png";
import tiles from "../assets/sprites/tiles.png";
import bg from "../assets/sprites/bg.jpg";
// import move_mp3 from "../assets/sounds/move.mp3";
// import move_ogg from "../assets/sounds/move.ogg";
// import grow_mp3 from "../assets/sounds/grow.mp3";
// import grow_ogg from "../assets/sounds/grow.ogg";
import { COL, ROW, TILE_SIZE, TILE_SPACING, TWEEN_DURATION } from "../constant";
const tileMap = {
0: 7,
1: 9,
2: 11,
3: 1,
4: 3,
5: 5,
6: 6,
7: 8,
8: 10,
9: 0,
10: 2,
11: 4,
};
class PlayScene extends Phaser.Scene {
tileArray = [];
canMove = false;
score = 0; // 分数
constructor() {
super({
key: "PlayScene",
});
}
preload() {
this.load.image("tile_default", tile_default);
this.load.image("bg", bg);
this.load.spritesheet("tiles", tiles, {
frameWidth: 172,
frameHeight: 172,
});
// this.load.audio("move", [move_mp3, move_ogg]);
// this.load.audio("grow", [grow_mp3, grow_ogg]);
}
create() {
this.score = 0;
this.layout();
// 随机生成 2 个数字方块
this.addTile();
this.addTile();
this.addEvent();
// this.addSound();
}
//添加声音
addSound() {
this.moveSound = this.sound.add("move");
this.growSound = this.sound.add("grow");
}
addEvent() {
// 移动端操作
this.input.on("pointerup", this.handleTouch);
// 键盘操作
this.input.keyboard.on("keydown", this.handleKey);
}
handleTouch = (e) => {
if (this.canMove) {
// 计算按住时间
let swipeTime = e.upTime - e.downTime;
// 生成 {x: v1, y: v2} 格式
let swipe = new Phaser.Geom.Point(e.upX - e.downX, e.upY - e.downY);
// 获取对角线长度
let swipeMagnitude = Phaser.Geom.Point.GetMagnitude(swipe);
// 偏向方向比例 1 为直着朝一个方向
let swipeNormal = new Phaser.Geom.Point(
swipe.x / swipeMagnitude,
swipe.y / swipeMagnitude
);
/**
* 滑动的对角线长度大于 20
* 滑动的时间小于 1000 毫秒
* 滑动的角度尽量偏一个方向
*/
if (
swipeMagnitude > 20 &&
swipeTime < 1000 &&
(Math.abs(swipeNormal.y) > 0.8 || Math.abs(swipeNormal.x) > 0.8)
) {
let children = this.tileGroup.getChildren();
if (swipeNormal.x > 0.8) {
for (var i = 0; i < children.length; i++) {
// 设置层级,越左侧层级越高
children[i].depth = this.game.config.width - children[i].x;
}
this.move(0, 1);
}
if (swipeNormal.x < -0.8) {
for (var i = 0; i < children.length; i++) {
// 越向右层级越高
children[i].depth = children[i].x;
}
this.move(0, -1);
}
if (swipeNormal.y > 0.8) {
for (var i = 0; i < children.length; i++) {
// 越上面层级越高
children[i].depth = this.game.config.height - children[i].y;
}
this.move(1, 0);
}
if (swipeNormal.y < -0.8) {
for (var i = 0; i < children.length; i++) {
// 越下面层级越高
children[i].depth = children[i].y;
}
this.move(-1, 0);
}
}
}
};
handleKey = (e) => {
if (this.canMove) {
let children = this.tileGroup.getChildren();
switch (e.code) {
case "KeyA":
case "ArrowLeft":
for (var i = 0; i < children.length; i++) {
// 越向右层级越高
children[i].depth = children[i].x;
}
// 行为 0,不变化,列为 -1,向左
this.move(0, -1);
break;
case "KeyD":
case "ArrowRight":
for (var i = 0; i < children.length; i++) {
// 设置层级,越左侧层级越高
children[i].depth = this.game.config.width - children[i].x;
}
// 行为 0,不变化,列为 1,向右
this.move(0, 1);
break;
case "KeyW":
case "ArrowUp":
for (var i = 0; i < children.length; i++) {
// 越下面层级越高
children[i].depth = children[i].y;
}
// 列为 0,不变化,行为 -1,向上
this.move(-1, 0);
break;
case "KeyS":
case "ArrowDown":
for (var i = 0; i < children.length; i++) {
// 越上面层级越高
children[i].depth = this.game.config.height - children[i].y;
}
// 列为 0,不变化,行为 1,向下
this.move(1, 0);
break;
}
}
};
/**
*
* @param stepRow 行的变化,上下变化,上下滑动,0 为不变化, 1为进1块 -1为退1块
* @param stepCol 列的变化,左右变化,左右滑动,0 为不变化, 1为进1块 -1为退1块
*/
move(rowStep, colStep) {
this.canMove = false;
this.movingTiles = 0;
let somethingMoved = false;
let moveScore = 0;
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
/**
* 一块一块移动去判断
* 正确的移动顺序应该是:
* 向左移动(move(0, -1)):从左到右,从上到下遍历判断移动【默认遍历 i,j 即可】
* 向右移动(move(0, 1)) :从右到左,从上到下遍历判断移动【默认遍历 i,j 取反值】
* 向上移动(move(-1, 0) :从左到右,从上到下遍历判断移动【默认遍历 i,j 即可】
* 向下移动(move(1, 0)) :从左到右,从下到上遍历判断移动【默认遍历 j,i 取反值】
*/
let row = rowStep === 1 ? 3 - i : i;
let col = colStep === 1 ? 3 - j : j;
let tileValue = this.tileArray[row][col].tileValue;
if (tileValue !== 0) {
// 横向移动单位距离
let rowSteps = rowStep;
// 纵向移动单位距离
let colSteps = colStep;
// 条件一:靠边移动,不可超出
// 条件二:移动的地方没有数值方块
while (
this.isInsideBoard(row + rowSteps, col + colSteps) &&
this.tileArray[row + rowSteps][col + colSteps].tileValue === 0
) {
colSteps += colStep;
rowSteps += rowStep;
}
// 条件一:靠边移动,不可超出
// 条件二:目标方块与当前方块 tileValue 相等,也就是数值相等
// 条件三:目标方块 canUpgrade 为 true【控制一次只能被覆盖一次】
// 条件四:当前方块 canUpgrade 为 true
// 条件五:tileValue 小于 12 Math.pow(2,12) 4096,最大 4096
if (
this.isInsideBoard(row + rowSteps, col + colSteps) &&
this.tileArray[row + rowSteps][col + colSteps].tileValue ===
tileValue &&
this.tileArray[row + rowSteps][col + colSteps].canUpgrade &&
this.tileArray[row][col].canUpgrade &&
tileValue < 12
) {
// 目标方块 tileValue + 1, 本来是 Math.pow(2,1) 变成了 Math.pow(2,2),也就是方块 2 变成 4
this.tileArray[row + rowSteps][col + colSteps].tileValue++;
// 移动分数
moveScore += Math.pow(
2,
this.tileArray[row + rowSteps][col + colSteps].tileValue
);
// 目标块只能被覆盖一次
this.tileArray[row + rowSteps][col + colSteps].canUpgrade = false;
// 设置当前方块 tileValue 为 0
this.tileArray[row][col].tileValue = 0;
// 当前方块移动到目标方块
// 参数一:当前数值精灵
// 参数二:横向位置
// 参数三:纵向位置
// 参数四:移动单位距离
// 参数五: bool
this.moveTile(
this.tileArray[row][col],
row + rowSteps,
col + colSteps,
Math.abs(rowSteps + colSteps),
true
);
somethingMoved = true;
} else {
// while 时最后一次条件不成立,但 colSteps 与 rowSteps 已经加了col与row,所以这里减回去。
rowSteps = rowSteps - rowStep;
colSteps = colSteps - colStep;
// 若横向或纵向有移动,则开始移动
if (colSteps !== 0 || rowSteps !== 0) {
// console.log(row, rowSteps, col, colSteps)
// 设置移动到的地方值为当前值
this.tileArray[row + rowSteps][
col + colSteps
].tileValue = tileValue;
// 设置当前块的值为 0
this.tileArray[row][col].tileValue = 0;
// 参数一:精灵
// 参数二:横向位置
// 参数三:纵向位置
// 参数四:移动单位距离
// 参数五: bool
this.moveTile(
this.tileArray[row][col],
row + rowSteps,
col + colSteps,
Math.abs(rowSteps + colSteps),
false
);
somethingMoved = true;
}
}
}
}
}
if (!somethingMoved) {
this.canMove = true;
} else {
// this.moveSound.play();
this.score += moveScore;
}
}
/**
*
* @param {Sprite} tile
* @param {Number} row
* @param {Number} col
* @param {Number} distance 根据距离动态改变动画运行时间
* @param {Booler} changeNumber
*/
moveTile(tile, row, col, distance, changeNumber) {
this.movingTiles++;
this.tweens.add({
targets: [tile.tileSprite],
x: this.setPosition(col, COL),
y: this.setPosition(row, ROW),
duration: TWEEN_DURATION * distance,
onComplete: () => {
this.movingTiles--;
if (changeNumber) {
this.transformTile(tile, row, col);
}
if (this.movingTiles === 0) {
this.scoreText.text = "Game Score: " + this.score;
this.resetTiles();
this.addTile();
}
},
});
}
transformTile(tile, row, col) {
// this.growSound.play();
this.movingTiles++;
tile.tileSprite.setFrame(tileMap[this.tileArray[row][col].tileValue - 1]);
this.tweens.add({
targets: [tile.tileSprite],
scaleX: 1.1,
scaleY: 1.1,
duration: TWEEN_DURATION,
yoyo: true,
repeat: 1,
onComplete: () => {
this.movingTiles--;
if (this.movingTiles === 0) {
this.scoreText.text = "Game Score: " + this.score;
this.resetTiles();
this.addTile();
}
},
});
}
// 精灵归位,数组中指定的 tileValue 信息来显示数组中
resetTiles() {
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
this.tileArray[i][j].canUpgrade = true;
this.tileArray[i][j].tileSprite.x = this.setPosition(j, COL);
this.tileArray[i][j].tileSprite.y = this.setPosition(i, ROW);
if (this.tileArray[i][j].tileValue > 0) {
this.tileArray[i][j].tileSprite.alpha = 1;
this.tileArray[i][j].tileSprite.visible = true;
// 假如 tileValue = 2,则方块数值为 Math.pow(2,2) == 4, 4 的精灵索引为1,所以等于 tileValue - 1
this.tileArray[i][j].tileSprite.setFrame(
tileMap[this.tileArray[i][j].tileValue - 1]
);
} else {
this.tileArray[i][j].tileSprite.alpha = 0;
this.tileArray[i][j].tileSprite.visible = false;
}
}
}
}
// 布局
layout() {
this.bg = this.add.image(
this.game.config.width / 2,
this.game.config.height / 2,
"bg"
);
this.bg.displayWidth = this.game.config.width;
this.bg.displayHeight = this.game.config.height;
// 头部
this.layout_header();
// 4 * 4 方块
this.layout_body();
}
//设置头部
layout_header() {
this.scoreText = this.add.text(20, 20, "", {
fontSize: 20,
});
this.scoreText.text = "Game Score: " + this.score;
}
//设置底部布局
layout_body() {
this.tileGroup = this.add.group();
for (let i = 0; i < 4; i++) {
this.tileArray[i] = [];
for (let j = 0; j < 4; j++) {
const defaultTile = this.add.sprite(
this.setPosition(j, COL),
this.setPosition(i, ROW),
"tile_default"
);
defaultTile.displayWidth = TILE_SIZE;
defaultTile.displayHeight = TILE_SIZE;
let tile = this.add.sprite(
this.setPosition(j, COL),
this.setPosition(i, ROW),
"tiles"
);
tile.displayWidth = TILE_SIZE;
tile.displayHeight = TILE_SIZE;
tile.alpha = 0;
tile.visible = false;
this.tileArray[i][j] = {
tileValue: 0,
tileSprite: tile,
canUpgrade: true,
};
this.tileGroup.add(tile);
}
}
}
setPosition(pos, direction) {
let top = direction === ROW ? 270 : 160;
return (
pos * (TILE_SIZE + TILE_SPACING) +
TILE_SIZE * 0.5 +
TILE_SPACING * 2 +
top
);
}
//添加随机的2
addTile() {
let emptyTiles = [];
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
if (this.tileArray[i][j].tileValue === 0) {
emptyTiles.push({
row: i,
col: j,
});
}
}
}
if (emptyTiles.length > 0) {
let chosenTile = Phaser.Utils.Array.GetRandom(emptyTiles);
this.tileArray[chosenTile.row][chosenTile.col].tileValue = 1;
this.tileArray[chosenTile.row][chosenTile.col].tileSprite.visible = true;
this.tileArray[chosenTile.row][chosenTile.col].tileSprite.setFrame(
tileMap[0]
);
this.tweens.add({
targets: [this.tileArray[chosenTile.row][chosenTile.col].tileSprite],
alpha: 1,
duration: TWEEN_DURATION,
onComplete: () => {
this.canMove = true;
},
});
}
}
isInsideBoard(row, col) {
return row >= 0 && col >= 0 && row < 4 && col < 4;
}
}
export default PlayScene;