Курс: Создание игры «Змейка» в DeepSeek на чистом JavaScript

Счет: 0
Скорость: 100%

🎮 Как играть:

📱 На телефоне: жми кнопки внизу
💻 На компьютере: используй стрелки ←↑→↓


Уровень: Начинающий
Продолжительность: 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
  • Обновление текста кнопок

Дополнительные задания:

  1. Добавить систему рекордов с LocalStorage
  2. Реализовать разные уровни сложности
  3. Создать меню выбора скинов для змейки
  4. Добавить звуковые эффекты

Рекомендации по улучшению кода:

  1. Рефакторинг:

// Было
function a() { … }
function b() { … }

// Стало
const Game = {
init() {},
draw() {},
move() {}
}

  1. Добавить Webpack для сборки
  2. Реализовать ООП-подход

Типичные ошибки:

  1. Некорректная работа ресайза:
  • Забыть вызвать resizeCanvas() при инициализации
  1. Наложение элементов:
  • Проверить z-index в CSS
  1. Мобильное управление:
  • Добавить 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>

Нравится пост? Отправляйте друзьям
DeepSeek
Comments: 4
  1. Игорь

    Вот это я понимаю, полезный контент. Без фреймворков, чисто на ванильке. Для понимания основ самое то.

  2. Степан

    спасибо за отличную статью

  3. Andrey Life

    Классный пост!! Есть прекрасный гайд из шести уроков для новичков, прекрасно, есть чему поучиться у вас.

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: