|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 997 B |
|
After Width: | Height: | Size: 6.5 KiB |
@ -0,0 +1,32 @@ |
|||||
|
import Phaser from "phaser"; |
||||
|
import flipbird from "./scenes/flipbird/flipbird"; |
||||
|
|
||||
|
function launch({ containerId, onLose }) { |
||||
|
const container = document.getElementById(containerId); |
||||
|
const width = container.clientWidth; |
||||
|
const height = container.clientHeight; |
||||
|
const game = new Phaser.Game({ |
||||
|
type: Phaser.AUTO, |
||||
|
width, |
||||
|
height, |
||||
|
parent: containerId, |
||||
|
physics: { |
||||
|
default: "arcade", |
||||
|
arcade: { |
||||
|
//gravity: { y: 300 },
|
||||
|
debug: false, |
||||
|
}, |
||||
|
}, |
||||
|
scene: flipbird, |
||||
|
}); |
||||
|
Object.assign(game, { |
||||
|
onLose, |
||||
|
restart() { |
||||
|
this.scene.scenes[0].scene.start("flipbird"); |
||||
|
}, |
||||
|
}); |
||||
|
return game; |
||||
|
} |
||||
|
|
||||
|
export default launch; |
||||
|
export { launch }; |
||||
@ -0,0 +1,17 @@ |
|||||
|
import { Scene } from "phaser"; |
||||
|
|
||||
|
export default class BootScene extends Scene { |
||||
|
constructor() { |
||||
|
super({ key: "BootScene" }); |
||||
|
} |
||||
|
|
||||
|
preload() { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
create() { |
||||
|
//打开flipbird
|
||||
|
this.scene.start("startFlipBird"); |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
import { Scene } from "phaser"; |
||||
|
import bg from "@/game/game/assets/flipbird/bg.png"; |
||||
|
|
||||
|
import gameoverbg from "@/game/game/assets/flipbird/gameoverbg.png"; |
||||
|
import gameover from "@/game/game/assets/flipbird/gameover.png"; |
||||
|
|
||||
|
export default class gameOverScene extends Scene { |
||||
|
constructor() { |
||||
|
super({ key: "flipBirdGameOver" }); |
||||
|
this.score = ""; |
||||
|
this.topScore = ""; |
||||
|
} |
||||
|
|
||||
|
init(data) { |
||||
|
//方法1. 引入sceneA 在初始化的时候就可以获得场景Scene传递的值;
|
||||
|
this.score = data.score; |
||||
|
this.topScore = data.topScore; |
||||
|
} |
||||
|
preload() { |
||||
|
this.load.image("bg", bg); |
||||
|
this.load.image("gameoverbg", gameoverbg); |
||||
|
this.load.image("gameover", gameover); |
||||
|
} |
||||
|
|
||||
|
create() { |
||||
|
this.bg = this.add.image(0, 0, "bg"); |
||||
|
this.bg.setScale(3.1); |
||||
|
this.gameoverbg = this.add.image( |
||||
|
this.game.config.width / 2, |
||||
|
this.game.config.height / 2, |
||||
|
"gameoverbg" |
||||
|
); |
||||
|
this.gameover = this.add.image( |
||||
|
this.game.config.width / 2, |
||||
|
this.game.config.height / 2 - 80, |
||||
|
"gameover" |
||||
|
); |
||||
|
this.scoreText = this.add.text( |
||||
|
this.game.config.width / 2, |
||||
|
this.game.config.height / 2 + 80, |
||||
|
"", |
||||
|
{ fontSize: "55px", fill: "black", fontWeight: 800 } |
||||
|
); |
||||
|
this.scoreText.setOrigin(0.5); |
||||
|
this.scoreText.text = "Score: " + this.score + "\nBest: " + this.topScore; |
||||
|
this.bg.setInteractive(); |
||||
|
this.bg.on("pointerdown", () => { |
||||
|
this.scene.start("flipbird"); |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,165 @@ |
|||||
|
import Phaser from "phaser"; |
||||
|
import pipeImg from "@/game/game/assets/flipbird/pipe.png"; |
||||
|
import birdImg from "@/game/game/assets/flipbird/clumsy.png"; |
||||
|
import bg from "@/game/game/assets/flipbird/bg.png"; |
||||
|
import ground from "@/game/game/assets/flipbird/ground.png"; |
||||
|
/** |
||||
|
* 素材再找 https://github.com/ellisonleao/clumsy-bird/blob/master/data/img/new.png
|
||||
|
* https://www.emanueleferonato.com/2019/05/02/flappy-bird-html5-prototype-updated-to-phaser-3-16-2/
|
||||
|
* |
||||
|
*/ |
||||
|
export default class PlayScene extends Phaser.Scene { |
||||
|
constructor() { |
||||
|
super({ key: "flipbird" }); |
||||
|
this.bg = null; |
||||
|
this.pipeGroup = null; |
||||
|
this.gameOptions = { |
||||
|
// bird gravity, will make bird fall if you dont flap
|
||||
|
birdGravity: 800, |
||||
|
|
||||
|
// horizontal bird speed
|
||||
|
birdSpeed: 120, //125
|
||||
|
|
||||
|
// flap thrust
|
||||
|
birdFlapPower: 300, |
||||
|
|
||||
|
// minimum pipe height, in pixels. Affects hole position
|
||||
|
minPipeHeight: 50, |
||||
|
|
||||
|
// distance range from next pipe, in pixels
|
||||
|
pipeDistance: [220, 280], |
||||
|
|
||||
|
// hole range between pipes, in pixels
|
||||
|
pipeHole: [200, 250], //[100, 130],
|
||||
|
|
||||
|
// local storage object name
|
||||
|
localStorageName: "bestFlappyScore", |
||||
|
}; |
||||
|
} |
||||
|
preload() { |
||||
|
//this.load.image("bird", "bird.png");
|
||||
|
this.load.image("bg", bg); |
||||
|
this.load.image("pipe", pipeImg); |
||||
|
this.load.image("ground", ground); |
||||
|
|
||||
|
this.load.spritesheet("bird", birdImg, { |
||||
|
frameWidth: 85, |
||||
|
frameHeight: 60, |
||||
|
}); |
||||
|
} |
||||
|
create() { |
||||
|
this.bg = this.add.image(0, 0, "bg"); |
||||
|
this.bg.setScale(1); |
||||
|
this.pipeGroup = this.physics.add.group(); |
||||
|
this.pipePool = []; |
||||
|
for (let i = 0; i < 4; i++) { |
||||
|
this.pipePool.push(this.pipeGroup.create(0, 0, "pipe")); |
||||
|
this.pipePool.push(this.pipeGroup.create(0, 0, "pipe")); |
||||
|
this.placePipes(false); |
||||
|
} |
||||
|
this.pipeGroup.setVelocityX(-this.gameOptions.birdSpeed); |
||||
|
this.bird = this.physics.add.sprite( |
||||
|
80, |
||||
|
this.game.config.height / 2, |
||||
|
"bird" |
||||
|
); |
||||
|
this.ground = this.add.tileSprite( |
||||
|
0, |
||||
|
this.game.config.height, |
||||
|
this.game.config.width, |
||||
|
0, |
||||
|
"ground" |
||||
|
); |
||||
|
this.ground.setOrigin(0); |
||||
|
|
||||
|
this.bird.body.gravity.y = this.gameOptions.birdGravity; |
||||
|
|
||||
|
this.anims.create({ |
||||
|
key: "fly", |
||||
|
frames: this.anims.generateFrameNumbers("bird", { start: 0, end: 3 }), |
||||
|
frameRate: 10, |
||||
|
repeat: -1, |
||||
|
}); |
||||
|
this.input.on("pointerdown", this.flap, this); |
||||
|
this.score = 0; |
||||
|
this.scoreText1 = this.add.text(20, 20, "", { |
||||
|
fontSize: 20, |
||||
|
}); |
||||
|
this.updateScore(this.score); |
||||
|
} |
||||
|
updateScore(inc) { |
||||
|
this.score += inc; |
||||
|
this.scoreText1.text = "Game Score: " + this.score; |
||||
|
} |
||||
|
placePipes(addScore) { |
||||
|
let rightmost = this.getRightmostPipe(); |
||||
|
let pipeHoleHeight = Phaser.Math.Between( |
||||
|
this.gameOptions.pipeHole[0], |
||||
|
this.gameOptions.pipeHole[1] |
||||
|
); |
||||
|
let pipeHolePosition = Phaser.Math.Between( |
||||
|
this.gameOptions.minPipeHeight + pipeHoleHeight / 2, |
||||
|
this.game.config.height - |
||||
|
this.gameOptions.minPipeHeight - |
||||
|
pipeHoleHeight / 2 |
||||
|
); |
||||
|
this.pipePool[0].x = |
||||
|
rightmost + |
||||
|
this.pipePool[0].getBounds().width + |
||||
|
Phaser.Math.Between( |
||||
|
this.gameOptions.pipeDistance[0], |
||||
|
this.gameOptions.pipeDistance[1] |
||||
|
); |
||||
|
this.pipePool[0].y = pipeHolePosition - pipeHoleHeight / 2; |
||||
|
this.pipePool[0].setOrigin(0, 1); |
||||
|
this.pipePool[1].x = this.pipePool[0].x; |
||||
|
this.pipePool[1].y = pipeHolePosition + pipeHoleHeight / 2; |
||||
|
this.pipePool[1].setOrigin(0, 0); |
||||
|
this.pipePool[1].flipY = true; |
||||
|
this.pipePool = []; |
||||
|
if (addScore) { |
||||
|
this.updateScore(1); |
||||
|
} |
||||
|
} |
||||
|
flap() { |
||||
|
this.bird.body.velocity.y = -this.gameOptions.birdFlapPower; |
||||
|
this.bird.anims.play("fly", true); |
||||
|
} |
||||
|
getRightmostPipe() { |
||||
|
let rightmostPipe = 0; |
||||
|
this.pipeGroup.getChildren().forEach(function (pipe) { |
||||
|
rightmostPipe = Math.max(rightmostPipe, pipe.x); |
||||
|
}); |
||||
|
return rightmostPipe; |
||||
|
} |
||||
|
update() { |
||||
|
this.physics.world.collide( |
||||
|
this.bird, |
||||
|
this.pipeGroup, |
||||
|
function () { |
||||
|
this.die(); |
||||
|
}, |
||||
|
null, |
||||
|
this |
||||
|
); |
||||
|
if (this.bird.y > this.game.config.height || this.bird.y < 0) { |
||||
|
this.die(); |
||||
|
} |
||||
|
this.pipeGroup.getChildren().forEach(function (pipe) { |
||||
|
if (pipe.getBounds().right < 0) { |
||||
|
this.pipePool.push(pipe); |
||||
|
if (this.pipePool.length == 2) { |
||||
|
this.placePipes(true); |
||||
|
} |
||||
|
} |
||||
|
}, this); |
||||
|
this.ground.tilePositionX += 2; |
||||
|
// this.bg.tilePositionY += 0.5
|
||||
|
} |
||||
|
die() { |
||||
|
if (this.game.onLose) { |
||||
|
this.game.onLose(); |
||||
|
this.scene.pause(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
import { Scene } from "phaser"; |
||||
|
import bg from "@/game/game/assets/flipbird/bg.png"; |
||||
|
|
||||
|
import birdImg from "@/game/game/assets/flipbird/clumsy.png"; |
||||
|
import startBtn from "@/game/game/assets/flipbird/start.png"; |
||||
|
|
||||
|
export default class startScene extends Scene { |
||||
|
constructor() { |
||||
|
super({ key: "startFlipBird" }); |
||||
|
} |
||||
|
|
||||
|
preload() { |
||||
|
this.load.image("bg", bg); |
||||
|
this.load.image("startBtn", startBtn); |
||||
|
this.load.spritesheet("bird", birdImg, { |
||||
|
frameWidth: 85, |
||||
|
frameHeight: 60 |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
create() { |
||||
|
this.bg = this.add.image(0, 0, "bg"); |
||||
|
this.startBtn = this.add.image( |
||||
|
this.game.config.width / 2, |
||||
|
this.game.config.height / 2 + 100, |
||||
|
"startBtn" |
||||
|
); |
||||
|
this.startBtn.setScale(0.5); |
||||
|
this.bg.setScale(3.1); |
||||
|
this.bird = this.physics.add.sprite( |
||||
|
this.game.config.width / 2, |
||||
|
this.game.config.height / 2, |
||||
|
"bird" |
||||
|
); |
||||
|
// this.tip = this.add.text(
|
||||
|
// this.game.config.width / 2,
|
||||
|
// this.game.config.height / 2 - 100,
|
||||
|
// "点击屏幕开始游戏",
|
||||
|
// {
|
||||
|
// fontFamily: 'Georgia, "Goudy Bookletter 1911", Times, serif',
|
||||
|
// fontSize: "30px",
|
||||
|
// color: "#000",
|
||||
|
// align: "center"
|
||||
|
// }
|
||||
|
// );
|
||||
|
this.startBtn.setInteractive(); |
||||
|
this.startBtn.on("pointerdown", () =>{ |
||||
|
this.scene.start("flipbird"); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
<font> |
||||
|
<info face="font" size="72" bold="0" italic="0" charset="" unicode="" stretchH="100" smooth="1" aa="1" padding="2,2,2,2" spacing="0,0" outline="0"/> |
||||
|
<common lineHeight="81" base="46" scaleW="90" scaleH="253" pages="1" packed="0"/> |
||||
|
<pages> |
||||
|
<page id="0" file="font.png"/> |
||||
|
</pages> |
||||
|
<chars count="10"> |
||||
|
<char id="48" x="2" y="2" width="49" height="48" xoffset="3" yoffset="0" xadvance="51" page="0" chnl="15"/> |
||||
|
<char id="49" x="2" y="52" width="16" height="48" xoffset="3" yoffset="0" xadvance="18" page="0" chnl="15"/> |
||||
|
<char id="50" x="2" y="102" width="33" height="48" xoffset="3" yoffset="0" xadvance="35" page="0" chnl="15"/> |
||||
|
<char id="51" x="2" y="152" width="32" height="49" xoffset="3" yoffset="0" xadvance="34" page="0" chnl="15"/> |
||||
|
<char id="52" x="20" y="52" width="32" height="48" xoffset="2" yoffset="0" xadvance="33" page="0" chnl="15"/> |
||||
|
<char id="53" x="53" y="2" width="34" height="48" xoffset="3" yoffset="0" xadvance="36" page="0" chnl="15"/> |
||||
|
<char id="54" x="2" y="203" width="34" height="48" xoffset="2" yoffset="0" xadvance="35" page="0" chnl="15"/> |
||||
|
<char id="55" x="36" y="152" width="29" height="48" xoffset="2" yoffset="0" xadvance="30" page="0" chnl="15"/> |
||||
|
<char id="56" x="37" y="102" width="32" height="48" xoffset="3" yoffset="0" xadvance="34" page="0" chnl="15"/> |
||||
|
<char id="57" x="54" y="52" width="34" height="48" xoffset="2" yoffset="0" xadvance="35" page="0" chnl="15"/> |
||||
|
<char id="32" x="0" y="0" width="0" height="0" xoffset="2" yoffset="0" xadvance="11" page="0" chnl="15"/> |
||||
|
</chars> |
||||
|
</font> |
||||
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 476 B |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 45 KiB |
@ -0,0 +1,6 @@ |
|||||
|
export const ROW = 1; //0
|
||||
|
export const COL = 1; |
||||
|
export const TILE_SIZE = 125; //80
|
||||
|
export const TILE_SPACING = 10; //8
|
||||
|
export const TWEEN_DURATION = 100; |
||||
|
export const LOCAL_STORAGE_NAME = "L2048"; |
||||
@ -0,0 +1,34 @@ |
|||||
|
import Phaser from "phaser"; |
||||
|
import PlayScene from "./scenes/PlayScene"; |
||||
|
import { TILE_SIZE, TILE_SPACING } from "./constant"; |
||||
|
|
||||
|
function launch({ containerId, onLose }) { |
||||
|
const container = document.getElementById(containerId); |
||||
|
const width = container.clientWidth; |
||||
|
const height = container.clientHeight; |
||||
|
const game = new Phaser.Game({ |
||||
|
type: Phaser.AUTO, |
||||
|
width, |
||||
|
height, |
||||
|
parent: containerId, |
||||
|
backgroundColor: 0xbbada0, |
||||
|
physics: { |
||||
|
default: "arcade", |
||||
|
arcade: { |
||||
|
gravity: { y: 300 }, |
||||
|
debug: true, |
||||
|
}, |
||||
|
}, |
||||
|
scene: PlayScene, |
||||
|
}); |
||||
|
Object.assign(game, { |
||||
|
onLose, |
||||
|
restart() { |
||||
|
this.scene.scenes[0].scene.start("PlayScene"); |
||||
|
}, |
||||
|
}); |
||||
|
return game; |
||||
|
} |
||||
|
|
||||
|
export default launch; |
||||
|
export { launch }; |
||||
@ -0,0 +1,520 @@ |
|||||
|
import Phaser from "phaser"; |
||||
|
|
||||
|
import tile_default from "@/game/game2048/assets/sprites/tile_default.png"; |
||||
|
import restart from "@/game/game2048/assets/sprites/restart.png"; |
||||
|
import score_img from "@/game/game2048/assets/sprites/score.png"; |
||||
|
import score_best from "@/game/game2048/assets/sprites/score_best.png"; |
||||
|
import tiles from "@/game/game2048/assets/sprites/tiles125.png"; |
||||
|
|
||||
|
import move_mp3 from "@/game/game2048/assets/sounds/move.mp3"; |
||||
|
import move_ogg from "@/game/game2048/assets/sounds/move.ogg"; |
||||
|
import grow_mp3 from "@/game/game2048/assets/sounds/grow.mp3"; |
||||
|
import grow_ogg from "@/game/game2048/assets/sounds/grow.ogg"; |
||||
|
|
||||
|
import { |
||||
|
COL, |
||||
|
ROW, |
||||
|
TILE_SIZE, |
||||
|
TILE_SPACING, |
||||
|
TWEEN_DURATION, |
||||
|
LOCAL_STORAGE_NAME, |
||||
|
} from "../constant"; |
||||
|
|
||||
|
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("restart", restart); |
||||
|
this.load.image("score", score_img); |
||||
|
this.load.image("score_best", score_best); |
||||
|
|
||||
|
this.load.spritesheet("tiles", tiles, { |
||||
|
frameWidth: TILE_SIZE, |
||||
|
frameHeight: TILE_SIZE, |
||||
|
}); |
||||
|
|
||||
|
this.load.audio("move", [move_mp3, move_ogg]); |
||||
|
this.load.audio("grow", [grow_mp3, grow_ogg]); |
||||
|
} |
||||
|
|
||||
|
create() { |
||||
|
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; |
||||
|
} |
||||
|
console.log("向右"); |
||||
|
this.move(0, 1); |
||||
|
} |
||||
|
|
||||
|
if (swipeNormal.x < -0.8) { |
||||
|
for (var i = 0; i < children.length; i++) { |
||||
|
// 越向右层级越高
|
||||
|
children[i].depth = children[i].x; |
||||
|
} |
||||
|
console.log("向左"); |
||||
|
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; |
||||
|
} |
||||
|
console.log("向下"); |
||||
|
this.move(1, 0); |
||||
|
} |
||||
|
|
||||
|
if (swipeNormal.y < -0.8) { |
||||
|
for (var i = 0; i < children.length; i++) { |
||||
|
// 越下面层级越高
|
||||
|
children[i].depth = children[i].y; |
||||
|
} |
||||
|
console.log("向上"); |
||||
|
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; |
||||
|
} |
||||
|
console.log("向左"); |
||||
|
|
||||
|
// 行为 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; |
||||
|
} |
||||
|
console.log("向右"); |
||||
|
|
||||
|
// 行为 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; |
||||
|
} |
||||
|
console.log("向上"); |
||||
|
|
||||
|
// 列为 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; |
||||
|
} |
||||
|
console.log("向下"); |
||||
|
|
||||
|
// 列为 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.setText(this.score); |
||||
|
this.resetTiles(); |
||||
|
this.addTile(); |
||||
|
} |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
transformTile(tile, row, col) { |
||||
|
this.growSound.play(); |
||||
|
this.movingTiles++; |
||||
|
tile.tileSprite.setFrame(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.setText(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( |
||||
|
this.tileArray[i][j].tileValue - 1 |
||||
|
); |
||||
|
} else { |
||||
|
this.tileArray[i][j].tileSprite.alpha = 0; |
||||
|
this.tileArray[i][j].tileSprite.visible = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 布局
|
||||
|
layout() { |
||||
|
// 头部
|
||||
|
this.layout_header(); |
||||
|
|
||||
|
// 4 * 4 方块
|
||||
|
this.layout_body(); |
||||
|
} |
||||
|
//设置头部
|
||||
|
layout_header() { |
||||
|
// 添加分数背景
|
||||
|
this.add.sprite( |
||||
|
this.setPosition(0, COL) + 30, |
||||
|
this.setPosition(0, ROW) - 115, |
||||
|
"score" |
||||
|
); |
||||
|
|
||||
|
// 添加最高分数背景
|
||||
|
this.add.sprite( |
||||
|
this.setPosition(1, COL) + 40, |
||||
|
this.setPosition(0, ROW) - 115, |
||||
|
"score_best" |
||||
|
); |
||||
|
|
||||
|
// 重新开始游戏
|
||||
|
let restartButton = this.add.sprite( |
||||
|
this.setPosition(3, COL) - 10, |
||||
|
this.setPosition(0, ROW) - 110, |
||||
|
"restart" |
||||
|
); |
||||
|
restartButton.setInteractive(); |
||||
|
restartButton.on("pointerdown", () => { |
||||
|
this.scene.start("PlayScene"); |
||||
|
}); |
||||
|
|
||||
|
// 分数
|
||||
|
this.scoreText = this.add |
||||
|
.text( |
||||
|
this.setPosition(0, COL) + 30, |
||||
|
this.setPosition(0, ROW) - 105, |
||||
|
"0", |
||||
|
{ |
||||
|
fontFamily: "Arial", |
||||
|
fontSize: 22, |
||||
|
fill: "#ffffff", |
||||
|
} |
||||
|
) |
||||
|
.setOrigin(0.5); |
||||
|
} |
||||
|
//设置底部布局
|
||||
|
layout_body() { |
||||
|
this.tileGroup = this.add.group(); |
||||
|
|
||||
|
for (let i = 0; i < 4; i++) { |
||||
|
this.tileArray[i] = []; |
||||
|
for (let j = 0; j < 4; j++) { |
||||
|
this.add |
||||
|
.sprite( |
||||
|
this.setPosition(j, COL), |
||||
|
this.setPosition(i, ROW), |
||||
|
"tile_default" |
||||
|
) |
||||
|
.setScale(1.5625); |
||||
|
|
||||
|
let tile = this.add.sprite( |
||||
|
this.setPosition(j, COL), |
||||
|
this.setPosition(i, ROW), |
||||
|
"tiles" |
||||
|
); |
||||
|
|
||||
|
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 ? 100 : 0; |
||||
|
return ( |
||||
|
pos * (TILE_SIZE + TILE_SPACING) + TILE_SIZE * 0.5 + TILE_SPACING + 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(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; |
||||