Как Pizza Tycoon имитировала дорожный трафик на процессоре с частотой 25 МГц

Как Pizza Tycoon имитировала дорожный трафик на процессоре с частотой 25 МГц

Прокомментировать Просмотры: 6

Предыстория

Я занимаюсь Pizza Legacy — open-source ремейком классической игры 1994 года Pizza Tycoon для DOS. Одной из ключевых особенностей был вид на город с активным трафиком: два-три десятка спрайтов машин, которые перемещались по дорогам, формировали заторы на светофорах и создавали иллюзию оживленного мегаполиса. Несмотря на мелкие огрехи симуляции, вроде «просачивания» машин друг сквозь друга, это выглядело крайне убедительно — и всё это работало на процессорах Intel 386 с тактовой частотой 25 МГц.

Разработка проекта стартовала в 2010 году. Я начал именно с этой системы, но на то, чтобы заставить машины двигаться «как надо», у меня ушло 14 лет. Множество попыток заканчивались крахом из-за чрезмерной усложненности архитектуры.

В 2017 году я внедрил систему, где каждый тайл отслеживал занятость, а автомобиль запрашивал «разрешение» на въезд, резервируя слот. Это превратилось в избыточную систему блокировок ради перемещения пары пикселей. Машины постоянно боролись за синхронизацию с тайлами.

Меня не покидал вопрос: если оригинал 90-х годов справлялся с этим на 25 МГц, почему мой код выходит таким перегруженным?

В итоге я решился на глубокое погружение в ассемблерный код оригинала. На помощь пришли LLM, которые отлично справились с анализом низкоуровневых инструкций. Разгадка оказалась проста: я подходил к задаче с багажом современных знаний — графами сцен, поиском путей и проверкой коллизий, предполагая, что мне нужны мощные вычислительные ресурсы.

Устройство городов

Взглянем на структуру игрового мира:

Top down view of a city in Pizza Tycoon

Города состоят из сетки 160 на 120 тайлов. Все элементы ландшафта берутся из файла landsym.vga:

All possible map tiles in Pizza Tycoon (landsym.vga)
Тайловый набор Pizza Tycoon (landsym.vga). Индекс 0x54 соответствует конкретному сегменту крыши.

Дорожная сеть

Главный инсайт: машинам не нужно знать пункт назначения. Направление движения «зашито» в сам тип тайла. Например, тайл дороги 0x16 жестко задает движение слева направо. Другие ID определяют движение в обратном направлении или по вертикали.

Город по факту представляет собой сеть дорог с односторонним движением. Машине достаточно определить, на каком тайле она находится, чтобы понять, куда двигаться дальше. На поворотах (например, CORNER_SW) логика проста: бросается жребий, и с вероятностью 50% автомобиль либо продолжает движение, либо поворачивает. Чтобы избежать неестественного поведения, введено ограничение: два поворота налево подряд запрещены.

Small cut-out of the city map with a grid super-imposed and arrows on various tiles to indicate valid vehicle directions.
Допустимые направления движения, размеченные на карте.

Перемещение и логика тайлов

Машины перемещаются с шагом в один пиксель на кадр. В дополнение к координатам есть внутренний счетчик (от 16 до 0), отвечающий за логику перекрестков. Как только счетчик достигает нуля, машина определяет новый тайл и направление. Поскольку каждый тайл занимает 16 пикселей, «тяжелая» логика выполняется в 16 раз реже, чем базовое смещение по оси X или Y. Начальные значения счетчика рандомизируются при создании авто, чтобы избежать «пакетного» обновления всех спрайтов одновременно.

Оптимизация коллизий

Вместо сложного поиска путей используется примитивная проверка пар. Для каждой машины проверяется список остальных: если путь прегражден — машина ждет 10 циклов. Ключ к эффективности — «быстрый выход»: если машины движутся в разных направлениях на разных полосах (например, восток и запад), проверка прерывается моментально, еще до вычислений координат. Благодаря этому даже при наличии 25 машин ресурсоемкие вычисления почти не требуются.

Жизненный цикл машин

Игра сканирует дорожные тайлы и с определенной вероятностью «спавнит» транспорт. Машины, покинувшие границы экрана, перерождаются в новых точках (другого цвета, с другим направлением), что избавляет от необходимости глобального менеджмента объектов. Это простое циклическое обновление позволяет поддерживать жизнь на карте без нагрузки на CPU.

Итог

Я осознал, что пытался «переинженерить» задачу. Машинам не нужен интеллект — им нужна лишь локальная инструкция, диктуемая тайлом. Код, написанный мною по мотивам анализа оригинала, получился лаконичным и эффективным. Ознакомиться с реализацией можно в методе decide_desired_direction файла Car.cpp.

 

Источник

Поделиться:

Похожие статьи

Поиск по играм, новостям и статьям…

Введите не менее двух символов

Введите не менее двух символов