Пишем игру Pac-Man с помощью JavaScript
Размер текста: A+ A-

Пишем игру Pac-Man с помощью JavaScript

Нажмите, чтобы оценить наш труд:
[Всего: 1 Средняя: 5]

Напишем код, который представляет собой простую реализацию игры Pac-Man, написанную на JavaScript с использованием HTML5 Canvas, CSS для генерации страницы.

Давайте опишем его структуру и выделим основные функции с их назначением.

В отличие от прошлых наших примеров, подробно расписывать написание JS кода мы не будем, распишем лишь структуру и основные функции с переменными. Это хорошая практика для начинающих программистов на JavaScript.

  • Приведем ссылку на демонстрацию (не работает с тачсрина мобильных устройств): Игра Pac-Man на JS.

Как работает игра:

  1. Игрок нажимает “Старт”, что запускает игру и отрисовывает изначальное поле.
  2. Pac-Man движется по полю с помощью стрелок, собирая точки и бонусы.
  3. Бонусы в виде белых точек дают суперсилу на 6 секунд, позволяя “есть” призраков.
  4. При столкновении с призраком без суперсилы игра заканчивается с сообщением “Игра окончена! Вы проиграли.”.
  5. Если собраны все точки и бонусы, появляется сообщение “Поздравляем! Вы выиграли!”.

Не реализовано (попробуйте сделать сами для практики):

  1. Если “съедены” все призраки, появляется сообщение “Поздравляем! Вы выиграли!”.
  2. Звук подбора точек
  3. Вывод динамически меняющихся очков за подбор точек.

Если кому нужен исходный код, то вот он (даны некоторые комментарии к блокам):

<!DOCTYPE html>
<html lang="ru">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Игра Pac-Man на JavaScript и PHP – Код с бонусами и призраками</title>
<meta name="description" content="Создайте игру Pac-Man с помощью JavaScript, PHP, HTML и CSS. Простой код с бонусами, призраками и управлением стрелками для одного уровня.">

<style>
body {
background-color: black; /* Добавляем черный фон для всей страницы */
margin: 0; /* Убираем отступы по умолчанию */
}
.game-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
padding: 20px;
}
#gameCanvas {
border: 2px solid black;
background: #000;
}
#startButton {
padding: 10px 20px;
font-size: 18px;
cursor: pointer;
background-color: #ffd700;
border: none;
border-radius: 5px;
}
#startButton:hover {
background-color: #ffeb3b;
}
#gameMessage {
font-size: 20px;
color: #fff;
text-align: center;
}
</style>
</head>

