В начале 2021 года я взялся написать игру для компьютера, которому тогда было уже 40 лет — речь о его благородии Commodore 64. Релиз игры Cab Hustle наконец состоялся осенью 2022 года, после того, как я в течение нескольких месяцев эпизодически добивался по ней некоторого прогресса. В начале 2023 года я также выпустил версию этой игры для ПК.

В этом посте я кое-что поведаю вам о том, как состоялась эта игра. Мы отправимся на экскурсию по моим воспоминаниям, я расскажу, что меня побудило взяться за этот проект, чему я за ним научился, поделюсь некоторыми мыслями о разработке игр как таковой, а также затрону феномен пиратского копирования 8-битного софта в XXI веке.

Скриншот игры, выполняемой в эмуляторе
Скриншот игры, выполняемой в эмуляторе

Итак, о чём же эта игра? В сущности, игрок сидит за штурвалом небольшого космического корабля и проходит на нём карту. Ему попадаются посадочные платформы, где он за деньги подхватывает пассажиров. Важнейшая часть игровой физики заключается в том, что движение корабля обеспечивается микродвигателями, но при этом он испытывает тяготение и может «нырять». Мне попадалась в онлайне запись, где кто-то назвал такой двигатель «подруливающим» — как мне представляется, меткая характеристика. Прохождение этой игры выложено на YouTube, а также вы можете посмотреть подробные инструкции.

Моя мотивация и экскурсия по аллее воспоминаний

Зачем разрабатывать игры для C64, когда минула уже четверть XXI века? Компьютер C64 появился у меня в 1980-е, и, как только я впервые включил его, мне сразу запала идея писать для него игры. Именно на этом компьютере я учился программировать, сначала на BASIC, а потом и с элементами какого‑то зачаточного ассемблера.

Код BASIC на C64 получается медленным, и возможности его ограничены. Практически не поддерживается структурное программирование, а любые операции сложнее ввода и вывода символов требуют непосредственно взаимодействовать с железом и записывать информацию прямо в регистры, отображаемые на память. В BASIC не предусмотрено команд для работы со звуками или продвинутой графикой, предоставляемыми машиной. Рабочий способ создать спрайт — нарисовать пиксели на странице из тетради в клеточку, перевести группы по 8 пикселей каждая из двоичной системы в десятичную, а затем вручную записать эти значения в инструкции DATA, содержащиеся в вашей программе на BASIC. Чтобы спрайт отобразился на экране, программа с��ачала записывает эти значения в память (при помощи команд POKE), а потом должна поставить указатель спрайта на эти данные, записав их в оперативную память дисплея. Наконец, нужно установить значения в различных регистрах VIC‑II, оживляющих спрайт, задать для него цвет и координаты.

Тогда я прибегнул к ассемблеру, воспользовавшись монитором из Final Cartridge III — были такие изящные кассеты, вставлявшиеся в порт расширения и позволявшие остановить машину и повозиться с её памятью. Встроенный ассемблер не слишком удобен в обращении, но на нём было вполне удобно писать простые процедуры. Были инструменты и получше, но не всё так просто достать или легко купить, когда ты старшеклассник. Тогда как раз начиналась 16-разрядная эра. Я кое-что скопил и, продав свой C64, смог переехать на новую платформу: Amiga.

Минуло ещё лет двадцать, и я набрёл на блог Dustlayer. В нём описана легко настраиваемая кросс-ассемблерная среда, а вдобавок есть масса документации о том, как приступить к разработке под C64. Вооружившись эмулятором, я написал несколько небольших программ, заново познакомившись с системой. Прошло ещё несколько лет, нагрянула пандемия, и я, воспользовавшись вынужденным домоседством, нашёл время,  чтобы собрать новый C64. Теперь, располагая достаточным временем, а также имея под рукой рабочую машину, я, наконец, собрался довести до завершения полноценную игру для C64.

Концепция

Во-первых, зачем вообще писать игру? Я хотел, чтобы готовый продукт конкурентоспособно выглядел на фоне софта, который считался современным в эпоху активного использования этой машины. Таким образом, я задал себе значительно более высокую планку, чем при допандемийных экспериментах с Dustlayer, но определённо не претендовал на лавры таких топовых игр того времени как «Sam’s Journey». Кроме того, я не планировал извлечь из этого проекта какую-либо коммерческую выгоду, но планировал довести его до состояния, в котором в эту игру смогут поиграть несколько человек.

Что касается темы игры, я стремился ухватить привлекательность, присущую игре TurboRaketti для Amiga, там нужно на огромной скорости носиться по туннелям. Игра TurboRaketti идёт с разделённым экраном, рассчитана на двух пользователей и изображает ближний бой, где игроки летают по пещерам на треугольных космических кораблях и перестреливаются друг с другом. На освоение игры требуется некоторое время, поначалу погибаешь в основном от того, что врезался в стену, а не потому, что тебя подстрелил оппонент. Но когда подсядешь на эту игру — она невероятно весёлая.

TurboRaketti (Amiga)
TurboRaketti (Amiga)

Но в моей версии для C64 я не хотел сразу начинать с игры на двоих. При необходимости учесть парный режим тестирование в целом усложняется, причём в моём случае противоречит цели «смогут поиграть несколько человек». Ведь в наши дни играть вдвоём на двух джойстиках одной приставки — практика гораздо более редкая, чем бывало в 80-е. Можно реализовать возможность, чтобы компьютер играл против тебя, но я счёл, что для игры-прототипа такое усложнение было бы чрезмерным.

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

Затем мне вспомнилась механика перевозки пассажиров из игры «Space Taxi». Там в механике полёта учитывается гравитация, но, в отличие от  TurboRaketti, корабль не вращается. В результате управлять кораблём легче, но игра получается не такой динамичной. Чтобы набрать темп, я реализовал примерно такую же схему, как в «Crazy Taxi» — более свежей 3D-аркаде, цель игрока в которой также заключается в перевозке пассажиров. В  Crazy Taxi вы круглосуточно перевозите пассажиров и получаете бонусы за скорость рейсов. Кроме того, в Crazy Taxi есть особенно приятный мне исследовательский элемент. Чем дольше тебе удаётся оставаться в игре, тем подробнее успеваешь посмотреть город. Поэтому мне хотелось создать более крупную карту, чем предлагаемые в Space Taxi пространства на один экран, так, чтобы игрок не смог сразу увидеть карту за первые пару прогонов, и у него был повод попробовать поиграть ещё.

Инструментарий

При работе с версией для C64 я пользуюсь cc65, который предо��тавляется сразу с компилятором C (одноимённым всему комплекту, cc65) и ассемблером (ca65). Код игры написан в основном на C, поэтому программировать для C64 гораздо приятнее, чем работать на чистом ассемблере, а экспериментировать получается гораздо быстрее. Например, если приходится иметь дело с целыми числами, то даже при работе с игровой физикой, где такие числа крупнее 8 разрядов, процесс протекает бесшовно.

Сгенерированный код на C в основном не оптимизированный, но для создаваемой игры он достаточно быстрый. Некоторые части кода, в которых критично время, написаны на ассемблере. Особенно примечательно, насколько быстра должна быть та процедура, которая обновляет экран, когда корабль влетает в новую локацию. Поэтому она написана на простом ассемблере. Применяемая в игре процедура сервиса прерывания (ISR) также написана на ассемблере. Эта процедура обрабатывает конкретные линии развёртки в каждом отрисовываемом кадре, например, чтобы отключить спрайты непосредственно перед выводом строки состояния на экран или для активации процедуры воспроизведения музыки, которая раз в каждые 16-20 миллисекунд записывает новые данные в звуковой чип. Такая ISR не только обязана быть быстрой, но и не может затирать регистры. В  Cc65 применяются различные псевдорегистры, размещаемые в памяти, и при вызове любой нетривиальной функции на C они бы затирались. Поэтому почти вся трудозатратная работа легче всего выполняется на ассемблере.

При помощи Aseprite я создал спрайты для корабля и для взрыва. Конвейер ассетов реализован на Python и считывает изображения спрайтов в формате PNG, применяет необходимые преобразования и подаёт данные изображения на вывод в виде массивов на C, которые не составляет труда включить в код.

Помещения и персонажа я создал при помощи программы Charpad для Windows, которая продаётся на Itch.io и предназначена конкретно для этой цели. Затем экспортированные изображения преобразуются при помощи Python. Например, имена символов и экспорты переписываются таким образом, чтобы их мог прочитать ассемблер ca65. Кроме того, код Python при упаковке данных о локациях использует для архивации zlib. На стороне C64 данные разархивируются при помощи специальной процедуры, оптимизированной под 6502, особенно удобной тем, что вместе с ней предоставляются библиотеки для cc65.

В качестве титульного трека я воспользовался GoatTracker. Сна��ала я попробовал DefleMask, но в этой программе у меня не получилось экспортировать SID-файл, который успешно проигрывался на C64. Кроме того, файлы, которые в результате экспорта даёт DefleMask, получаются значительно крупнее.

