17 changed files with 254 additions and 21 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,236 @@ |
|||||
|
import Phaser from "phaser"; |
||||
|
import bg from "./bg.jpg"; |
||||
|
import puzzle from "./puzzle.png"; |
||||
|
import countdown from "./countdown.png"; |
||||
|
import "../../font.css"; |
||||
|
|
||||
|
const PUZZLE_WH = 1640; |
||||
|
const PIECE_WH = 410; |
||||
|
const OFFSET_LEFT = 260 + PIECE_WH / 2; |
||||
|
const OFFSET_TOP = 390 + PIECE_WH / 2; |
||||
|
|
||||
|
const surroundingPieces = (z, w, h) => { |
||||
|
const x = z % w; |
||||
|
const y = Math.floor(z / h); |
||||
|
|
||||
|
const left = x === 0 ? null : z - 1; |
||||
|
const up = y === 0 ? null : z - w; |
||||
|
const right = x === w - 1 ? null : z + 1; |
||||
|
const down = y === h - 1 ? null : z + w; |
||||
|
|
||||
|
return [left, up, right, down].filter((i) => i !== null); |
||||
|
}; |
||||
|
const shuffle = (puzzle, moves, width, height) => { |
||||
|
let empty = puzzle[0]; |
||||
|
let lastPiece = empty; |
||||
|
|
||||
|
for (let i = 0; i < moves; i++) { |
||||
|
let pieces = surroundingPieces(empty, width, height); |
||||
|
pieces = pieces.filter((i) => i !== lastPiece); |
||||
|
|
||||
|
let pieceIndex = pieces[Math.floor(Math.random() * pieces.length)]; |
||||
|
|
||||
|
puzzle[empty] = puzzle[pieceIndex]; |
||||
|
lastPiece = empty; |
||||
|
|
||||
|
puzzle[pieceIndex] = 0; |
||||
|
empty = pieceIndex; |
||||
|
} |
||||
|
|
||||
|
return puzzle; |
||||
|
}; |
||||
|
|
||||
|
const createShuffledIndexArray = (piecesAmount) => { |
||||
|
const indexArray = new Array(piecesAmount).fill(0).map((_, i) => i); |
||||
|
return shuffle(indexArray, 25, 4, 4); |
||||
|
}; |
||||
|
|
||||
|
class Scene extends Phaser.Scene { |
||||
|
constructor() { |
||||
|
super("scene"); |
||||
|
} |
||||
|
preload() { |
||||
|
this.load.image("bg", bg); |
||||
|
this.load.image("countdown", countdown); |
||||
|
this.load.spritesheet("puzzle", puzzle, { |
||||
|
frameWidth: PIECE_WH, |
||||
|
frameHeight: PIECE_WH, |
||||
|
}); |
||||
|
} |
||||
|
create() { |
||||
|
this.won = false; |
||||
|
const bg = this.add.image( |
||||
|
this.game.config.width / 2, |
||||
|
this.game.config.height / 2, |
||||
|
"bg" |
||||
|
); |
||||
|
bg.displayWidth = this.game.config.width; |
||||
|
bg.displayHeight = this.game.config.height; |
||||
|
const countdown = this.add.image(465, 474, "countdown"); |
||||
|
countdown.setDepth(1); |
||||
|
this.seconds = 300; |
||||
|
this.secText = this.add.text(520, 450, this.seconds, { |
||||
|
fontSize: 45, |
||||
|
fontFamily: "game", |
||||
|
color: "#429b47", |
||||
|
}); |
||||
|
this.secText.setDepth(2); |
||||
|
|
||||
|
const BOARD_COLS = Math.floor(PUZZLE_WH / PIECE_WH); |
||||
|
const BOARD_ROWS = Math.floor(PUZZLE_WH / PIECE_WH); |
||||
|
|
||||
|
let piecesAmount = BOARD_COLS * BOARD_ROWS; |
||||
|
this.time.addEvent({ |
||||
|
delay: 1000, |
||||
|
loop: true, |
||||
|
callback: () => { |
||||
|
if (this.seconds === 0) this.game.onLose && this.game.onLose(); |
||||
|
if (!this.won && this.seconds > 0) this.seconds--; |
||||
|
this.secText.setText(this.seconds); |
||||
|
}, |
||||
|
}); |
||||
|
const shuffledIndexArray = createShuffledIndexArray(piecesAmount); |
||||
|
let piecesIndex = 0; |
||||
|
let piecesGroup = this.add.group(); |
||||
|
|
||||
|
this.piecesGroup = piecesGroup; |
||||
|
for (let i = 0; i < BOARD_ROWS; i++) { |
||||
|
for (let j = 0; j < BOARD_COLS; j++) { |
||||
|
let piece; |
||||
|
if (shuffledIndexArray[piecesIndex]) { |
||||
|
piece = piecesGroup.create( |
||||
|
OFFSET_LEFT + j * PIECE_WH, |
||||
|
OFFSET_TOP + i * PIECE_WH, |
||||
|
"puzzle", |
||||
|
shuffledIndexArray[piecesIndex] |
||||
|
); |
||||
|
} else { |
||||
|
//initial position of black piece
|
||||
|
piece = piecesGroup.create( |
||||
|
OFFSET_LEFT + j * PIECE_WH, |
||||
|
OFFSET_TOP + i * PIECE_WH, |
||||
|
"puzzle", |
||||
|
shuffledIndexArray[piecesIndex], |
||||
|
false |
||||
|
); |
||||
|
piece.black = true; |
||||
|
this.blackPiece = piece; |
||||
|
} |
||||
|
piece.name = "piece" + i.toString() + "x" + j.toString(); |
||||
|
piece.currentIndex = piecesIndex; |
||||
|
piece.destIndex = shuffledIndexArray[piecesIndex]; |
||||
|
piece.inputEnabled = true; |
||||
|
// piece.events.onInputDown.add();
|
||||
|
piece.posX = j; |
||||
|
piece.posY = i; |
||||
|
piece.setInteractive(); |
||||
|
piecesIndex++; |
||||
|
} |
||||
|
} |
||||
|
this.input.on( |
||||
|
"gameobjectdown", |
||||
|
(_, piece) => { |
||||
|
var blackPiece = this.canMove(piece); |
||||
|
if (blackPiece) { |
||||
|
this.movePiece(piece, blackPiece); |
||||
|
} |
||||
|
}, |
||||
|
this |
||||
|
); |
||||
|
} |
||||
|
canMove(piece) { |
||||
|
let foundBlackElem = false; |
||||
|
|
||||
|
this.piecesGroup.getChildren().forEach(function (element) { |
||||
|
if ( |
||||
|
(element.posX === piece.posX - 1 && |
||||
|
element.posY === piece.posY && |
||||
|
element.black) || |
||||
|
(element.posX === piece.posX + 1 && |
||||
|
element.posY === piece.posY && |
||||
|
element.black) || |
||||
|
(element.posY === piece.posY - 1 && |
||||
|
element.posX === piece.posX && |
||||
|
element.black) || |
||||
|
(element.posY === piece.posY + 1 && |
||||
|
element.posX === piece.posX && |
||||
|
element.black) |
||||
|
) { |
||||
|
foundBlackElem = element; |
||||
|
return; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return foundBlackElem; |
||||
|
} |
||||
|
movePiece(piece, blackPiece) { |
||||
|
var tmpPiece = { |
||||
|
posX: piece.posX, |
||||
|
posY: piece.posY, |
||||
|
currentIndex: piece.currentIndex, |
||||
|
}; |
||||
|
this.tweens.add({ |
||||
|
targets: piece, |
||||
|
x: OFFSET_LEFT + blackPiece.posX * PIECE_WH, |
||||
|
y: OFFSET_TOP + blackPiece.posY * PIECE_WH, |
||||
|
ease: "linear", |
||||
|
duration: 300, |
||||
|
}); |
||||
|
|
||||
|
piece.posX = blackPiece.posX; |
||||
|
piece.posY = blackPiece.posY; |
||||
|
piece.currentIndex = blackPiece.currentIndex; |
||||
|
piece.name = "piece" + piece.posX.toString() + "x" + piece.posY.toString(); |
||||
|
|
||||
|
blackPiece.posX = tmpPiece.posX; |
||||
|
blackPiece.posY = tmpPiece.posY; |
||||
|
blackPiece.currentIndex = tmpPiece.currentIndex; |
||||
|
blackPiece.name = |
||||
|
"piece" + blackPiece.posX.toString() + "x" + blackPiece.posY.toString(); |
||||
|
|
||||
|
this.checkIfFinished(); |
||||
|
} |
||||
|
checkIfFinished() { |
||||
|
let isFinished = true; |
||||
|
|
||||
|
this.piecesGroup.getChildren().forEach((element) => { |
||||
|
if (element.currentIndex !== element.destIndex) { |
||||
|
isFinished = false; |
||||
|
return; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
if (isFinished) { |
||||
|
this.won = true; |
||||
|
this.blackPiece.visible = true; |
||||
|
this.game.onWon && this.game.onWon(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
export default class Game extends Phaser.Game { |
||||
|
constructor({ containerId, onLose, onWon }) { |
||||
|
const container = document.getElementById(containerId); |
||||
|
const containerWH = container.clientWidth; |
||||
|
super({ |
||||
|
type: Phaser.CANVAS, |
||||
|
width: containerWH, |
||||
|
height: containerWH, |
||||
|
parent: containerId, |
||||
|
scale: { |
||||
|
mode: Phaser.Scale.FIT, |
||||
|
autoCenter: Phaser.Scale.CENTER_BOTH, |
||||
|
parent: containerId, |
||||
|
width: 2160, |
||||
|
height: 2160, |
||||
|
}, |
||||
|
scene: Scene, |
||||
|
}); |
||||
|
Object.assign(this, { |
||||
|
onLose, |
||||
|
onWon, |
||||
|
restart() { |
||||
|
this.scene.scenes[0].scene.start("scene"); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 180 KiB |
Loading…
Reference in new issue