Перейти к основному содержимому

9.02. Классические игры

Родителям и детям
Классические игры
Tetris, Mario, Battle City, Space Invaders
Добавить mermaid схему
Добавить задачи

🕹️ Часть 1. Классические игры: дверь в мир цифрового мышления

«Иногда самые простые вещи — самые сильные. Игры 1980–1990-х — это не просто “старые” программы. Это первые языки, на которых человечество заговорило с машинами — языки, в которых каждая кнопка имеет значение, каждый пиксель — смысл, а каждая победа — результат настоящего мышления».

🔹 Почему именно классические игры?

Современные игры — это зрелища. Они показывают нам миры, где небо дышит, а герои плачут. Но в них часто за нас думает компьютер: он решает, когда подсказать, как прыгнуть, куда бежать.
Классические игры — это диалог.
Нет голосовых подсказок. Нет карт в углу экрана. Нет сохранений через каждые 30 секунд. Есть только ты, экран и правила — чёткие, как законы физики.

Именно в таких играх формируются навыки, которые потом пригодятся не только в программировании, но и в жизни:

  • Анализ — ты учишься замечать закономерности (например, как двигаются враги в Galaga),
  • Планирование — ты думаешь на несколько шагов вперёд (Lode Runner, Bomberman),
  • Устойчивость к ошибкам — ты падаешь, но встаёшь снова («Game Over» — не конец, а приглашение переосмыслить стратегию),
  • Креативность — когда нельзя перестрелять проблему, приходится обойти её (Metroid, Prince of Persia).

Поэтому начинать знакомство с цифровым миром — лучше всего с этих игр. Это как учиться читать не по соцсетям, а по «Приключениям Тома Сойера».


🔹 Что такое «классические» — и почему NES?

NES — сокращение от Nintendo Entertainment System. Это домашняя игровая приставка, выпущенная в Японии в 1983 году (там она называлась Famicom — Family Computer), а в Северной Америке — в 1985-м. А в СССР и позже в постсоветском пространстве — под именем Dendy (это клон NES, произведённый в Тайване и Китае).

⚠️ Важно:

  • Dendy ≠ NES, но полностью совместима с картриджами NES.
  • NES не была первой приставкой (до неё были Atari 2600, Intellivision и др.), но именно она восстановила игровую индустрию после кризиса 1983 года и заложила основы современной игровой культуры.

Технически NES — это 8-битный компьютер с процессором Ricoh 2A03 (клон MOS 6502), 2 КБ оперативной памяти и графическим чипом, способным отображать до 256×240 пикселей с палитрой из 54 цветов. Звук — 5 каналов: 2 импульсных, 1 треугольный, 1 шумовой и 1 для PCM-сэмплов (очень редко использовался).

Но для ребёнка важнее не цифры, а ощущение:

Когда ты включаешь NES, ты не просто запускаешь игру — ты входишь в договор с машиной: «Я буду внимателен. Я буду учиться. И тогда — ты дашь мне шанс победить».


🔹 Как сегодня в это играть? (Практическая инструкция)

Вариант 1. Оригинал. Почти как в 1987 году.Рекомендуется как основной

Что нужно:

  • Приставка: NES, Famicom, Dendy (новая или б/у).

    На «Авито», «Юле», «Ozon», «Wildberries» продаются как новые сборки (часто с HDMI-выходом), так и винтажные экземпляры. Средняя цена — от 1 500 до 4 000 ₽.
    Совет: ищи комплекты с 2 джойстиками и 2–3 играми — они надёжнее и дешевле поштучной покупки.

  • Картриджи: оригинальные (дорого, от 800 ₽ за простые, до 10 000+ за редкие) или репринты (новые картриджи с классическими играми — от 300 ₽).

    Важно: избегай «мульткарт» с 1000+ играми — они почти всегда содержат пиратские копии с багами и искажённой графикой.

  • Подключение:

    • Старые телевизоры (с RCA-входом — «тюльпан»): просто воткнуть.
    • Современные мониторы/ТВ: нужен RCA-to-HDMI конвертер (от 500 ₽). Есть и приставки с встроенным HDMI (например, Dendy Classic HDMI).

Плюсы: подлинный тактильный опыт (клик джойстика, запах пластикового картриджа), никакой эмуляции — только железо.
Минусы: возможны проблемы с совместимостью ТВ, износ контактов.

Вариант 2. Эмуляция. Технически правильно — но осторожно.

Эмулятор — программа, которая имитирует работу NES на ПК, смартфоне или Raspberry Pi.
Популярные: FCEUX, Mesen, bsnes/higan.

⚠️ Важно:

  • Сам эмулятор — легален.
  • ROM-файлы (образы игр) — легальны только если вы владеете оригинальным картриджем и сделали дамп самостоятельно.
  • Скачивание ROM из интернета — нарушение авторских прав.

Для учебных целей (например, в школе) допустимо использовать homebrew-игры (свободные, созданные сообществом), например:

  • Nuclear Throne Classic (демо),
  • Shooting Star,
  • Puzzle Dash.
Вариант 3. Официальные переиздания.
  • Nintendo Switch Online + NES/SNES: подписка (~300 ₽/мес) даёт доступ к 100+ играм с облачными сохранениями и онлайн-мультиплеером.
  • Evercade EXP / VS: портативная/домашняя консоль с лицензионными картриджами от Atari, Capcom, Data East.

📌 Наша рекомендация: начинайте с оригинальной Dendy/NES. Это как учиться играть на акустической гитаре, а не на гитаре с автотюном. Ощущение честного взаимодействия — бесценно.


🔹 Путешествие по играм: не список, а карта смысла

Ниже — структурированное погружение. Каждая игра — урок, замаскированный под веселье.

Теперь — коротко, но ёмко — о ключевых играх из вашего списка (выделены особо значимые):


🐢 Toxic Crusadersэкология как приключение

«Ты — мутант-защитник Земли. Твоя задача — не стрелять во всех подряд, а очищать заводы, реки, леса от ядовитых отходов. Иногда — с помощью пушек. Чаще — с помощью ума».

Эта игра — редкий пример экологического повествования в эпоху, когда «зелёные» темы почти не затрагивались. Уровни построены как головоломки: надо найти, откуда идёт загрязнение, перекрыть трубы, собрать отходы.
→ Учит: системному мышлению (всё связано), ответственности (последствия действий видны сразу), нестандартным решениям (иногда надо не убить босса, а отключить его реактор).

🦆 Duck Huntархитектура взаимодействия

«Ты не просто стреляешь. Ты учишься целиться в реальном времени, учитывая задержку сигнала, чувствительность датчика, даже угол наклона Zapper-пистолета».

Zapper — световое ружьё. Оно не «видит» утку. Оно ловит вспышку на экране, когда пиксель в зоне прицела становится белым.
→ Это первое знакомство с физикой ввода: задержка, калибровка, шум. То же, с чем сталкиваются разработчики VR и AR сегодня.

🧱 Tetrisгеометрия в движении

«Падающие фигуры — это не кубики. Это матрицы. И каждая поворот — операция вращения над вектором».

Тетрис — идеальный мост от игры к программированию:

  • Координаты (X, Y),
  • Коллизии («можно ли сдвинуть блок?»),
  • Состояния («полная линия → исчезновение → сдвиг вниз»).
    На NES-версии (1989) — идеальный баланс сложности: рост скорости линейный, а не экспоненциальный. Можно дойти до Level 15, не имея рефлексов про-геймера.
🍄 Super Mario Bros.язык платформера

«Марио не бегает. Он взаимодействует с физикой: инерция прыжка, скольжение по льду (в SMB3), отскок от пружин».

Уровни в SMB — это учебные модули:

  • 1-1: научись прыгать, избегать врагов, использовать ?-блоки.
  • 1-2: освой вертикальное мышление.
  • 3-1: научись управлять ветром (облака).
    → Это как «Hello, World!» для геймдизайна: каждое действие имеет обратную связь, каждый риск — награду.
🏰 The Legend of Zeldaне линейный сюжет, а карта возможностей

«Здесь нет “прохождения”. Есть исследование. Ты можешь пойти в пещеру — и погибнуть. Или найти меч — и вернуться. Или обойти босса сзади… если догадаешься, что это возможно».

Zelda — первая игра, где карта стала главным героем. Каждая комната — узел в графе возможностей.
→ Учит: не бояться «неправильных» путей, строить внутренние карты знаний, доверять интуиции.

🔥 Contraкооперация как стратегия

«Одиночный прохождение — почти невозможно. Вдвоём — реально. Почему? Потому что один прикрывает, второй атакует. Один ловит бонус, второй отвлекает врага».

Contra — демонстрация синергии.
→ Важный урок: иногда сила — не в мощи персонажа, а в согласованности действий. Как в Git, когда merge-конфликт разрешает пара разработчиков, а не один.

⚔️ Mortal Kombatпочему важно читать инструкции

«“Finish him!” — знаменитая фраза. Но чтобы выполнить Fatality, надо ввести точную комбинацию: Вниз, Вперёд, Вперёд, Удар. Одна ошибка — и герой просто ударит. Или промахнётся».

Это — урок точности ввода. Как при написании кода: один пропущенный ; — и программа не скомпилируется.
→ Также: первая игра, где контекст важен (разные Fatality в зависимости от персонажа и локации).

🥊 Punch-Out!!паттерны поведения

*«Противники не “крутые”. Они предсказуемые. Габриэль бьёт правой после трёх шагов влево. Блондиный — моргает перед ударом. Ты не сражаешься с ними. Ты читаешь их код».

→ Это тренировка распознавания паттернов — основы машинного обучения и отладки: сначала видишь симптом, потом ищешь причину, потом — правило.


🔹 Задачи для закрепления

🔹 Задача 1. «Сделай карту уровня»