На последнем этапе сборки программы её нужно как следует ужать при помощи pucrunch. Операция функционально аналогична сжатию исполняемого файла на современной машине, скажем, при помощи UPX. Pucrunch сжимает программу во время сборки и добавляет к ней декомпрессор-заглушку. После загрузки на C64 полноценная программа распаковывается из заархивированных данных и попадает прямо в память.

Компиляция для ПК

Справившись с версией для C64, я заинтересовался, насколько сложно будет собрать из того же дерева исходников версию для ПК — так почему бы не попытаться. На ПК в основном используется всё тот же код на C, что и в C64 — требуется внести в него лишь незначительные изменения. Фрагменты на ассемблере требовалось заменить, но на современной машине на их месте достаточно быстро работает даже неэффективный, наскоро написанный в качестве замены код на C.

Для компиляции кода я воспользовался MinGW-w64 под Linux. На моём компьютере с Windows сборочные инструменты работают на базе подсистемы Windows для Linux (WSL). Я предпочитаю работать с инструментарием, основанным на Linux, а не прибегать к нативным инструментам  Windows (таким как Visual Studio), поэтому и собрал такую конфигурацию, рассчитанную на кросс-компиляцию.

В версии для ПК используем SDL2 для обработки графики, звуков и пользовательского ввода. Графика и пользовательский ввод реализуются как неглубокая эмуляция аппаратных средств C64, так, чтобы код для Cab Hustle на C нашёл в эмулированной памяти всё, что ему нужно (массив на 64K байт).

С другой стороны, звук для ПК целиком пишется заново, чтобы нам не приходилось самим реализовывать эмуляцию звукового аппаратного обеспечения C64. В качестве заглавного трека воссоздаём версию GoatTracker при помощи Renoise. Вывод сжимаем при помощи Ogg Vorbis и линкуем в двоичный файл. Звуковые эффекты создавались при помощи Supercollider, а потом также сжимались с использованием Ogg Vorbis. В версии игры для ПК для воспроизведения этих звуковых файлов используется SDL Mixer — благодаря этому нам не приходится манипулировать звуковыми аппаратными регистрами как в версии для C64.

В сторону отмечу, что некоторые вендоры AV поначалу помечали получающийся в результате двоичный файл как подозрительный. Очевидно, необычный инструментарий в сочетании со столь высокой энтропией в сегменте данных (большая часть информации в котором приходилась на сжатый при помощи Ogg Vorbis заглавный трек) слишком смущал некоторые движки.

Эпизодический прогресс

Первый коммит в Cab Hustle датируется ещё апрелем 2021 года, и с тех пор я спорадически (нечасто) допиливал игру, пока не довёл её до первого релиза в октябре 2022 года. В основном я выделял на эту работу несколько часов в каждый второй уикенд. По мере прогресса в разработке игры, я также написал о ней несколько постов в блоге и опубликовал пару видеороликов на YouTube.

Аудитория у моего канала на YouTube была небольшая. Тем не менее, к моему немалому удивлению, другие пользователи YouTube подхватывали мои ролики, делая видеоблоги о готовящихся играх для C64. Оливье из Commodore 64 Mania даже помог погонять игру на оригинальном железе для C64 — до этого пробовал её лишь на эмуляторе и на моём Ultimate 64Merci pour ton aide avec le jeu, Olivier!

Cab Hustle на оригинальном C64, использующий настоящий CRT (источник фото: Commodore 64 Mania)
Cab Hustle на оригинальном C64, использующий настоящий CRT (источник фото: Commodore 64 Mania)

Шёл июль, когда на YouTube ко мне обратился Jazzcat, поинтересов��вшись, будет ли игра готова к осени, чтобы её можно было включить на диск-приложение к номеру журнала Zzap! 64, выход которого планировался в сентябре-октябре. Тогда мне пришлось, ух, поторопиться, чтобы в срок довести игру до логического завершения.

Релиз

В итоге игра действительно вышла в эксклюзивной версии на диске, прилагаемом к сентябрьскому/октябрьскому номеру журнала Zzap! 64. Месяцем позже я выпустил слегка обновлённую версию игры и также выложил её для бесплатного скачивания на Itch.io. Далее я дорабатывал версию, портированную на ПК, которая вышла на Itch.io ещё три месяца спустя.

Как приняли игру

Игра получила смешанные отклики. В предвкушении релиза в сообществе царило некоторое воодушевление по поводу современной версии Space Taxi — оно легко считывалось из комментариев на форумах или на YouTube. Я знал, что игра сложна, а кривая обучения при освоении её интерфейса довольно крутая. После того, как игра вышла, именно за это её и критиковали в первую очередь — слишком сложная. Ребята, которым нравятся гравитационные игры, быстро её освоили, но большинству остальных пользователей просто не хотелось тратить время и учиться управлению моим кораблём.  

