Несколько вещей, которые я узнал, делая простую игру-головоломку Sokoban на JavaScript.
Для просмотра ссылки Войдиили Зарегистрируйся и Для просмотра ссылки Войди или Зарегистрируйся-версия .
Игра состоит из стены, играбельного персонажа, блоков и пятен на земле, которые являются местами хранения. Цель игры состоит в том, чтобы запихнуть все блоки во все места хранения. Это может быть непросто, потому что легко оказаться в состоянии, когда блок больше не может быть перемещен, и теперь вам нужно перезапустить игру.
Вот игра которую я сделал:
Оригинальная игра имеет немного лучшую графику:
В моей версии большая синяя точка-это символ, розовые точки-места хранения, а оранжевые блоки - ящики.
Я написал его на лету в течение нескольких часов. Создание маленьких игр сильно отличается от того, что я обычно делаю на работе, поэтому я нашел это забавным и достижимым испытанием. К счастью, с некоторыми предыдущими проектами (Для просмотра ссылки Войдиили Зарегистрируйся и Для просмотра ссылки Войди или Зарегистрируйся) У меня был некоторый опыт работы с концепцией построения координат.
Поехали!
Так что доступ map[0][0]будет y0 x0и map[1][3]будет y1 x3.
Оттуда легко сделать карту, основанную на существующем уровне Сокобана, где каждая координата является сущностью в игре - ландшафт, игрок и т. Д.
Сущности
Карта
С помощью этих данных я могу сопоставить каждую сущность с цветом и отобразить ее на экране на холсте HTML5. Так что теперь у меня есть карта, которая выглядит правильно, но она еще ничего не делает.
Для этого мне просто нужно знать сущности, соседствующие с игроком, и сущности, соседствующие с блоком, если игрок толкает блок. Если игрок толкает несколько блоков, мне придется рекурсивно подсчитывать, сколько их там.
Теперь, когда у вас есть игрок и соседние координаты, каждое действие будет действием перемещения. Если игрок пытается пройти через проходимую ячейку (пустую или пустую), просто переместите игрока. Если игрок пытается толкнуть блок, переместите игрока и блок. Если соседний блок-стена, ничего не делайте.
Используя начальное игровое состояние, вы можете выяснить, что там должно быть. Пока я передаю направление функции, я могу установить новые координаты - добавление или удаление a yбудет вверх и вниз, добавление или удаление an x будет влево или вправо.
Если игрок перемещает блок, я написал небольшую рекурсивную функцию, чтобы проверить, сколько блоков находится в ряду, и как только у него будет это количество, он проверит, что такое соседняя сущность, переместит блок, если это возможно, и переместит игрока, если блок переместился.
Затем, если блок можно переместить, он просто переместит его или переместит и преобразует его в блок успеха, если он находится над хранилищем, а затем переместит игрока.
Функция рендеринга просто отображает каждую координату и создает прямоугольник или круг нужного цвета.
В основном весь рендеринг в HTML canvas состоит из пути для контура (обводки) и пути для внутренней части (заливки). Поскольку один пиксель на координату был бы довольно крошечной игрой, я умножил каждое значение на а multipler, которое 75в данном случае было пикселями.
Надеюсь, вам понравилось читать об этом и вы почувствуете вдохновение создавать свои собственные маленькие игры и проекты.
Для просмотра ссылки Войди
Игра состоит из стены, играбельного персонажа, блоков и пятен на земле, которые являются местами хранения. Цель игры состоит в том, чтобы запихнуть все блоки во все места хранения. Это может быть непросто, потому что легко оказаться в состоянии, когда блок больше не может быть перемещен, и теперь вам нужно перезапустить игру.
Вот игра которую я сделал:
Оригинальная игра имеет немного лучшую графику:
В моей версии большая синяя точка-это символ, розовые точки-места хранения, а оранжевые блоки - ящики.
Я написал его на лету в течение нескольких часов. Создание маленьких игр сильно отличается от того, что я обычно делаю на работе, поэтому я нашел это забавным и достижимым испытанием. К счастью, с некоторыми предыдущими проектами (Для просмотра ссылки Войди
Поехали!
Карта и объекты
Первое, что я сделал, это построил карту, которая представляет собой двумерный массив, где каждая строка соответствует координате y, а каждый столбец-координате x.
JavaScript:
const map = [
['y0 x0', 'y0 x1', 'y0 x2', 'y0 x3'],
['y1 x0', 'y1 x1', 'y1 x2', 'y1 x3'],
// ...etc
]
Оттуда легко сделать карту, основанную на существующем уровне Сокобана, где каждая координата является сущностью в игре - ландшафт, игрок и т. Д.
Сущности
JavaScript:
const EMPTY = 'empty'
const WALL = 'wall'
const BLOCK = 'block'
const SUCCESS_BLOCK = 'success_block'
const VOID = 'void'
const PLAYER = 'player'
Карта
JavaScript:
onst map = [
[EMPTY, EMPTY, WALL, WALL, WALL, WALL, WALL, EMPTY],
[WALL, WALL, WALL, EMPTY, EMPTY, EMPTY, WALL, EMPTY],
[WALL, VOID, PLAYER, BLOCK, EMPTY, EMPTY, WALL, EMPTY],
// ...etc
С помощью этих данных я могу сопоставить каждую сущность с цветом и отобразить ее на экране на холсте HTML5. Так что теперь у меня есть карта, которая выглядит правильно, но она еще ничего не делает.
Логика игры
Есть не так уж много действий, о которых стоит беспокоиться. Игрок может двигаться ортогонально - вверх, вниз, влево и вправо - и есть несколько вещей, чтобы рассмотреть:- PLAYERИ BLOCKне может двигаться через а WALL
- PLAYERИ BLOCKможет перемещаться через EMPTYпространство или VOIDпространство (место хранения).
- Игрок может нажать кнопку a BLOCK
- А BLOCKстановится аSUCCESS_BLOCK, когда оно находится на вершине а VOID.
- А BLOCKможет подтолкнуть все остальные BLOCKфигуры
Для этого мне просто нужно знать сущности, соседствующие с игроком, и сущности, соседствующие с блоком, если игрок толкает блок. Если игрок толкает несколько блоков, мне придется рекурсивно подсчитывать, сколько их там.
Движение
Поэтому первое, что нам нужно сделать в любое время, когда происходит изменение, - это найти текущие координаты игрока и то, какой тип сущности находится выше, ниже, слева и справа от них.
JavaScript:
function findPlayerCoords() {
const y = map.findIndex(row => row.includes(PLAYER))
const x = map[y].indexOf(PLAYER)
return {
x,
y,
above: map[y - 1][x],
below: map[y + 1][x],
sideLeft: map[y][x - 1],
sideRight: map[y][x + 1],
}
}
JavaScript:
function move(playerCoords, direction) {
if (isTraversible(adjacentCell[direction])) {
movePlayer(playerCoords, direction)
}
if (isBlock(adjacentCell[direction])) {
movePlayerAndBlocks(playerCoords, direction)
}
}
JavaScript:
function movePlayer(playerCoords, direction) {
// Replace previous spot with initial board state (void or empty)
map[playerCoords.y][playerCoords.x] = isVoid(levelOneMap[playerCoords.y][playerCoords.x])
? VOID
: EMPTY
// Move player
map[getY(playerCoords.y, direction, 1)][getX(playerCoords.x, direction, 1)] = PLAYER
}
JavaScript:
function countBlocks(blockCount, y, x, direction, board) {
if (isBlock(board[y][x])) {
blockCount++
return countBlocks(blockCount, getY(y, direction), getX(x, direction), direction, board)
} else {
return blockCount
}
}
const blocksInARow = countBlocks(1, newBlockY, newBlockX, direction, map)
JavaScript:
map[newBoxY][newBoxX] = isVoid(levelOneMap[newBoxY][newBoxX]) ? SUCCESS_BLOCK : BLOCK
movePlayer(playerCoords, direction)
Визуализация
Легко отслеживать всю игру в 2D-массиве и выводить обновленную игру на экран с каждым движением. Игровой тик невероятно прост - каждый раз, когда происходит событие keydown для up, down, left, right (или w, a, s, d для интенсивных игроков)move(), будет вызвана функция, которая использует индекс игрока и соседние типы ячеек, чтобы определить, каким должно быть новое, обновленное состояние игры. После изменения render()вызывается функция, которая просто окрашивает всю доску в обновленное состояние.
JavaScript:
const sokoban = new Sokoban()
sokoban.render()
// re-render
document.addEventListener('keydown', event => {
const playerCoords = sokoban.findPlayerCoords()
switch (event.key) {
case keys.up:
case keys.w:
sokoban.move(playerCoords, directions.up)
break
case keys.down:
case keys.s:
sokoban.move(playerCoords, directions.down)
break
case keys.left:
case keys.a:
sokoban.move(playerCoords, directions.left)
break
case keys.right:
case keys.d:
sokoban.move(playerCoords, directions.right)
break
default:
}
sokoban.render()
})
JavaScript:
function render() {
map.forEach((row, y) => {
row.forEach((cell, x) => {
paintCell(context, cell, x, y)
})
})
}
JavaScript:
function paintCell(context, cell, x, y) {
// Create the fill
context.beginPath()
context.rect(x * multiplier + 5, y * multiplier + 5, multiplier - 10, multiplier - 10)
context.fillStyle = colors[cell].fill
context.fill()
// Create the outline
context.beginPath()
context.rect(x * multiplier + 5, y * multiplier + 5, multiplier - 10, multiplier - 10)
context.lineWidth = 10
context.strokeStyle = colors[cell].stroke
context.stroke()
}
Вывод
Это была забавная маленькая игра. Я организовал файлы так:- Для просмотра ссылки Войди
или Зарегистрируйся для сущностных данных, картографических данных, цветов отображения сущностей и ключевых данных. - Для просмотра ссылки Войди
или Зарегистрируйся для проверки того, какой тип объекта существует в определенной координате, и определения того, какие новые координаты должны быть для игрока. - Для просмотра ссылки Войди
или Зарегистрируйся для поддержания состояния игры, логики и визуализации. - Для просмотра ссылки Войди
или Зарегистрируйся для инициализации экземпляра приложения и обработки ключевых событий.
Надеюсь, вам понравилось читать об этом и вы почувствуете вдохновение создавать свои собственные маленькие игры и проекты.