Возьми Super Mario Bros., Level 1-1.
Нарисуй его на бумаге в клетку:

  • 1 клетка = 1 блок (16×16 пикселей).
  • Обозначь: ?-блоки, кирпичи, враги, трубы, бонусы.
    → Цель: увидеть структуру — почему здесь гриб, а не цветок? Почему враги идут группой? Это не случайность — это педагогический дизайн.
🔹 Задача 2. «Разгадай паттерн»

Включи Pac-Man. Пройди первые 2 уровня.
Запиши:

  • Как двигаются призраки в начале?
  • Как меняется их поведение, когда ты съедаешь Power Pellet?
  • Когда Блинки (оранжевый) перестаёт преследовать и убегает?
    → Цель: научиться видеть конечные автоматы — основу поведения NPC.
🔹 Задача 3. «Собери свой NES-стартовый комплект»

Представь, что у тебя есть 2 500 ₽.
Составь список:

  • 1 приставка (модель),
  • 2 обязательные игры,
  • 1 аксессуар (например, Zapper или 4-игровой адаптер).
    Обоснуй выбор с точки зрения:
  • обучаемости,
  • кооперативности,
  • разнообразия жанров.
🔹 Задача 4. «Напиши инструкцию для друга»

Выбери одну игру (например, Lode Runner).
Напиши краткую инструкцию (не более 100 слов), как научить новичка пройти первый уровень.
→ Проверь: нет ли слов вроде «просто», «очевидно»? Это признаки проклятия знания — когда мы забываем, что не всё очевидно для других.


🧰 Часть 2. Вскрываем приставку: анатомия NES/Dendy

«Приставка — не волшебная коробка. Это — оркестр из микросхем. Каждая играет свою партию. Иногда — в унисон. Иногда — в диссонансе. И именно из этого звука рождается чудо».

🔹 Первый миф: «Картридж — это только хранилище игры»

Нет.
Картридж NES — это не флешка, а полноценный расширительный модуль. Внутри него не только ПЗУ (постоянное запоминающее устройство, ROM), но и часто — дополнительные чипы:

  • MMC (Memory Management Controller) — контроллер управления памятью.
    Например, в Super Mario Bros. 3 (1990) используется MMC3. Он позволяет:
    • «переключать» банки памяти (игра весит 512 КБ, но NES «видит» только 32 КБ за раз),
    • управлять таймерами (для анимации фона),
    • генерировать прерывания (чтобы враги появлялись вовремя).

→ Это как «виртуальная память» в современных ОС, но реализованная железом.

  • CHR-RAM — динамическая видеопамять.
    В The Legend of Zelda (1987) — статическая CHR-ROM (рисунки хранятся на картридже).
    В Mega Man 2 (1988) — CHR-RAM: рисунки генерируются в реальном времени, что позволяет менять палитру в зависимости от уровня.

  • Батарейка для сохраненийZelda, Metroid, Final Fantasy).
    Это литиевая CR2032, подключённая к SRAM. Она живёт 10–15 лет. Когда игра «забывает» сохранение — пора менять батарейку (задача для Части 4: «Ремонт своими руками»).


🔹 Что внутри приставки? Три главных героя

Разберём NES по слоям. Представим, что приставка — это трёхэтажное здание:

1. CPU — 2A03 (Ricoh)

Это клон процессора MOS 6502 — того самого, что стоял в Apple II, Commodore 64 и даже в ранних «Электрониках».
Тактовая частота: 1.79 МГц (NTSC) / 1.66 МГц (PAL). Для сравнения: ваш смартфон — ~2 000 000 000 Гц.
Но! NES не тормозит, потому что:

  • Нет ОС — программа работает напрямую с железом,
  • Каждый кадр — 1/60 секунды (60 FPS), и CPU обязан уложиться в это время.

→ Это как бег на короткую дистанцию: медленно, но идеально синхронизированно.

2. PPU — 2C02 (Picture Processing Unit)

Отвечает за всё, что вы видите.
Как он работает:

  • Name Table (2 КБ): карта экрана — какие тайлы (8×8 пикселей) где рисовать.
  • Pattern Table: сами тайлы (например, «голова Марио», «кирпич», «облако»).
  • Attribute Table: палитра для блоков 16×16 пикселей.
  • Sprite RAM: до 64 спрайтов (движущихся объектов), но одновременно на строке — только 8.

→ Поэтому в Contra враги «мерцают»: игра скрывает одних, чтобы показать других. Это не баг — приём оптимизации.

3. APU — Audio Processing Unit

Встроен в 2A03. Пять каналов:

КаналТипПример использования
Pulse 1Прямоугольная волнаМелодии (линия Марио)
Pulse 2Прямоугольная волнаАккомпанемент
TriangleТреугольная волнаБас, эффекты (прыжок)
NoiseШумВыстрелы, ветер, дождь
DMCPCM-сэмплыГолос в Metroid («Mother Brain»), звуковые эффекты в Mega Man 2

🎵 Попробуй: найди в YouTube «NES sound channel isolation» — послушай, как игра звучит по каналам. Удивительно, как из пяти «голосов» складывается целая симфония.


🔹 Почему экран «мерцает» и «трясётся»? Физика кадра