<body>
<div class="game-container">
<button id="startButton">Старт</button>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<div id="gameMessage"></div>
</div>

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startButton = document.getElementById('startButton');
const gameMessage = document.getElementById('gameMessage');
const gridSize = 20;
const tileCount = canvas.width / gridSize;
const baseSpeed = 150; // Базовая скорость обновления
let speed = baseSpeed;
let pacman = {
x: 1,
y: 1,
dx: 0,
dy: 0,
powerUp: false,
powerUpTime: 0
};
let ghosts = [
{ x: 18, y: 1, dx: -1, dy: 0, color: 'red', alive: true },
{ x: 18, y: 18, dx: -1, dy: 0, color: 'green', alive: true },
{ x: 1, y: 18, dx: 1, dy: 0, color: 'orange', alive: true },
{ x: 10, y: 10, dx: 0, dy: -1, color: 'purple', alive: true }
];
let gameRunning = false;
let dots = [];
let bonuses = [
{ x: 9, y: 6, active: true }, // Верхний центр, чуть выше стены
{ x: 9, y: 9, active: true }, // Средний центр, чуть выше центральной стены
{ x: 9, y: 14, active: true } // Нижний центр, ниже стены
];
let lastTime = 0;
const walls = [
[0,0,20,1], [0,19,20,1], [0,0,1,20], [19,0,1,20], // границы
[2,2,6,1], [2,4,1,4], [5,5,6,1], [10,2,1,6], // верхний левый
[12,2,6,1], [17,4,1,4], [13,5,6,1], // верхний правый
[2,10,6,1], [2,12,1,6], [5,13,6,1], // нижний левый
[12,10,6,1], [17,12,1,6], [13,13,6,1], // нижний правый
[8,8,4,1], [8,11,4,1], [10,7,1,5] // центр
];
// Создание точек (еды)
function createDots() {
dots = [];
for (let x = 0; x < tileCount; x++) {
for (let y = 0; y < tileCount; y++) {
if (!isWall(x, y) && !isBonus(x, y)) {
dots.push({x, y});
}
}
}
}
// Проверка на стены
function isWall(x, y) {
return walls.some(wall =>
x >= wall[0] && x < wall[0] + wall[2] &&
y >= wall[1] && y < wall[1] + wall[3]
);
}
// Проверка на бонус
function isBonus(x, y) {
return bonuses.some(bonus => bonus.x === x && bonus.y === y && bonus.active);
}
// Проверка столкновения с призраками
function checkCollision() {
for (let ghost of ghosts) {
if (ghost.alive && ghost.x === pacman.x && ghost.y === pacman.y) {
if (pacman.powerUp) {
ghost.alive = false; // Pac-Man ест призрака
} else {
gameRunning = false;
gameMessage.textContent = 'Игра окончена! Вы проиграли.';
return true; // Столкновение произошло
}
}
}
return false;
}
// Движение призраков
function moveGhosts() {
ghosts.forEach(ghost => {
if (!ghost.alive) return;
let newX = ghost.x + ghost.dx;
let newY = ghost.y + ghost.dy;
if (isWall(newX, newY) || newX < 0 || newX >= tileCount || newY < 0 || newY >= tileCount) {
const directions = [
{dx: 1, dy: 0}, {dx: -1, dy: 0},
{dx: 0, dy: 1}, {dx: 0, dy: -1}
];
const newDir = directions[Math.floor(Math.random() * directions.length)];
ghost.dx = newDir.dx;
ghost.dy = newDir.dy;
} else {
ghost.x = newX;
ghost.y = newY;
}
});
}
// Отрисовка игры
function draw(timestamp) {
if (!gameRunning) return;
if (!lastTime) lastTime = timestamp;
const deltaTime = timestamp - lastTime;
if (deltaTime > speed) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Отрисовка стен
ctx.fillStyle = 'blue';
walls.forEach(wall => {
ctx.fillRect(wall[0] * gridSize, wall[1] * gridSize,
wall[2] * gridSize, wall[3] * gridSize);
});
// Отрисовка точек
ctx.fillStyle = 'white';
dots.forEach(dot => {
ctx.beginPath();
ctx.arc(dot.x * gridSize + gridSize/2, dot.y * gridSize + gridSize/2, 2, 0, Math.PI * 2);
ctx.fill();
});
// Отрисовка бонусов
ctx.fillStyle = 'white';
bonuses.forEach(bonus => {
if (bonus.active) {
ctx.beginPath();
ctx.arc(bonus.x * gridSize + gridSize/2, bonus.y * gridSize + gridSize/2, 5, 0, Math.PI * 2);
ctx.fill();
}
});
// Движение Pac-Man
let newX = pacman.x + pacman.dx;
let newY = pacman.y + pacman.dy;
if (!isWall(newX, newY) && newX >= 0 && newX < tileCount && newY >= 0 && newY < tileCount) {
pacman.x = newX;
pacman.y = newY;
if (checkCollision()) return; // Прерываем цикл, если столкновение произошло
}
// Съедание точек
dots = dots.filter(dot => !(dot.x === pacman.x && dot.y === pacman.y));
// Съедание бонусов
bonuses.forEach(bonus => {
if (bonus.x === pacman.x && bonus.y === pacman.y && bonus.active) {
bonus.active = false;
pacman.powerUp = true;
pacman.powerUpTime = Date.now();
speed = baseSpeed / 2; // Увеличение скорости
}
});
// Проверка победы
if (dots.length === 0 && bonuses.every(b => !b.active)) {
gameRunning = false;
gameMessage.textContent = 'Поздравляем! Вы выиграли!';
}
// Обработка суперсилы
if (pacman.powerUp && Date.now() - pacman.powerUpTime > 6000) {
pacman.powerUp = false;
speed = baseSpeed; // Возврат к нормальной скорости
}
// Отрисовка Pac-Man с мерцанием при суперсиле
ctx.fillStyle = pacman.powerUp && Math.floor(timestamp / 100) % 2 ? 'white' : 'yellow';
ctx.beginPath();
ctx.arc(pacman.x * gridSize + gridSize/2, pacman.y * gridSize + gridSize/2,
gridSize/2 - 2, 0.2, Math.PI * 1.8);
ctx.lineTo(pacman.x * gridSize + gridSize/2, pacman.y * gridSize + gridSize/2);
ctx.fill();
// Движение призраков и проверка столкновений
moveGhosts();
if (checkCollision()) return; // Прерываем цикл, если столкновение произошло
// Отрисовка призраков
ghosts.forEach(ghost => {
if (ghost.alive) {
ctx.fillStyle = pacman.powerUp ? '#0000ff' : ghost.color; // Синий цвет при суперсиле
ctx.fillRect(ghost.x * gridSize, ghost.y * gridSize, gridSize - 2, gridSize - 2);
}
});
lastTime = timestamp;
}
requestAnimationFrame(draw);
}
// Управление
document.addEventListener('keydown', (e) => {
if (!gameRunning) return;
switch (e.key) {
case 'ArrowUp': pacman.dx = 0; pacman.dy = -1; break;
case 'ArrowDown': pacman.dx = 0; pacman.dy = 1; break;
case 'ArrowLeft': pacman.dx = -1; pacman.dy = 0; break;
case 'ArrowRight': pacman.dx = 1; pacman.dy = 0; break;
}
});
// Старт/перезапуск игры
$(startButton).on('click', () => {
pacman = { x: 1, y: 1, dx: 0, dy: 0, powerUp: false, powerUpTime: 0 };
ghosts = [
{ x: 18, y: 1, dx: -1, dy: 0, color: 'red', alive: true },
{ x: 18, y: 18, dx: -1, dy: 0, color: 'green', alive: true },
{ x: 1, y: 18, dx: 1, dy: 0, color: 'orange', alive: true },
{ x: 10, y: 10, dx: 0, dy: -1, color: 'purple', alive: true }
];
bonuses = [
{ x: 9, y: 6, active: true }, // Верхний центр, чуть выше стены
{ x: 9, y: 9, active: true }, // Средний центр, чуть выше центральной стены
{ x: 9, y: 14, active: true } // Нижний центр, ниже стены
];
gameRunning = true;
createDots();
lastTime = 0;
speed = baseSpeed;
gameMessage.textContent = '';
requestAnimationFrame(draw);
});
</script>

