Напишем код, который представляет собой простую реализацию игры Pac-Man, написанную на JavaScript с использованием HTML5 Canvas, CSS для генерации страницы.
Давайте опишем его структуру и выделим основные функции с их назначением.
В отличие от прошлых наших примеров, подробно расписывать написание JS кода мы не будем, распишем лишь структуру и основные функции с переменными. Это хорошая практика для начинающих программистов на JavaScript.
- Приведем ссылку на демонстрацию (не работает с тачсрина мобильных устройств): Игра Pac-Man на JS.
Как работает игра:
- Игрок нажимает “Старт”, что запускает игру и отрисовывает изначальное поле.
- Pac-Man движется по полю с помощью стрелок, собирая точки и бонусы.
- Бонусы в виде белых точек дают суперсилу на 6 секунд, позволяя “есть” призраков.
- При столкновении с призраком без суперсилы игра заканчивается с сообщением “Игра окончена! Вы проиграли.”.
- Если собраны все точки и бонусы, появляется сообщение “Поздравляем! Вы выиграли!”.
Не реализовано (попробуйте сделать сами для практики):
- Если “съедены” все призраки, появляется сообщение “Поздравляем! Вы выиграли!”.
- Звук подбора точек
- Вывод динамически меняющихся очков за подбор точек.
Если кому нужен исходный код, то вот он (даны некоторые комментарии к блокам):
<!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: Основная логика игры, включая управление, движение объектов, столкновения и отрисовку.
Основные переменные:
- 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.

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







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!
Thank you, but advertisements are not permitted in comments