На сайте RetroGamerNation игра получила рейтинг 57% (это значение получилось в результате медленного снижения после первых результатов в 67%). А в ноябрьском/декабрьском номере Zzap! 64 игра набрала 65%. Рецензенты ставили высокие баллы за внешние данные, но отмечали сложность игры, связанную не только с искусством управления кораблём, но и с жёстким ограничением времени на уровень. 

Часто попадались отзывы, в которых мою игру описывали как клон Space Taxi, критикуя при этом элементы, добавленные из Crazy Taxi (жёсткий тайминг) и из TurboRaketti (игровую физику и элементы управления). И в этом самая суть проблемы — сообщество C64 мечтало получить клон Space Taxi, а я, в свою очередь, стремился написать игру, в которой был бы схвачен азарт полёта через пещеры, свойственный TurboRaketti, и треугольная форма корабля была данью уважения этой игре.

Другие отзывы были связаны с разными недостающими фичами, в частности, с малым количеством уровней, внутриигровой музыкой, звуковыми эффектами, разными уровнями сложности и т.д. Я хотел выдать минимально жизнеспособный продукт (MVP) и, исходя из таких приоритетов, было полезно поставить в качестве дедлайна «успеть к изготовлению диска к новому номеру Zzap! 64», а значит — и отказаться от многих идей, которые могли бы добавить игре лоска и разнообразия.

Ещё одно наблюдение: лишь немногие игроки читали инструкции, пренебрегая и кратким текстом об игре, и его видеоверсией на YouTube. С одной стороны, это явно видно по статистике посещения этих страниц. С другой стороны, именно в этих источниках разобраны вещи, которые, как оказалось, раздражали многих пользователей (например, касающиеся препятствий в следующей локации). В данном случае полезно иметь в виду, каковы ожидания современных геймеров от новых игр: все концепции следует делать более самоочевидными, либо какими-то другими способами объяснять внутри игры.

В целом, мне показалось, что именно обзор от Zzap! 64 получился наиболее справедливым. Речь шла о моей первой игре для этой платформы, которую я довёл до состояния минимально жизнеспособного продукта и немного отшлифовал. В целом я доволен результатом. Более того, я не ожидал, что игра будет пользоваться таким вниманием в печати, на YouTube, на форумах, на Reddit, в подкастах или в блогах.

Взломаем?!

Ещё несколько слов о внимании: я не ожидал, что сообщество взломщиков C64 по-прежнему столь активное. Всего через несколько часов после релиза на CSDB появились пиратские версии. В этих версиях в игры добавлялись такие детали: инструкции, учебный режи�� (бесконечное топливо, бесконечное время) и, конечно же, взлом-демки.

Более того, вводную музыку порезали и отправили в коллекцию High Voltage SID, поэтому теперь её можно послушать онлайн.

Как скачивали игру: цифры

Исходно я планировал, чтобы в игру поиграли хотя бы несколько человек? А как получилось? Через Zzap! 64 пришло несколько тысяч человек (но не каждый, купивший журнал с диском в самом деле сыграет в игру). На Itch.io я зафиксировал чуть выше 330 загрузок. Кроме того, были ещё и взломанные версии на CSDB, которые, по-видимому, скачали в количестве не менее 760 экземпляров — судя по приведённой там статистике. В любом случае, более чем приличные показатели для версии под C64.

Правда, версия для ПК не пользовалась вниманием. Её скачали на Itch.io менее 20 раз. На рынке игр для ПК конкуренция гораздо выше, и пользователи ПК даже не посмотрят в сторону клона игры под C64, доведённого до стадии MVP.

Я сам, играя в эту игру, предпочитаю версию для ПК, которая запускается гораздо быстрее, чем версия для C64 в эмуляторе. При этом и сам игровой процесс гораздо симпатичнее. Первоначально в версии для C64 наблюдалась небольшая задержка, когда для обновления картинки на экране поступают в несжатом виде новые данные о локациях. На современном компьютере разархивирование примерно 250 происходит без единого писка.

Какой урок можно из этого извлечь? Если вы хотите написать игру, на которую действительно обратят внимание, в которую будут играть, и по которой поступит полезная обратная связь, то игра для C64 — отличный вариант для начала. Ваши возможности в данном случае будут ограничены, но преодолевать эти преграды – половина удовольствия от работы. А преодоление ограничений только поможет вам вырасти конкурентоспособным разработчиком-одиночкой или занять сильную позицию в небольшой команде.