</body>
</html>

Общая структура кода

  • HTML: Основной контейнер <div class=”game-container”> содержит кнопку “Старт”, канвас <canvas> для отрисовки игры и элемент <div id=”gameMessage”> для вывода сообщений о победе/поражении.
  • CSS: Стили для оформления интерфейса, включая черный фон страницы (body), канвас, кнопку и белый текст сообщений.
  • JavaScript: Основная логика игры, включая управление, движение объектов, столкновения и отрисовку.
jQuery в этом коде не обязателен. Он используется только для примитивной демонстрации использования для обработки клика по кнопке startButton через $(startButton).on(‘click’, …). Это можно заменить нативным JavaScript методом startButton.addEventListener(‘click’, …), и остальной код будет работать без изменений, так как больше jQuery нигде не применяется. Всё остальное — управление, отрисовка, логика — уже на чистом JavaScript.

Основные переменные:

  • canvas, ctx: Элемент <canvas> и его 2D-контекст для отрисовки.
  • gridSize = 20: Размер одной клетки игрового поля (20×20 пикселей).
  • tileCount = 20: Количество клеток по ширине и высоте (канвас 400×400 пикселей).
  • baseSpeed = 150, speed: Скорость обновления игры (в миллисекундах).
  • pacman: Объект Pac-Man с координатами (x, y), направлением движения (dx, dy), состоянием суперсилы (powerUp) и временем её действия (powerUpTime).
  • ghosts: Массив из 4 призраков с координатами, направлением, цветом и статусом “жив/мертв” (alive).
  • dots: Массив точек (еды) на поле.
  • bonuses: Массив из 3 бонусов, дающих суперсилу.
  • walls: Массив стен лабиринта в формате [x, y, ширина, высота].
  • gameRunning: Флаг, указывающий, активна ли игра.

Основные функции и их назначение:

