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