[Перевод] Как в 1995 году писали игры для Sega Saturn
Это документ, написанный мной в 1995 году, когда я работал над первой игрой студии Neversoft: Skeleton Warriors. Это была первая игра, в которой я не использовал язык ассемблера 68K.
Комплект разработчика (dev kit) («Small Box» и ICE) стоит справа от меня. Фото сделано примерно в то время.
Состояние игры
В представленном ниже документе вкратце описывается состояние кода Skeleton Warriors для Sega Saturn, а также упоминаются некоторые из множества аспектов, которые нужно было ещё сделать.
Также он позволил мне оценить печальное состояние этого кода, и, надеюсь, заставил меня взяться за ум. Документ нужен был, чтобы ускорить Дэну, Кену и Джеймсу знакомство с уже готовым кодом, объяснить им назначение каждого модуля и взаимодействие между ними.
GOV и . Также я немного рассказываю о встраивании данных (файлы . GOB) в программу, и о том, что мы будем делать в будущем.
Оборудование для разработки
Наша целевая платформа — Sega Saturn, имеющая два Risc-микропроцессора SH2 и один 68000. Пока мы используем только основной процессор Master SH2, вспомогательный slave SH2 будет использоваться, когда мы разберёмся, как это сделать. 68000 применяется для управления звуковым чипом, нам не пришлось писать для него код, потому что он будет использовать предоставленную Sega звуковую библиотеку.
Программа почти целиком написана на чистом C. Мы используем компилятор GNU SH2 для получения выходного ассемблера SH2. В коде есть несколько модулей SH2, в которых в основном находятся исключительно данные. Пока я не написал ничего значимого на SH2.
Это не стандартная система разработки Sega, однако она все, кто с ней работал, считают её лучшей. В качестве системы разработки мы пользуемся PsyQ. Основная часть поставляемых Sega примеров кода должна работать в системе разработки SNASM, но легко конвертируется под PsyQ. Альтернативой ей является SNASM, созданная принадлежащей Sega студии Cross Products.
Исходники компилируются на PC и скачиватся на Saturn, где и запускается программа. Система PsyQ состоит из платы интерфейса SCSI, которая устанавливается в PC, картриджа, вставляемого в Saturn и соединяющего их кабеля. Код можно отлаживать с PC.
Система разработки PsyQ
Это позволяет консоли Saturn загружать файлы с PC почти так же, как она загружала бы их с CD. Связь контролируется резидентной программой (PSYBIOS), обрабатывающей коммуникации между машинами. Мы используем эту функцию для загрузки файлов каждого уровня.
Меньший из двух ящиков — это E7000PC, являющийся встроенным эмулятором SH2. У меня в комнате есть пара больших и громких ящиков, а ещё два PC. Также он полезен для отслеживания записи в память, но пока я почти не пользовался этой функцией. Он помогает разобраться, где вылетает программа, если отладчик PsyQ её не остановил.
По сути это Saturn с дополнительными интерфейсами для E7000 и эмулятора CD. Второй из громких ящиков — это нечто под названием «Small Box» (первый «Large Box» был размерами примерно с небольшой холодильник). На передней панели у него есть переключатели ID стран и переключатель между PAL и NTSC.
В нём можно собрать образ CD и эмулировать его в реальном времени, чтобы посмотреть, как будет выглядеть игра, когда доберётся до настоящего CD. Внутри второго компьютера находится эмулятор CD — большая плата, благодаря которой жёсткий диск компьютера притворяется CD-приводом. Эмулятор более-менее работает, хотя у него есть некоторые проблемы, над решением которых мы сейчас работаем вместе с Sega.
Dev kit самой компании Sega
Компилирование и компоновка
Общая сборка готовой программы управляется одним makefile: MAKEFILE.MAK. Он содержит зависимости и целевые объекты для всего проекта, в том числе компиляцию файлов .GOB и .GOV.
C) компилируются программой CCSH в объектные модули SH2 (. Отдельные модули исходного кода C (файлы . Она сначала вызывает препроцессор GNU C под названием CPPSH (находится в C:\GNUSH2\BIN), затем вызывает CC1SH для его выходных данных, чтобы создать ассемблерный код SH2, и, наконец, вызывает ASSH (в C:\PSYQ) для сборки его в готовый объектный формат. OBJ).
Однако я не работал с ним, можете поэкспериментировать. Мы не используем C++, потому что мне сказали, что он создаёт огромные объектные файлы.
S) просто собираются при помощи ASMSH напрямую в файлы . Несколько файлов на языке ассемблера SH2 (с расширением . В настоящее время они используются только для встраивания данных, и не содержат машинно-зависимого кода. OBJ (это не то же самое, что ASSH, а более сложный макроассемблер).
Один начинается по адресу $06000000, а другой — по $00200000. ОЗУ Saturn, в которое можно загружать код, разделено на два блока по 1МБ. Код программы записывается по адресу $06010000 (первые $10000 байт используются для системного пространства, стека и тому подобного.) Блок по адресу $00200000 используется исключительно для хранения графики главного персонажа.
Код позиционно-зависимый и компилируется так, чтобы выполняться по этому конкретному адресу ($06010000) и никак иначе.
OBJ компонуются вместе при помощи программы PSYLINK для создания файла MAIN. Файлы . PSYLINK использует файл TEST. CPE — исполняемой программы с небольшим заголовком, который можно скачать в Saturn командой RUN. OBJ нужно включать и куда их помещать. LNK для указания того, какие файлы .
Данные
Игра разбита на несколько уровней, во многих уровнях используются одинаковые данные, но в основном они для каждого уровня свои. Все данные для каждого уровня собираются в два огромных файла .GOV и .GOB. (в случае шахты это MINE.GOV и MINE.GOB). Файл GOV содержит короткий заголовок, а затем идут все данные, которые должны находиться в видеопамяти. Файл .GOB содержит все данные, которые должны находиться в ОЗУ.
Уровень состоит из части показанных ниже файлов данных.
SSQ — файл секвенсора спрайтов
. . MAP — обе карты для заполняемых символами фонов.
. SBM — файл битовой карты, используемый для битовых фонов
. PTH — данные точек дороги и триггеров.
. TIL — тайлсеты и палитры для заполняемых символами фонов.
. TEX — текстуры для дороги.
SSQ и . Файлы . Файлы . SBM созданы моим становящимся всё более неудобным секвенсором «SEQ». TIL, . MAP, . TEX созданы становящимся всё более потрясающим редактором карт «TULE», написанного Дэном. PTH и .
GOV и . Эти файлы при помощи ассемблера ASMSH собираются в соответствующие файлы . Чтобы посмотреть, как это делается, см. GOB. S и LEVEL1. файлы LEVEL. В файл . S. GOV также включается часть данных конкретного уровня.
Модули
TEST.S — ничего особенного, задаёт несколько меток.
C — верхний уровень программы. MAIN. В нём довольно много мусора, потому что в этот модуль проще всего добавлять что-то новое для быстрого тестирования. Содержит инициализацию оборудования, настройку уровней, код прохождения уровней и различные другие мелкие элементы, которые на самом деле стоило бы поместить в более подходящие модули. Содержит флаг для включения и отключения цветных полос TIMING. Содержит код загрузки с CD или файлового сервера на PC.
C — различные процедуры для доступа к оборудованию и выполнения различных графических функций. GFXLIB. Если вы часто используете процедуру отсюда, то неплохо было бы взглянуть, что она делает и написать более быструю версию в своём коде. Почти все они написаны с нуля Дэном и часто очень неэффективны.
Поблагодарим Дэна, без него это было бы невозможно. Тем не менее, все функции работают и обеспечивают превосходный фреймворк для черновой реализации и тестирования.
C — различные процедуры для считывания с джойстика Saturn, очень зависимые от оборудования. SMP_PAD.
C — все глобальные переменные и несколько общих функций. GLOBALS. Однако по различным причинам реализация глобальных переменных в SH2 довольно медленная, поэтому со временем я возможно преобразую часть в глобальные структуры, если понадобится. Использование глобальных переменных — приемлемая практика программирования. Содержит переменные, описывающие состояние MAN и PATH.
C — обрабатывает движение и отображение человека (Prince Lightstar, Talyn, Guardian или Grimskull — персонажа, которым управляет игрок). MAN. Кроме того, он обеспечивает соответствующую анимацию для каждого действия. Пока это в основном логика движения и коллизий с дорогой. Тут ещё нужно сделать много работы.
C — обрабатывает движение и отображение объектов в игре, в особенности объектов врагов, например, воинов-скелетов и маленьких инопланетян. OB. Структура данных готова ещё не полностью, в частности, не совсем проработаны проблемы с коллизиями и анимацией. Здесь программируется основная часть геймплея: вражеский ИИ, основные движения и срабатывание триггеров. Предстоит ещё куча работы.
S — различные таблицы, в настоящее время в основном анимации основных персонажей игрока. DATA.
C — скроллинг фонов с параллаксом. LAYER. Также выполняет скроллинг линий (эффект волн) в слое тумана. Обновляет состоящие из символов фоны и скроллит битовые карты. Их нужно сжать в формат RLE, который я использовал для версии на Genesis. Пока таблицы для слоёв карт символов хранятся без сжатия. Эта задача может перейти к Кену, если мы получим систему разработки под Saturn раньше, чем для Sony.
C — палитра. PAL. Любой пиксель на экране может быть одного из этих цветов. Можно выбирать из 2048 цветов. В PAL. Я логичным образом разделил палитру на восемь палитр по 256 цветов. Также им понадобится затемнение и более сложная циклическая смена, а также вспышки яркости и т.д. C содержится код для их инициализации, подготовки и код для их циклической смены.
C — примитивная система для обработки снарядов (бросок меча, удар рукой, выстреливаемые из рук ракеты и т.д.) как отдельных объектов. BUL. Также нужен правильный код коллизий и анимаций. По-прежнему требуется довольно много работы для более сложного использования снарядов.
C — простой модуль для запоминания состояния джойстика в более удобном формате. PAD. Запоминает, была ли недавно нажата кнопка, и нажата ли она сейчас.
C — одна строка, сообщающая, какой уровень будет первым, для простоты его смены в командном файле. START.
C — простые процедуры для вывода полоски силы. PANEL.
C — чудовищные процедуры для отрисовки дороги, а также обработки коллизий с дорогой. PATH.
C — простые синус, косинус и поворот точки на угол. MATH.
C. [Обновление] Вот пример кода из MAN. Куча прописанных в коде чисел. Всё жёстко прописано в коде и ссылается на глобальную структуру данных Man.
/**************************************************************/
/* Trigger jumping if needed, also variable height jump logic */ Man_JumpTrigger()
if ( Man.Mode != M_Crouch || Man_StandingRoom() ) // ok if not crouched, or there is headroom { if (Pad_Jump->Pressed) /* jump button pressed */ { if ((Man.Contact || (Man.Mode == M_Hang) || Man.JumpFudge) && Pad_Jump->Triggered && !Man.Blocking) /* and not already jumping */ { if (Man.Mode == M_Hang && Pad1.Down.Pressed) { Man.Contact=0; Man.Mode=M_Jump; Man.AnimBase = LS_Jumping; /* Change base anim to jumping */ Man_TriggerSeq(LS_Jump); /* start the jumping start anim */ Man.YV.f = 0x10000; /* and have no YV */ Man.Y.i += 4; /* and have no YV */ } else { Pad_Jump->Triggered = 0; if ( !JetPacCheat ) Man.YV.f = -0x00080000; /* Initial jump speed */ else Man.YV.f = -0x00008000; // Initial speed in Jetpac mode Man.Contact = 0; /* not on the ground any more */ Man.JumpTime = 0; /* just started jumping */ Man.AnimBase = LS_Jumping; /* Change base anim to jumping */ Man_TriggerSeq(LS_Jump); /* start the jumping start anim */ Man.XV.f+=Man.FlyVel; if (Man.HangEnd && Man.Mode == M_Hang) // if hanging { // and on the end of a path Man.HangEnd = 0; Man.X.i += 12*Man.Facing; // the move past end of path Man.JumpTime = -3; // bit more fixed v jump time } Man.Mode = M_Jump; /* change mode to jumping */ } } else /* Already jumping */ { if (Man.JumpTime++ < MaxJumpTime) /* Still in initial jump period */ Man.YV.f -= 0x0005000; /* So can maintain jump YV */ } } else /* jump button not pressed */ { Man.JumpTime = MaxJumpTime+1; /* so can't alter YV again until landed */ } } }
OB.C разросся до чудовищного файла из 9000 строк, в которые включены все паттерны поведений отдельных объектов в игре. Также там есть огромное количество прописанных в коде чисел, например таких:
Drop_Arac(S_Ob *pOb)
{ int t; if (pOb->Jump==1) { pOb->yv.f+=0x7fff; pOb->y.f+=pOb->yv.f; t=Path_GetYZ(pOb->x.i,pOb->y.i,pOb)-15; if ((t>pOb->y.i)&&(t<pOb->y.i+20)) { pOb->Jump=0; pOb->y.i+=15; Turn_Around(pOb); pOb->SeqFile=Sprites[SpriteMap[34]]; Object_TriggerSeq(Arac_JumpLand,pOb); } } else { if (pOb->Frame==16) pOb->Jump=1; if (pOb->AnimStat==AnimDone) { pOb->t1=0; pOb->Mode=&Pattern_Arac; } } Command_Arac(pOb);
}
Неприятное зрелище. Такой стиль кода пришёл из времён, когда игры были очень маленькими и наработал я его при работе с 68K.