createDots()

  • Описание: Создает массив точек (еды) для Pac-Man.
  • Логика: Проходит по всем клеткам поля (20×20). Если клетка не занята стеной (!isWall(x, y)) и не содержит бонус (!isBonus(x, y)), добавляет точку в массив dots.
  • Назначение: Инициализирует еду на поле при старте игры.

isWall(x, y)

  • Описание: Проверяет, находится ли клетка (x, y) внутри стены.
  • Логика: Использует walls.some(), чтобы проверить, пересекаются ли координаты (x, y) с любым прямоугольником из массива walls.
  • Назначение: Используется для предотвращения движения Pac-Man и призраков сквозь стены.

isBonus(x, y)

  • Описание: Проверяет, находится ли клетка (x, y) на месте активного бонуса.
  • Логика: Проверяет массив bonuses на совпадение координат с активным бонусом (bonus.active).
  • Назначение: Исключает наложение точек на бонусы и определяет, когда Pac-Man собирает бонус.

checkCollision()

  • Описание: Проверяет столкновение Pac-Man с призраками.
  • Логика:
  • Проходит по массиву ghosts.
  • Если живой призрак (ghost.alive) находится на той же клетке, что и Pac-Man:
  • При активной суперсиле (pacman.powerUp) призрак “умирает” (ghost.alive = false).
  • Без суперсилы игра останавливается (gameRunning = false), и отображается сообщение “Игра окончена! Вы проиграли.”.
  • Возвращает true, если столкновение произошло, иначе false.
  • Назначение: Управляет логикой взаимодействия Pac-Man с призраками.

moveGhosts()

  • Описание: Управляет движением призраков.
  • Логика:
  • Для каждого живого призрака вычисляет новые координаты (newX, newY) на основе текущего направления (dx, dy).
  • Если путь заблокирован стеной или границей поля, выбирает случайное новое направление из четырех возможных.
  • Если путь свободен, обновляет координаты призрака.
  • Назначение: Обеспечивает случайное движение призраков по полю.

draw(timestamp)

  • Описание: Основной игровой цикл, отвечающий за обновление и отрисовку состояния игры.
  • Логика:
  • Выполняется каждые speed миллисекунд через requestAnimationFrame.
  • Очищает канвас и отрисовывает стены, точки, бонусы, Pac-Man и призраков.
  • Обновляет положение Pac-Man и проверяет столкновения после его движения.
  • Обрабатывает сбор точек и бонусов.
  • Проверяет условия победы (все точки и бонусы собраны).
  • Управляет суперсилой (включает/выключает через 6 секунд).
  • Двигает призраков и проверяет столкновения после их движения.
  • Назначение: Сердце игры, объединяющее всю логику и визуализацию.

Дополнительные элементы:

Управление (document.addEventListener(‘keydown’)):

  • Обрабатывает нажатия стрелок клавиатуры, изменяя направление Pac-Man (dx, dy), если игра активна.

Старт/перезапуск ($(startButton).on(‘click’)):

  • Инициализирует объекты игры (Pac-Man, призраки, бонусы), создает точки, сбрасывает состояние и запускает цикл draw.
Нажмите, чтобы оценить наш труд:
[Всего: 1 Средняя: 5]
Ethan Carter

Я, Итан Картер – американский разработчик и технический автор с более чем 20-летним опытом в системном и прикладном программировании. Мой основной профиль — низкоуровневая разработка на Assembler: 22 года практики, включая глубокую работу с оптимизацией кода, архитектурой процессоров и производительностью критичных по скорости решений. Я защитил PhD dissertation по Assembler, а также более 18 лет работаю с ASP.NET, создавая корпоративные веб-системы, API и масштабируемые backend-решения.

Дополнительно я имею 9 лет опыта в C++ и C#, а также 7 лет практики программирования микроконтроллеров на Assembler. Благодаря моему сочетанию академической подготовки и прикладного инженерного опыта я могу писать статьи на стыке архитектуры ПО, низкоуровневой оптимизации и современной разработки, делая сложные технические темы понятными для профессиональной аудитории.

2 комментария к “Пишем игру Pac-Man с помощью JavaScript”

  1. free tv online

    I just like the valuable info you provide for
    your articles. I’ll bookmark your weblog and check again right here frequently.
    I’m reasonably sure I will be informed lots of new stuff
    right right here! Best of luck for the next!

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *


Срок проверки reCAPTCHA истек. Перезагрузите страницу.

О нас | Контакты


Прокрутить вверх