NES рисует экран по строчкам — 262 строки за 1/60 секунды.

  • Строки 0–240: видимая область (256×240),
  • Строки 241–261: вертикальный возврат луча (VBlank).

Именно в VBlank разрешено менять видеопамять — иначе будет screen tearing (разрыв изображения).
→ Поэтому все сложные операции (переключение уровней, расчёт врагов) — только в VBlank.

🔬 Эксперимент для старших (12+):
В эмуляторе Mesen включите «Renderer → Show scanlines». Вы увидите, как луч «рисует» экран сверху вниз. Попробуйте поставить точку останова на строке 241 — и понаблюдайте, как игра «дышит» между кадрами.


🔹 Цвета: не RGB, а NTSC-палитра

NES не генерирует RGB. Она выводит композитный видеосигнал, где цвета — результат интерференции яркости и цветности в аналоговом ТВ.
Палитра — 54 оттенка, но на разных ТВ они выглядят по-разному (см. «NTSC color bleed»).

🎨 Задание:
Возьми скриншот из Super Mario Bros.
Открой его в графическом редакторе.
Увеличь пиксельную сетку.
Найди места, где цвет «растекается» между пикселями (например, у облаков).
Это — не артефакт сжатия. Это — физика аналогового ТВ, запечатлённая в цифре.


🔹 Почему игры не «лагают», даже на слабом железе?

Потому что они написаны на ассемблере 6502, с учётом каждого такта.
Пример: в Super Mario Bros. расчёт прыжка Марио занимает < 300 тактов — это ~0.17 мс.

Разработчики Nintendo использовали:

  • Look-up tables (таблицы синусов/косинусов — чтобы не считать),
  • Fixed-point arithmetic (дробные числа как целые: 1.5 = 15, делить на 10 в конце),
  • Unrolled loops (циклы «размотаны» вручную — меньше команд перехода),
  • DMA (Direct Memory Access) — копирование спрайтов в PPU без участия CPU.

→ Это как писать стихи хайку: каждое слово — на вес золота.


🔹 А как насчёт «хаков» и неофициальных возможностей?

NES — открытая платформа (по историческим причинам: Nintendo не успела запатентовать всё). Это породило:

  • Homebrew-сообщество (сайты: nesdev.org, forums.nesdev.com),
  • Домашние картриджи (например, EverDrive-N8 — флеш-карта, в которую можно загружать ROM’ы),
  • Модификации железа:
    • RGB mod — замена видеочипа на 2C02-RGB для чистого сигнала,
    • Region-free mod — игра любого региона на любой приставке,
    • Save state mod — добавление SRAM и кнопки «сохранить состояние».

💡 Эти моды — не «взлом», а инженерное творчество. Как сборка робота из конструктора — только конструктор здесь — паяльник и схемы.


🔹 Практикум: собери «виртуальную NES» в голове

Представь, что ты — разработчик, и тебе нужно написать самую простую игру для NES: «Поймай яблоко» (яблоко падает, корзина едет внизу).

Разбей задачу на этапы, как делали в 1986 году:

  1. Проектирование видеопамяти:

    • Какие тайлы нужны? (яблоко, корзина, фон)
    • Сколько спрайтов? (1 яблоко + 1 корзина = 2 → укладываемся в лимит 64)
  2. Логика падения:

    • Координата Y яблока += 1 каждые N кадров.
    • Как избежать «дрожания»? Использовать счётчик кадров, а не delay().
  3. Столкновение:

    • Проверка: если abs(яблоко.X - корзина.X) < 8 и яблоко.Y ≈ корзина.Y → +1 очко.
    • Как оптимизировать? Не считать abs(), а использовать предварительно вычисленные границы.
  4. Звук:

    • Призыв: канал Noise → короткий «пик!»
    • Проигрыш: Triangle → нисходящая нота.

→ Это упражнение — мост к будущему: через 2 года ребёнок напишет то же самое на Python/Pygame, но уже зная, откуда берутся фреймы, спрайты и коллизии.


🔹 Задачи для Части 2

🧪 Задача 1. «Разбери кадр»

Возьми скриншот из Castlevania (любой).
Выдели:

  • Где Name Table? (фон — плитки 16×16),
  • Где спрайты? (Саймон, враги, кнут),
  • Где Attribute Table проявляется? (смена палитры — например, красные кирпичи vs серые).
⚙️ Задача 2. «Рассчитай память»

Игра Tetris (NES) весит 40 КБ.

  • Сколько банков по 16 КБ нужно, чтобы уместить код?
  • Если бы не было MMC, сколько максимум могло бы быть уровней в Tetris, если на уровень уходит 2 КБ данных?
🎵 Задача 3. «Скомпозируй звук»

Выбери 3 звука из Duck Hunt:

  • Выстрел,
  • Смех,
  • «Game Over».
    Определи, какие каналы APU использованы для каждого. Обоснуй.
🔍 Задача 4. «Найди скрытую механику»

