Классическая игра-пятнашки на JavaScript и canvas.
HTML-разметка
Разметка самая простая — только один элемент canvas
с одноименным идентификатором.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
canvas { border:2px solid #333; }
</style>
<script src="script.js"></script>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>
Класс Game
Все действия будут происходить в файле script.js
, где мы напишем код нашей игры.
Начнем с того, что создадим класс Game
, который будет представлять собой Пятнашки в целом. Он будет получать в качестве параметров контекст рисования и размер одной пятнашки.
Для справки: ООП в JavaScript
Конструктор
function Game(context, cellSize){
this.state = [
[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,0]
];
this.color = "#FFB93B";
this.context = context;
this.cellSize = cellSize;
this.clicks = 0;
}
Game.prototype.getClicks = function() {
return this.clicks;
};
В конструкторе определяем:
- начальное состояние поля (
this.state
); - количество кликов, за которое пользователь прошел игру (
this.clicks
); - цвет заливки пятнашек (
this.color
).
Метод getClicks
возвращает актуальное значение поля clicks
. Он нам пригодится в конце игры, при выигрыше.
Отрисовка
Метод cellView
получает координаты и рисует в них квадрат пятнашки заданного размера и цвета.
Game.prototype.cellView = function(x, y) {
this.context.fillStyle = this.color;
this.context.fillRect(
x + 1,
y + 1,
this.cellSize - 2,
this.cellSize - 2
);
};
Метод numView
устанавливает стили для текста на пятнашках:
Game.prototype.numView = function() {
this.context.font = "bold " + (this.cellSize/2) + "px Sans";
this.context.textAlign = "center";
this.context.textBaseline = "middle";
this.context.fillStyle = "#222";
};
Метод draw
отрисовывает всю игру:
Game.prototype.draw = function() {
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
if (this.state[i][j] > 0) {
this.cellView(
j * this.cellSize,
i * this.cellSize
);
this.numView();
this.context.fillText(
this.state[i][j],
j * this.cellSize + this.cellSize / 2,
i * this.cellSize + this.cellSize / 2
);
}
}
}
};
Он проходится по всем клеткам игрового поля, получает значение каждой клетки из this.state
, для каждой непустой клетки рисует квадрат (this.cellView
), затем устанавливает стили для текста и рисует соответствующую цифру.
Перемещение
При клике на пятнашку будет вызван метод move
, который должен переместить ее на пустую клетку.
Game.prototype.getNullCell = function(){
for (let i = 0; i<4; i++){
for (let j=0; j<4; j++){
if(this.state[j][i] === 0){
return {x: i, y: j};
}
}
}
};
Game.prototype.move = function(x, y) {
let nullCell = this.getNullCell();
let canMoveVertical = (x - 1 == nullCell.x || x + 1 == nullCell.x) && y == nullCell.y;
let canMoveHorizontal = (y - 1 == nullCell.y || y + 1 == nullCell.y) && x == nullCell.x;
if (canMoveVertical || canMoveHorizontal) {
this.state[nullCell.y][nullCell.x] = this.state[y][x];
this.state[y][x] = 0;
this.clicks++;
}
};
Вспомогательный метод getNullCell
возвращает позицию пустой клетки на поле.
Метод move
сначала проверяет, находится ли клетка по указанным координатам рядом с пустой клеткой, то есть можно ли ее сдвинуть. Если можно, то он меняет местами две клетки и увеличивает количество ходов.
Проверка результата
Метод victory
проверяет, сложены ли пятнашки правильно:
Game.prototype.victory = function() {
let combination = [[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,0]];
let res = true;
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
if (combination[i][j] != this.state[i][j]) {
res = false;
break;
}
}
}
return res;
};
Перемешивание поля
function getRandomBool() {
if (Math.floor(Math.random() * 2) === 0) {
return true;
}
}
Game.prototype.mix = function(count) {
let x, y;
for (let i = 0; i < count; i++) {
let nullCell = this.getNullCell();
let verticalMove = getRandomBool();
let upLeft = getRandomBool();
if (verticalMove) {
x = nullCell.x;
if (upLeft) {
y = nullCell.y - 1;
} else {
y = nullCell.y + 1;
}
} else {
y = nullCell.y;
if (upLeft) {
x = nullCell.x - 1;
} else {
x = nullCell.x + 1;
}
}
if (0 <= x && x <= 3 && 0 <= y && y <= 3) {
this.move(x, y);
}
}
this.clicks = 0;
};
Метод mix
перемешивает пятнашки заданное количество раз. Он случайным образом выбирает клетку рядом с пустой и меняет их местами. Для выбора клетки используется вспомогательная функция getRandomBool
.
Инициализация игры
Как только страница полностью загрузится, мы найдем на странице холст, создадим контекст рисования и инициализируем нашу игру.
window.onload = function(){
let canvas = document.getElementById("canvas");
canvas.width = 320;
canvas.height = 320;
let context = canvas.getContext("2d");
context.fillRect(0, 0, canvas.width, canvas.height);
let cellSize = canvas.width / 4;
let game = new Game(context, cellSize);
game.mix(300);
game.draw();
}
Обработка кликов
Осталось только добавить обработку события клика по пятнашке. Кроме клика мы будем поддерживать и событие касания, поэтому общую логику вынесем в отдельную функцию onEvent
, которая будет получать координаты выбранной пятнашки:
canvas.onclick = function(e) {
let x = (e.pageX - canvas.offsetLeft) / cellSize | 0;
let y = (e.pageY - canvas.offsetTop) / cellSize | 0;
onEvent(x, y);
};
canvas.ontouchend = function(e) {
let x = (e.touches[0].pageX - canvas.offsetLeft) / cellSize | 0;
let y = (e.touches[0].pageY - canvas.offsetTop) / cellSize | 0;
onEvent(x, y);
};
function onEvent(x, y) {
game.move(x, y);
context.fillRect(0, 0, canvas.width, canvas.height);
game.draw();
if (game.victory()) {
alert("Собрано за "+game.getClicks()+" касание!");
game.mix(300);
context.fillRect(0, 0, canvas.width, canvas.height);
game.draw(context, cellSize);
}
}
Оператор побитового ИЛИ (|) здесь используется для округления полученного числа.
После выполнения хода, проверяем, не собраны ли пятнашки. Если собраны, то выводим сообщение.
Пятнашки готовы:
See the Pen Fifteen game by FurryCat (@mohnatus-the-lessful) on CodePen.
5 комментариев