🎮 Как играть:
📱 На телефоне: жми кнопки внизу
💻 На компьютере: используй стрелки ←↑→↓
Уровень: Начинающий
Продолжительность: 6 уроков (3-4 часа)
Урок 1: Подготовка структуры HTML
Цель: Создать базовую HTML-структуру игры
1.1 Основные элементы:
<div class=«game-container»>
<canvas id=«gameCanvas»></canvas>
<!— Интерфейсные элементы —>
</div>
1.2 Пояснение:
<canvas>
— игровое поле для отрисовки графики- Система контроля через data-атрибуты:
data-direction
Урок 2: Стилизация CSS
Цель: Создать адаптивный дизайн
2.1 Основные правила:
gameCanvas {
max-width: 600px;
image-rendering: crisp-edges; /* Сглаживание пикселей */
}
2.2 Адаптивность:
- Медиа-запросы для мобильных устройств
- Flex-распределение элементов управления
Урок 3: Инициализация игры
Цель: Настроить базовую логику
3.1 Основные константы:
const gridSize = 20; // Размер клетки
const BASE_SPEED = 400; // Начальная скорость
3.2 Состояние игры:
- Змейка: массив координат
- Еда: объект с позицией
- Направление движения: строковая переменная
Урок 4: Игровой цикл и движение
Цель: Реализовать движение змейки
4.1 Алгоритм движения:
function moveSnake() {
const head = {…snake[0]};
// Обновление координат головы
snake.unshift(head);
if(!eaten) snake.pop();
}
4.2 Система скоростей:
- Динамическое изменение интервала setInterval
- Формула ускорения:
speedMultiplier *= (1 - SPEED_INCREMENT)
Урок 5: Коллизии и взаимодействия
Цель: Реализовать столкновения
5.1 Проверка столкновений:
if(
head.x < 0 || head.x >= tileCount ||
snake.some(segment => …)
) gameOver();
5.2 Генерация еды:
- Рекурсивная проверка свободных клеток
- Использование
Math.random()
Урок 6: Управление и UI
Цель: Добавить интерактивность
6.1 Обработчики событий:
document.addEventListener(‘keydown’, (e) => {
// Логика изменения направления
});
6.2 Система паузы:
- Переключение состояния через
isPaused
- Обновление текста кнопок
Дополнительные задания:
- Добавить систему рекордов с LocalStorage
- Реализовать разные уровни сложности
- Создать меню выбора скинов для змейки
- Добавить звуковые эффекты
Рекомендации по улучшению кода:
- Рефакторинг:
// Было
function a() { … }
function b() { … }// Стало
const Game = {
init() {},
draw() {},
move() {}
}
- Добавить Webpack для сборки
- Реализовать ООП-подход
Типичные ошибки:
- Некорректная работа ресайза:
- Забыть вызвать
resizeCanvas()
при инициализации
- Наложение элементов:
- Проверить z-index в CSS
- Мобильное управление:
- Добавить
touchstart
обработчики
Весь код написан DeepSeek
<div class=«game-container»>
<canvas id=«gameCanvas»></canvas>
<div id=«score»>Счет: 0</div>
<div id=«speed»>Скорость: 100%</div>
<div class=«controls»>
<button id=«startBtn»>Старт</button>
<button id=«pauseBtn»>Пауза</button>
</div>
<div class=«instructions»>
<h3>🎮 Как играть:</h3>
<p>📱 На телефоне: жми кнопки внизу<br>
💻 На компьютере: используй стрелки ←↑→↓</p>
</div>
<div class=«mobile-controls»>
<button class=»arrow-btn» data-direction=»up»>↑</button>
<div>
<button class=»arrow-btn» data-direction=»left»>←</button>
<button class=»arrow-btn» data-direction=»down»>↓</button>
<button class=»arrow-btn» data-direction=»right»>→</button>
</div>
</div>
</div>
<style>
.game-container {
text-align: center;
margin: 0 auto;
max-width: 100%;
padding: 10px;
}
#gameCanvas {
width: 100% !important;
height: auto !important;
max-width: 600px;
max-height: 80vh;
border: 2px solid #333;
background-color: #87CEEB;
image-rendering: crisp-edges;
}
.controls button {
padding: 10px 20px;
font-size: 16px;
margin: 10px 5px;
cursor: pointer;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
}
#pauseBtn {
background-color: #f44336;
}
.instructions {
background: #fff3cd;
padding: 15px;
border-radius: 10px;
margin: 15px auto;
max-width: 400px;
}
.mobile-controls {
display: none;
margin-top: 20px;
}
.arrow-btn {
font-size: 30px;
width: 60px;
height: 60px;
margin: 5px;
border-radius: 50%;
background: #4CAF50;
color: white;
border: none;
cursor: pointer;
}
@media (max-width: 600px) {
.mobile-controls {
display: block;
}
}
</style>
<script>
const canvas = document.getElementById(‘gameCanvas’);
const ctx = canvas.getContext(‘2d’);
const startBtn = document.getElementById(‘startBtn’);
const pauseBtn = document.getElementById(‘pauseBtn’);
const scoreElement = document.getElementById(‘score’);
const speedElement = document.getElementById(‘speed’);
const gameContainer = document.querySelector(‘.game-container’);
// Настройки игры
let gridSize = 20;
let tileCount;
const BASE_SPEED = 400;
const SPEED_INCREMENT = 0.05;
// Состояние игры
let snake = [];
let food = {};
let direction = ‘right’;
let score = 0;
let gameLoop = null;
let isPaused = false;
let currentSpeed = BASE_SPEED;
let speedMultiplier = 1;
// Инициализация размеров
function resizeCanvas() {
const containerWidth = gameContainer.clientWidth;
const maxSize = Math.min(containerWidth, window.innerHeight * 0.6);
const canvasSize = Math.min(maxSize, 600) — 20;
canvas.width = Math.floor(canvasSize / gridSize) * gridSize;
canvas.height = canvas.width;
tileCount = Math.floor(canvas.width / gridSize);
if(!gameLoop) drawStartScreen();
}
function drawStartScreen() {
ctx.fillStyle = ‘#87CEEB’;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = ‘white’;
ctx.font = ’30px Arial’;
ctx.textAlign = ‘center’;
ctx.fillText(‘Игра DeepSeek’, canvas.width/2, canvas.height/2);
ctx.font = ’20px Arial’;
ctx.fillText(‘Нажмите «Старт» чтобы начать’, canvas.width/2, canvas.height/2 + 40);
}
function initGame() {
snake = [{x: 5, y: 5}];
food = generateFood();
direction = ‘right’;
score = 0;
currentSpeed = BASE_SPEED;
speedMultiplier = 1;
scoreElement.textContent = `Счет: ${score}`;
updateSpeedDisplay();
}
function generateFood() {
while(true) {
const newFood = {
x: Math.floor(Math.random() * tileCount),
y: Math.floor(Math.random() * tileCount)
};
if (!snake.some(segment => segment.x === newFood.x && segment.y === newFood.y)) {
return newFood;
}
}
}
function drawGame() {
ctx.fillStyle = ‘#87CEEB’;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Отрисовка змейки
snake.forEach((segment, index) => {
ctx.fillStyle = index === 0 ? ‘#0096ff’ : ‘#00c8ff’;
ctx.beginPath();
ctx.roundRect(
segment.x * gridSize,
segment.y * gridSize,
gridSize — 2,
gridSize — 2,
5
);
ctx.fill();
if(index === 0) {
ctx.fillStyle = ‘white’;
ctx.font = ’12px Arial’;
ctx.textAlign = ‘center’;
ctx.fillText(‘DS’,
segment.x * gridSize + gridSize/2,
segment.y * gridSize + gridSize/2 + 4
);
}
});
// Отрисовка еды
ctx.fillStyle = ‘#ff6b6b’;
ctx.beginPath();
ctx.arc(
food.x * gridSize + gridSize/2,
food.y * gridSize + gridSize/2,
gridSize/2 — 2,
0,
Math.PI * 2
);
ctx.fill();
}
function moveSnake() {
if(isPaused) return;
const head = {…snake[0]};
switch(direction) {
case ‘up’: head.y—; break;
case ‘down’: head.y++; break;
case ‘left’: head.x—; break;
case ‘right’: head.x++; break;
}
// Проверка столкновений
if (head.x < 0 || head.x >= tileCount ||
head.y < 0 || head.y >= tileCount ||
snake.some(segment => segment.x === head.x && segment.y === head.y)) {
gameOver();
return;
}
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
score += 10;
scoreElement.textContent = `Счет: ${score}`;
food = generateFood();
increaseSpeed();
} else {
snake.pop();
}
drawGame();
}
function increaseSpeed() {
speedMultiplier *= (1 — SPEED_INCREMENT);
currentSpeed = BASE_SPEED * speedMultiplier;
updateSpeedDisplay();
restartGameLoop();
}
function updateSpeedDisplay() {
const percentage = Math.round((1 / speedMultiplier) * 100);
speedElement.textContent = `Скорость: ${percentage}%`;
}
function restartGameLoop() {
clearInterval(gameLoop);
gameLoop = setInterval(() => moveSnake(), currentSpeed);
}
function gameOver() {
clearInterval(gameLoop);
gameLoop = null;
alert(`Игра окончена! Счет: ${score}`);
startBtn.textContent = ‘Старт’;
drawStartScreen();
}
function togglePause() {
isPaused = !isPaused;
startBtn.textContent = isPaused ? ‘Продолжить’ : ‘Пауза’;
}
// Обработчики событий
startBtn.addEventListener(‘click’, () => {
if(!gameLoop) {
initGame();
restartGameLoop();
startBtn.textContent = ‘Пауза’;
} else {
togglePause();
}
});
pauseBtn.addEventListener(‘click’, togglePause);
document.addEventListener(‘keydown’, (e) => {
switch(e.key) {
case ‘ArrowUp’: if (direction !== ‘down’) direction = ‘up’; break;
case ‘ArrowDown’: if (direction !== ‘up’) direction = ‘down’; break;
case ‘ArrowLeft’: if (direction !== ‘right’) direction = ‘left’; break;
case ‘ArrowRight’: if (direction !== ‘left’) direction = ‘right’; break;
}
});
document.querySelectorAll(‘.arrow-btn’).forEach(btn => {
btn.addEventListener(‘click’, () => {
const newDir = btn.dataset.direction;
if(
(direction === ‘up’ && newDir === ‘down’) ||
(direction === ‘down’ && newDir === ‘up’) ||
(direction === ‘left’ && newDir === ‘right’) ||
(direction === ‘right’ && newDir === ‘left’)
) return;
direction = newDir;
});
});
// Инициализация
resizeCanvas();
window.addEventListener(‘resize’, resizeCanvas);
drawStartScreen();
</script>