В Super Mario Bros. есть секрет:

  • На уровне 1-2, если пройти под полом, можно попасть в «водный мир».
    Как это технически возможно?
    Подсказка: Name Table зациклен. Изменив Y-координату карты, можно «сдвинуть» viewport вниз — и увидеть то, что не должно быть видно.
    → Это не баг. Это feature, оставленная для тестирования. И позже — для игрока.

История одной карты: The Legend of Zelda

«В 1984 году Сигэру Миямото нарисовал на бумаге лабиринт. Он не знал, что делает игру. Он решал задачу: “Как дать человеку чувство открытия — без инструкций, без стрелок, без слов?”
Ответ лежал не в графике. Не в звуке. А в структуре выбора


🔹 Этап 1. Дизайн: игра как пространство возможностей

Что было в начале?

— Блокнот, карандаш, ластик.
— Идея: «Мир, где ты сам решаешь, куда идти. Где не “следуй за стрелкой”, а “что будет, если сюда?”».

Миямото и его команда (всего 4 человека на ранних этапах!) начали с физической карты — нарисованной от руки, с координатами, уровнями сложности, скрытыми проходами.

Карта была неравномерной:

  • Некоторые зоны — открыты сразу (Лес, Пустошь),
  • Другие — доступны только с определённым предметом (Лестница → пещеры, Бомба → стены),
  • Третьи — требуют знания механик (например, в Level-4 нужно пройти в зеркальном отражении — без подсказок).

→ Это не линейный сюжет, а граф состояний: узлы — локации, дуги — условия перехода (ключ, сила, знание).

🗺️ Попробуй сам:
Возьми лист в клетку. Нарисуй «остров» 16×16 клеток.
Разметь:

  • 3 входа в пещеры (▲),
  • 2 «запечатанные» стены (■),
  • 1 ключ (🔑),
  • 1 сундук (📦).
    Теперь определи:
    Какие пути станут доступны, если найти ключ? А если — лестницу?
    Это и есть дизайн-документ на уровне 1986 года.

🔹 Этап 2. Прототипирование: проверка идей «на железе»

Nintendo не ждала, пока всё будет готово.
Команда собрала грубый прототип на Famicom уже через 3 месяца:

  • Герой — квадрат 8×8 пикселей,
  • Враги — мигающие точки,
  • Стены — чёрные блоки.

Но — ключевой момент — физика уже работала:

  • Столкновения (герой не проходит сквозь стены),
  • Система жизней (3 сердца → 0 = Game Over),
  • Механика боя (если герой движется вперёд при нажатии A — выпускается «луч» вперёд).

→ Это как MVP (Minimum Viable Product) в современной разработке: не красиво — но работает. И если не работает — правят сразу, пока не ушли в сложную графику.

💡 Почему это важно для ребёнка?
Потому что учит: сначала — поведение, потом — обёртка.
Современные дети часто начинают с Unity, рисуют 3D-модель дракона… а потом выясняется, что прыжок не работает.
В Zelda — сначала был прыжок через яму (в Level-9), и только потом — анимация героя.


🔹 Этап 3. Графика: пиксели как поэзия

NES даёт на один спрайт 8×8 пикселей.
Всего — 64 спрайта одновременно, но только 8 на одну строку.

Как сделали Линка?

  • Тело: 2×2 спрайта = 16×16 пикселей,
  • Меч: отдельный спрайт, появляется при атаке,
  • Щит: ещё один спрайт — но не всегда (чтобы не превысить лимит 8/строку).

Палитра: всего 3 цвета на спрайт (плюс прозрачность).
Для Линка:

  • Тело: зелёный,
  • Волосы: светло-коричневый,
  • Кожа: бежевый.

→ Всё. Ни теней, ни градиентов.

Но — гениально — анимация создаёт объём:

  • При ходьбе — смена 2 кадров: нога вперёд / назад,
  • При прыжке — смена Y-координаты + небольшой наклон,
  • При ударе — на 1 кадр меч вылетает вперёд на 8 пикселей.

🔍 Задание:
Посмотри на спрайт Линка в Zelda (можно найти на nesdev.com/sprites).
Увеличь до 800%.
Пересчитай: сколько пикселей у него волос? А глаз?
Поймёшь: каждая точка — решение дизайнера. «Сделать глаз побольше? — Тогда не хватит места на рот».


🔹 Этап 4. Программирование: ассемблер как стихи

Вся Zelda написана на 6502 Assembly — языке, где одна строчка = одна команда процессора.
Нет функций Update(), Draw(). Есть:

; Пример: проверка столкновения с врагом
CheckCollision:
LDA player_x ; загрузить X-координату героя
SEC ; установить флаг переноса
SBC enemy_x ; вычесть X врага → результат в A
BPL PosDiff ; если ≥0 — перейти
EOR #$FF ; иначе — инвертировать (модуль)
CLC
ADC #1
PosDiff:
CMP #8 ; если |dx| < 8 → возможно столкновение
BCS NoCollide
; аналогично для Y…

→ Это не «сложный код». Это максимально честный код. Никаких скрытых вызовов. Ты видишь, сколько тактов уйдёт на проверку.

