Урок 11 · Слайд 1
🐍

Архитектура игры

Как устроена классическая Змейка

Любая игра состоит из трёх частей:

  • Состояние — где находятся объекты (змейка, еда)
  • Обновление — логика движения и столкновений
  • Отрисовка — рисуем текущее состояние на Canvas
game.js — структура
// Состояние
let змейка = [{x:5, y:5}]; // массив сегментов
let еда = {x:10, y:10};
let направление = {x:1, y:0}; // движение вправо
let счёт = 0;

// Игровой цикл (запускается каждые 150мс)
function gameLoop() {
  обновить();
  нарисовать();
}
setInterval(gameLoop, 150);
📖 Словарик
Game loop
Бесконечный цикл: обновить состояние → нарисовать → повторить.
"setInterval(gameLoop, 150) — 6-7 кадров в секунду"
Состояние игры
Все данные, описывающие текущую ситуацию: позиции, очки.
"Массив змейки + позиция еды + счёт"
Урок 11 · Слайд 2
🟩

Сетка и поле

Делим Canvas на клетки

game.js
const РАЗМЕР = 20; // пикселей в клетке
const КОЛОНОК = 20; // количество клеток
const СТРОК = 20;

// Рисуем фон (сетку)
function нарисоватьФон() {
  for (let x = 0; x < КОЛОНОК; x++) {
    for (let y = 0; y < СТРОК; y++) {
      ctx.fillStyle = (x + y) % 2 === 0
        ? '#e8f5e9' : '#c8e6c9';
      ctx.fillRect(x*РАЗМЕР, y*РАЗМЕР, РАЗМЕР, РАЗМЕР);
    }
  }
}
📖 Словарик
Клетка (tile)
Единица сетки. Позиция в координатах клеток, не пикселей.
"Клетка (5, 3) = пиксели (100, 60) при размере 20px"
% 2 === 0
Чётность: шахматный узор через остаток от деления.
"(x+y) % 2 чередует цвет через одну клетку"
Урок 11 · Слайд 3
🔢

Массив сегментов змейки

Голова + тело = массив координат

game.js — движение змейки
// Змейка = массив клеток [{x,y}, ...]
// Первый элемент = голова
let змейка = [{x:5,y:5}, {x:4,y:5}, {x:3,y:5}];

function двигаться() {
  // Новая голова = старая голова + направление
  const голова = {
    x: змейка[0].x + направление.x,
    y: змейка[0].y + направление.y
  };
  
  // Добавляем голову в начало
  змейка.unshift(голова);
  
  // Убираем хвост (если не съели еду)
  змейка.pop();
}
📖 Словарик
unshift()
Добавляет элемент в начало массива.
"unshift(голова) — новая голова в начале массива"
pop()
Удаляет последний элемент массива.
"pop() убирает хвост змейки"
Направление {x, y}
Вектор движения: {1,0} вправо, {-1,0} влево, {0,1} вниз.
"{x:0, y:-1} — движение вверх"
Урок 11 · Слайд 4
⌨️

Управление клавишами

Стрелки меняют направление

game.js — управление
let направление = {x: 1, y: 0};
let следующее = {x: 1, y: 0}; // буфер

document.addEventListener('keydown', (e) => {
  switch (e.key) {
    case 'ArrowUp':
      if (направление.y !== 1) следующее = {x:0, y:-1};
      break;
    case 'ArrowDown':
      if (направление.y !== -1) следующее = {x:0, y:1};
      break;
    case 'ArrowLeft':
      if (направление.x !== 1) следующее = {x:-1, y:0};
      break;
    case 'ArrowRight':
      if (направление.x !== -1) следующее = {x:1, y:0};
      break;
  }
});
📖 Словарик
switch / case
Выбор действия по значению переменной.
"switch(e.key) выбирает стрелку нажатой клавиши"
Буфер направления
Хранит следующее направление, применяем в начале кадра.
"Без буфера можно развернуться в себя"
Противоположное направление
Проверяем, что не разворачиваемся на 180°.
"Если движемся вправо (x=1), нельзя повернуть влево (x=-1)"
Урок 11 · Слайд 5
🍎

Еда и рост змейки

Съедаем — растём!

game.js
// Случайная позиция еды
function поставитьЕду() {
  еда = {
    x: Math.floor(Math.random() * КОЛОНОК),
    y: Math.floor(Math.random() * СТРОК)
  };
}

// Проверяем: голова на еде?
function проверитьЕду() {
  const голова = змейка[0];
  if (голова.x === еда.x && голова.y === еда.y) {
    счёт++; // +1 очко
    поставитьЕду(); // новая еда
    // НЕ вызываем pop() → змейка растёт!
  } else {
    змейка.pop(); // убираем хвост
  }
}
📖 Словарик
Коллизия (столкновение)
Проверка совпадения координат двух объектов.
"Если голова == позиция еды — съели!"
Math.floor(Math.random() * N)
Случайное целое число от 0 до N-1.
"floor(random() * 20) — случайная клетка из 20"
Урок 11 · Слайд 6
💥

Столкновения и конец игры

Стены и сама себя

game.js
function проверитьСтолкновение() {
  const голова = змейка[0];
  
  // Стены
  if (голова.x < 0 || голова.x >= КОЛОНОК ||
      голова.y < 0 || голова.y >= СТРОК) {
    конецИгры();
    return;
  }
  
  // Врезалась в себя (пропускаем голову [0])
  for (let i = 1; i < змейка.length; i++) {
    if (голова.x === змейка[i].x &&
        голова.y === змейка[i].y) {
      конецИгры();
      return;
    }
  }
}
📖 Словарик
Проверка границ
Убеждаемся, что объект не вышел за пределы поля.
"x < 0 || x >= 20 — выход за левую или правую стену"
Самостолкновение
Проверяем совпадение головы с каждым сегментом тела.
"Начинаем с i=1, голова [0] не считается"
Урок 11 · Слайд 7
💯

Счёт и отрисовка

Рисуем всё красиво

game.js — нарисовать()
function нарисовать() {
  // Очистить
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  нарисоватьФон();
  
  // Нарисовать змейку
  змейка.forEach((клетка, индекс) => {
    ctx.fillStyle = индекс === 0 ? '#1e8449' : '#27ae60';
    ctx.fillRect(клетка.x*РАЗМЕР+1, клетка.y*РАЗМЕР+1,
      РАЗМЕР-2, РАЗМЕР-2);
  });
  
  // Нарисовать еду (красный круг)
  ctx.beginPath();
  ctx.arc(еда.x*РАЗМЕР+РАЗМЕР/2, еда.y*РАЗМЕР+РАЗМЕР/2,
    РАЗМЕР/2-2, 0, Math.PI*2);
  ctx.fillStyle = '#e74c3c';
  ctx.fill();
  
  // Счёт
  ctx.font = 'bold 20px Arial';
  ctx.fillStyle = '#333';
  ctx.fillText('Счёт: ' + счёт, 10, 25);
}
Урок 11 · Слайд 8
🏆

Полный код + задание

Собери свою Змейку!

1
Создай HTML с <canvas id="game" width="400" height="400">
2
Реализуй состояние: змейка, еда, направление, счёт, работает
3
Напиши функции: двигаться(), проверитьЕду(), проверитьСтолкновение()
4
Напиши функцию нарисовать() — фон, змейка, еда, счёт
5
Добавь управление клавишами + запусти setInterval(gameLoop, 150)
6
★ Бонус: экран "Конец игры" + кнопка "Играть снова" 🏆