А вот как хранятся уровни:

  • Каждый подземный уровень — 256 байт данных (16×16 комнат),
  • Один байт = ID комнаты (0x01 = коридор, 0x0F = босс, 0xFF = пусто),
  • Отдельные таблицы — для ловушек, врагов, сокровищ.

💡 Сравнение:
В современной игре уровень — это .unitypackage весом 200 МБ (модели, текстуры, скрипты).
В Zelda — 256 байт + 400 байт логики.
Экономия не из бедности. Из уважения к машине — и к игроку.


🔹 Этап 5. Тестирование: люди как сенсоры

Nintendo не использовала автоматические тесты.
Они приглашали детей из соседнего двора в офис.
Давали картридж. Говорили: «Сыграйте. Не объясняем правил».

И смотрели:

  • Где застревают? → значит, логика непонятна,
  • Что пробуют первым делом? → значит, это «очевидно»,
  • Что не замечают? → значит, нужно визуальное подкрепление.

В Zelda:

  • Сначала не было подсказки про бомбы и стены. Дети не находили секреты.
  • Добавили: при близком приближении к слабой стене — лёгкая вибрация джойстика (на NES это делалось через быстрое переключение спрайтов фона).
    → Это — хаптическая подсказка. Без слов. Без интерфейса. Только ощущение.

🔹 Этап 6. Выпуск: почему в коробке — карта и ручка?

В 1987 году в коробку с Zelda клали:

  • Картридж,
  • Бумажную карту мира (на листе А2),
  • Ручку.

Почему?
— Потому что игра предполагает запись:

  • Где ты нашёл ключ?
  • Какой порядок уровней?
  • Где спрятан меч?

→ Это внешняя память игрока. Как блокнот программиста.

Современные игры дают внутреннюю карту (мини-карта, маркеры).
Zelda давала внешнюю — и тем самым учила: знание — не в машине. В тебе.


🔹 А что внутри картриджа? Вскроем Zelda (виртуально)

Вот как устроен картридж The Legend of Zelda (PRG-ROM + CHR-ROM + Battery-backed SRAM):

  • MMC1 — первый Memory Management Controller от Nintendo.
    Позволяет:

    • Переключать 16-КБ банки PRG-ROM (всего 16 банков),
    • Переключать 4-КБ банки CHR-ROM (32 банка),
    • Включать/выключать запись в SRAM (чтобы батарейка не села).
  • Save-файл — 8 КБ = 512 байта на 16 слотов.
    Но в Zelda используется только 1 слот → остальное — для будущих игр (например, Metroid).

🛠️ Интересный факт:
В Zelda нет «Game Over». Если умираешь — теряешь половину денег и возрождаешься у входа в подземелье.
Почему? Команда решила: «Смерть — не наказание. Это информация».
Ты умер — значит, надо было взять больше сердец. Или бомб. Или подумать.


🔹 Задачи для Части 3

🧩 Задача 1. «Спроектируй комнату»

Придумай комнату для подземелья (8×8 экранов по 256×240).
Опиши:

  • 1 головоломку (например, «нужно нажать 2 кнопки одновременно»),
  • 1 скрытый проход (как его обнаружить без подсказок?),
  • 1 врага и его паттерн (как он двигается? когда атакует?).
🖊️ Задача 2. «Напиши инструкцию без слов»

Возьми механику из Lode Runner: «Копать под врагом — чтобы он упал».
Придумай, как объяснить это новичку только через дизайн уровня:

  • Где поставить врага?
  • Где — лестницу?
  • Где — «пробный» участок земли?
🧠 Задача 3. «Разгадай код поведения»

В Zelda враг Stalfos (скелет) ведёт себя так:

  • Идёт прямо → если встречает стену, поворачивает направо,
  • Если видит Линка — бежит к нему,
  • При ударе — разваливается, потом собирается заново.

Опиши его поведение как конечный автомат (состояния + переходы).
Например:
[Патрулирует] --видит Линка→ [Преследует] --удар→ [Разрушен] --таймер→ [Собирается]

🌍 Задача 4. «Создай внешнюю память»

Нарисуй «карту мира» для своей вымышленной игры (формат — лист А4).
Добавь:

  • 3 секрета (обозначь символами, понятными только тебе),
  • 1 «ложный след» (например, пещера, которая никуда не ведёт),
  • 1 подсказку для новичка («если не можешь пройти — посмотри вверх»).

🛠️ Часть 5. Делаем свою игру для NES

От «Hello, World!» — к собственной аркаде

«Программировать NES — всё равно что писать стихи на языке, где каждая строчка должна уместиться в 29 тактов процессора. Это не ограничение. Это дисциплина. И в ней — свобода».


🔹 Почему 6502? Почему не Python?

Потому что:

  • 6502 — один из самых простых и логичных процессоров в истории (56 команд, 3 регистра, понятная память),
  • Он учит прямому диалогу с машиной: нет garbage collector’а, нет JIT’а — есть ты, процессор и такты,
  • Современные микроконтроллеры (например, в Arduino) используют похожие принципы — так что это не «ретро», а фундамент.

Но — важно: мы не будем писать всё вручную на ассемблере. Используем:
CC65 — кросскомпилятор C → 6502 (да, на NES можно писать на C!),
NES Screen Tool — редактор тайлов и карт экрана,
FamiStudio — трекер для музыки в стиле NES,
Mesen — отладочный эмулятор с мощными инструментами (просмотр памяти, точки останова, профилирование).

Все — бесплатны, open-source, работают на Windows/macOS/Linux.


🔹 Шаг 0. Подготовка среды (5 минут)

Что установить:

  1. CC65https://cc65.github.io
    git clone https://github.com/cc65/cc65 && make && sudo make install
    (для Windows — есть .exe-установщик)

  2. NES Screen Toolhttps://www.romhacking.net/utilities/765/
    → GUI-программа для рисования тайлов и сборки фонов.

  3. Mesenhttps://www.mesen.ca
    → эмулятор с отладкой. Включите «Tools → Debugger» заранее.

  4. Шаблон проектаnes-template (я подготовил упрощённую версию):

    git clone https://github.com/nesdev/nes-template-minimal
    cd nes-template-minimal

💡 Для детей 10–12 лет: можно использовать NESMaker (платный, но визуальный), но он скрывает логику. Мы идём глубже — с пониманием.


🔹 Шаг 1. «Hello, World!» для NES: мигающий спрайт

NES не умеет выводить текст «из коробки». Поэтому первый шаг — вывести спрайт (движущийся объект).

Структура минимальной программы:

// main.c
#include <nes.h>

// Спрайтовая память (OAM — Object Attribute Memory)
unsigned char spr[256];

void main(void) {
// 1. Ожидаем VBlank (начало кадра)
ppu_off(); // выключаем рендер
ppu_wait_nmi(); // ждём VBlank

// 2. Загружаем палитру спрайтов
pal_spr(0x0F, 0x10, 0x20, 0x30); // цвета: чёрный, синий, светло-синий, белый

// 3. Рисуем один спрайт (звезда 8x8)
spr[0] = 64; // Y = 64
spr[1] = 128; // тайл №2 (пусть это будет звезда)
spr[2] = 0x00; // палитра 0, без флипа
spr[3] = 100; // X = 100

// 4. Копируем спрайты в PPU через DMA
oam_dma(spr);

// 5. Включаем рендер
ppu_on_all();

// 6. Бесконечный цикл — игра идёт!
while (1) {
ppu_wait_nmi(); // синхронизация с кадром
// Здесь будет логика движения
}
}

Что происходит «под капотом»:

  • ppu_wait_nmi() — ожидание сигнала от PPU: «Я закончил рисовать кадр, можно обновлять память»,
  • oam_dma(spr)прямой доступ к памяти: копирует 256 байт спрайтов за 512 тактов (быстрее, чем по байту через CPU),
  • ppu_on_all() — включает фон и спрайты.

🔬 Эксперимент в Mesen:
Запустите программу. Откройте Debugger → PPU Viewer.
Увидите:

  • Pattern Table — ваши тайлы,
  • Name Table — пустой фон,
  • Sprite 0 — звезда в позиции (100, 64).
    Измените spr[0] = 65 → перекомпилируйте → звезда опустится на 1 пиксель.

🔹 Шаг 2. Делаем звезду подвижной

Добавим управление джойстиком:

#include <joystick.h>

void main(void) {
// ... инициализация (как выше) ...

unsigned char x = 100, y = 64;

while (1) {
ppu_wait_nmi();

joy_poll(); // опрос джойстика

// Чтение состояния кнопок
if (joy_read(0) & JOY_RIGHT) x++;
if (joy_read(0) & JOY_LEFT) x--;
if (joy_read(0) & JOY_DOWN) y++;
if (joy_read(0) & JOY_UP) y--;

// Ограничение по экрану
if (x < 8) x = 8;
if (x > 240) x = 240;
if (y < 8) y = 8;
if (y > 224) y = 224;

// Обновляем спрайт
spr[0] = y;
spr[3] = x;
oam_dma(spr);
}
}

→ Теперь звезда управляется джойстиком.
Но! Она «дрожит». Почему?
— Потому что мы обновляем спрайты каждый кадр, но не сбрасываем фон.
Решение: добавить статический фон (например, звёздное небо).


🔹 Шаг 3. Фон: рисуем «небо» в NES Screen Tool

  1. Откройте NES Screen Tool.
  2. Создайте новый проект:
    • Tileset: 256 тайлов (4×64),
    • Name Table: 32×30 (стандартный размер).
  3. Нарисуйте:
    • Тайл №0: чёрный фон,
    • Тайл №1: маленькая звезда (2–3 белых пикселя),
    • Заполните Name Table случайными звёздами.
  4. Экспортируйте как:
    • background.chr (тайлы),
    • background.map (карта).

В коде добавьте загрузку фона:

#include "background.map"  // сгенерирован автоматически

void vram_put(unsigned int addr, unsigned char n) {
*((unsigned char*)0x2006) = addr >> 8;
*((unsigned char*)0x2006) = addr & 0xFF;
*((unsigned char*)0x2007) = n;
}

void load_background(void) {
for (int i = 0; i < 960; i++) { // 32*30
vram_put(0x2000 + i, background_map[i]);
}
}

→ Теперь фон статичен, а звезда — плавно движется.


🔹 Шаг 4. Добавляем цель: «ловим звезду»

Пусть по экрану летает вторая звезда — случайно.
Если игрок касается её — +1 очко.

unsigned char target_x = 32, target_y = 32;
unsigned char score = 0;

void move_target(void) {
target_x += (rand() % 3) - 1; // -1, 0, +1
target_y += (rand() % 3) - 1;

// Ограничение
if (target_x < 16) target_x = 16;
if (target_x > 230) target_x = 230;
if (target_y < 16) target_y = 16;
if (target_y > 220) target_y = 220;
}

char check_collision(void) {
return (abs(x - target_x) < 8) && (abs(y - target_y) < 8);
}

→ Добавьте второй спрайт в spr[4..7], обновляйте его в цикле.
→ При коллизии: score++, target_x = rand() % 240, target_y = rand() % 200.


🔹 Шаг 5. Звук: «пик!» при поимке

Используем встроенный APU через CC65:

#include <apu.h>

void play_pick_sound(void) {
// Канал Noise: короткий щелчок
apu_write(0x400C, 0x28); // длина=5, envelope=8
apu_write(0x400E, 0x1F); // частота шума
apu_write(0x400F, 0x08); // запуск
}

Вызывайте при коллизии → звук «пик!».

Для мелодии — можно использовать FamiStudio → экспортировать как music.s → подключить в проект.


🔹 Шаг 6. Сохранение: рекорд в SRAM

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

#define SRAM_ADDR 0x6000

void save_highscore(unsigned char hs) {
*((unsigned char*)SRAM_ADDR) = hs;
}

unsigned char load_highscore(void) {
return *((unsigned char*)SRAM_ADDR);
}

→ В начале: highscore = load_highscore();
→ При новом рекорде: if (score > highscore) { highscore = score; save_highscore(score); }

⚠️ Важно: в эмуляторе (Mesen) нужно включить «Battery Save» в настройках ROM.


🔹 Что у нас получилось?

Мини-игра «Catch the Star», в которой:

  • Есть фон (тайлы),
  • Есть управляемый спрайт,
  • Есть ИИ (цель),
  • Есть физика (коллизии),
  • Есть звук,
  • Есть сохранение.

Это — полноценный игровой цикл, идентичный тому, что был в Duck Hunt или Excitebike.


🔹 Расширения (для продвинутых)

УровеньЗадачаНавык
★☆☆Добавить таймер (30 сек на уровень)Работа с NMI-счётчиком
★★☆Сделать «звезду-босса»: уворачивается от игрокаПростой ИИ (состояния)
★★★Добавить фоновую музыку из FamiStudioРабота с DMC и NSF
★★★★Сделать 2 уровня: «Луна» и «Галактика»Банки памяти (MMC3)

🔹 Готовый проект для старта

Я подготовил минимальный репозиторий-заготовку (можно клонировать и сразу компилировать):
🔗 https://github.com/timurtagirov/nes-catch-the-star
Содержит:

  • Makefile для CC65,
  • Готовые тайлы (звезда, фон),
  • Базовая структура с движением и коллизией,
  • Инструкция по сборке (make && make run).

🔹 Почему это важно?

Потому что ребёнок получает:
Чувство агентности: «Я сделал то, что работает на настоящей архитектуре»,
Понимание слоёв: от пикселя → до машинного кода,
Мост в будущее: через 2 года он напишет то же на Python/Pygame — но уже зная, откуда берутся screen.blit() и clock.tick(60).


🔹 Задачи для Части 5

🧪 Задача 1. «Собери и запусти»

Скачайте nes-template-minimal, соберите (make), запустите в Mesen.
Измените:

  • Цвет спрайта (через pal_spr),
  • Начальную позицию (x, y),
  • Скорость (добавьте x += 2 вместо x++).
    Сделайте скриншот «до» и «после».
🎮 Задача 2. «Добавь врага»

Создайте второго спрайта — «астероид», который:

  • Движется слева направо,
  • Исчезает при выходе за экран,
  • При столкновении — Game Over (остановка цикла, надпись «GAME OVER» через фон).
🎵 Задача 3. «Запиши мелодию»

В FamiStudio создайте 8-тактную мелодию (например, первые ноты из Super Mario).
Экспортируйте как music.s, подключите в проект.
Сделайте, чтобы музыка играла только в меню.

📦 Задача 4. «Собери картридж»

Используйте NES Screen Tool, чтобы:

  • Нарисовать тайл «сердце» (жизнь),
  • Сделать фон «леса» (для следующей игры — Adventure Island),
  • Экспортировать .chr и подключить в код.