Написание аркадной игры
После сборки 16-цветного девайса на пе─
нтагоне (см.статью) я решил закрепить этот
успех написанием игрушки под 16 цветов.Бы─
ла выбрана идея игры Pang. Сначала я скон─
струировал шарики (в фотошопе с использо─
ванием преобразования декартовых координат
в полярные) и написал выводилку спрайтов
(чтобы посмотреть, как эти шарики прыгают
на фоне картинки). Выводилка спрайтов и
стиралка спрайтов попеременно вызываются в
цикле LOO (так в программе и сейчас). Пока
обновляется 2-й экран,видно 1-й,и наоборот
(так и сейчас).
Спрайты конвертировались из 16-цветного
.bmp (палитру для конверсии можете нарисо─
вать сами, это очень просто: сначала неяр─
кие, потом яркие, в стандартом порядке,0-й
цвет будет прозрачный) прилагаемым исход─
ником bmpTOspr.H. Исходник bmpBKspr.H де─
лает то же самое, но переворачивает спрайт
лицом влево, если он был лицом вправо (и
наоборот).
Потом я попросил Shiru Otaku вытащить
цветные спрайты персонажа и прочих объек─
тов этой игры с первой попавшейся пристав─
ки. Shiru Otaku работает очень быстро, по─
тому нужные спрайты были добыты за 3 дня.
Они (в SNES версии) нарисованы под 32768
цветов (16 цветов на спрайт, 256 цветов на
экран). Перевести все эти цвета в 15 стан─
дартных было очень просто - достаточно бы─
ло разложить все спрайты на одну картинку
(получилось около 60 разных цветов) и от─
редактировать цвета в палитре.При этом да─
же сохранились практически все цветопере─
ходы.Благодаря тому,что в SNES-версии раз─
решение 256x224, не пришлось масштабиро─
вать спрайты.
Параллельно я добавил в программу (ко─
торая,как вы помните,умела только выводить
спрайты) следующие вещи:
1. Интеллект, который работает на преры─
вании (см.статью про игровой цикл в IG#6).
Этот интеллект оперирует 11-байтными опи─
сателями юнитов (тип персонажа, его фаза,
координаты,скорости,время и энергия). Эне─
ргия в Pang не нужна,это поле зарезервиро─
вано для игр-"стрелялок". Оно двухбайтное,
потому что в Wolf2004 еле хватало одного
байта, хотя в Wolf2004 нет боссов с кучей
энергии (способ убивания Гитлера основан
не на энергии, а на хитрости). К слову, в
Wolf2004 ещё и не было скоростей движения
спрайтов. Взамен организовывался временный
(!) буфер направлений движения для персо─
нажей, близких к игроку. Так пришлось пос─
тупить из-за того, что монстр/объект в
Wolf2004 с древних времён описывался 8 ба─
йтами,и разбайтовка этих байт торчала сли─
шком во многих местах программы (не через
индексные регистры) - переделать было бы
очень трудно. В Wolf2004 нужно было разде─
лить живых и неживых персонажей в разные
списки в разных форматах (живым больше
байт, неживым меньше). Но это не было сде─
лано.Увеличить универсальный описатель с 8
байт до 9 и выше было нельзя - не хватало
памяти, например, на уровне L. Впрочем, в
Wolf2004 есть несколько свободных килобайт
в верхней памяти,там предполагалось разме─
стить работу с сетью (LAN).
2. Пересчитывалка координат персонажей в
координаты на экране,передаваемые в список
SPRS (которым пользуется процедура вывода
спрайтов). Заодно тип персонажа и фаза пе─
ресчитываются в адрес спрайта. И вообще,
переброска перед выводом требуется для на─
дёжности - ведь если из цепочки был удалён
один персонаж (а удаляется он по прерыва─
нию,т.к.интеллект висит на прерывании!),то
выводилка может случайно перескочить через
конец списка или может взять половину дан─
ных от одного персонажа (и тут случайно -
БАЦ - прерывание!), а вторую половину - от
другого.Так что если перекидывалка узнаёт,
что во время её выполнения произошло пре─
рывание, она начинает перекидывание снача─
ла. В Wolf2004 персонажи не удалялись (ка─
жущееся "удаление" - это просто занесение
в Y-координату специального числа) и не
добавлялись,поэтому не требовалось копиро─
вание списка персонажей перед циклом выво─
да.
Чтобы совсем избавиться от глюков,позже
я организовал: 2.I. Проверку на случай пе─
реполнения буфера под спрайтами. 2.II. Вы─
ход из цикла искусственного интеллекта по─
сле выполнения операций удаления каких-ли─
бо персонажей (можно было поступить иначе:
сдвигать текущий указатель юнита, если был
удалён кусок списка,лежавший в памяти ниже
этого юнита).
3. Списки фаз для пересчёта фаз персона─
жей в адреса спрайтов.
4. Для каждого персонажа адрес списка
фаз (п.3) и адрес процедуры обработки пер─
сонажа (для цикла интеллекта).
Сначала спрайты не имели заголовка,и их
размеры лежали отдельно.Заголовок я сделал
потом. Длину спрайта в байтах я внёс в за─
головок в самый последний момент, раньше
эта длина вычислялась. Поэтому в исходнике
такая оригинальная загрузка спрайтов - че─
рез макрос iNCBIN. Этот макрос подставляет
в заголовок спрайта то самое поле длины.
Макрос выглядит так запутанно потому, что
для работы он должен узнать текущее смеще─
ние компиляции (ведь спрайты грузятся вну─
три блика DISP..ENT !).
Фоновые экраны у меня первоначально
грузились просто из bmp,лежащего на диске─
те. Но это неудобно и некрасиво. Поэтому я
организовал упаковку. Если,например,заста─
вку к Pang паковать в виде 4bit bmp, то
получается rar размером 2790. А если пако─
вать разложенную по столбцам,то получается
rar размером 2254. XOR между строк и XOR
между столбцов не помогает.
Распаковка заставок (DEPKS16) происхо─
дит так:
1. включаем 1-й экран (мог быть включен
2-й).
2. очищаем 2-й экран;включаем 2-й экран.
(Вариант: 2.очищаем #5800..#5aff; включаем
6912 режим.)
3. bmp, упакованный rar'ом (без заголов─
ка),кладётся в хвост страницы #14. (Удобно
перекидывать из другой страницы через 2
LDIR'а, используя область #a000...#bfff.)
4. вызывается smallunr.H (с параметром
RARS=#6000) - распаковка rar на адрес чуть
ниже #a000 (например, #9f00). Назовем этот
адрес "addr".
Сейчас данные в addr..addr+#5fff (почти
до конца памяти), включена #14 страница.
5. разворачиваем экран #4000 (начиная с
addr+192, после столбца пропускаем 3*192).
6. разворачиваем экран #6000 (начиная с
addr+(3*192)).
7. убиваем каждый 2-й столбец в блоке
данных (192 байта копируем, 192 пропускаем
и т.д.).
Сейчас данные в addr..addr+#2fff (почти
до #d000).
8. разворачиваем экран #e000 (начиная с
addr+192, после столбца пропускаем 192).
9. убиваем каждый 2-й столбец в блоке
данных.
Сейчас данные в addr..addr+#17ff (почти
до #b800).
10. разворачиваем экран #c000 (берём ба─
йты с addr, подряд).
11. включаем 1-й экран (или включаем 16-
цветный режим, если выключили).
Как видите, используется всего 2 дей─
ствия-процедуры ("разворачивать" и "уби─
вать") и не используются лишние области
памяти (кроме остатка одной из экранных
страниц - страницы #14 )! (На АТМ страницы
другие.) Память #a000..#bfff - буфер изоб─
ражения под спрайтами, и его вполне можно
портить, пока спрайтовый движок не вызыва─
ется. А вызывается он только внутри цикла
LOO.
Дальше было просто добавление програм─
мок и подпрограммок искусственного интел─
лекта. Эти программки довольно однообраз─
ны. Во всех используются одни и те же ре─
гистры: IX (адрес описателя персонажа), BC
(Y), DE (X). Сделана сначала одна процеду─
рка проверки коллизий,а потом ещё две.Одна
проверяет коллизию героя с врагами (вызы─
вается из обработчиков героя и перебирает
врагов, хотя можно было бы вызывать из об─
работчиков врагов и ничего не перебирать -
герой-то один). Другая проверяет коллизию
гарпуна с врагами (аналогичное замечание).
Третья - коллизию героя с призом.
Были между делом нашлёпаны процедурки
вывода счёта и времени, таблички уровней и
прочая мелочь.
В конце концов из запасников были выта─
щены маленькие музончики (которые я писал
в 2002-2004 гг.) и вставлены в игру. Был
взят модуль звуковых эффектов из старого
доброго Wolf2004, в звуках немного измене─
ны параметры.Этот модуль не универсальный,
он умеет играть только небольшой класс
звуков (например, в процессе проигрывания
эффекта не могут меняться периоды тональ─
ника и шума).
Сборка игры во время её написания прои─
зводилась много раз (как минимум перед ка─
ждым тестированием на реальном пентагоне)
и проблем не вызывала, т.к.это автосборка.
Основной блок плюс спрайты пакуется в бей─
сик-файл. Файлы с музыкой и картинками ле─
жат отдельно и подгружаются (в странички)
через #3d13 после старта игры. Программу,
оформленную таким образом, можно пересоби─
рать хоть по 100 раз на дню. Меня удивляют
некоторые программисты, которые при каждой
сборке своих программ вручную запускают
пакер и так же вручную приклеивают загруз─
чик (вручную вводя размеры файлов и пр.).
Программа писалась под Unreal Speccy
v0.32b8 и между делом проверялась на реа─
льном пентагоне. SMT оперативно организо─
вал поддержку 16-цветного устройства в
эмуляторе,благодаря чему перетаскивать иг─
ру на пентагон для проверки приходилось не
так уж часто. От момента запроса спрайтов
до релиза игры, т.е. показа её заинтересо─
ванным лицам (в двух сборках:для Пентагона
и АТМ !) прошло 17 дней. Для меня это ре─
корд. Лестницы и блоки я делать не стал -
игра и сейчас имеет хорошую playability,да
и проект изначально задумывался как недол─
гий.
Об ускорении этого движка.
Программа создавалась как кроссплатфор─
менная - легко переделываемая, кроме того,
обучающая - легко понимаемая (обратите
внимание на количество комментариев в ис─
ходниках). Поэтому хитрости не были приме─
нены.
1. На АТМ можно поместить страницу гра─
фики в область ПЗУ и не LDIR'ить спрайты
перед выводом. Того же можно добиться на
пентагоне, если положить самые популярные
спрайты (т.е. шарики и верёвку - итого 4
спрайта) в нижнюю память и вставить небо─
льшую проверку перед LDIR'ом. Попробуйте в
исходнике. Выигрыш должен быть небольшой
- 21 t/байт (сейчас ў130 t/байт, включая
RESPR ). Причём этот выигрыш считается от
процессорного времени, оставшегося после
вычитания времени на музыку (5% для 70000
t),интеллект (10% по оценке для 70000 t) и
вывод цифр (5%?). Т.е. общий выигрыш можно
оценить в (21 t/130 t)*(100%-10%-5%-5%) <
< 13% времени. На пентагоне в нетурбо выи─
грыш меньше - где-то 11% (т.к. 45000 t
вместо 70000 t,в силу чего музыка и интел─
лект забирают порядка 30% ).
2. При использовании большого объёма па─
мяти можно хранить картинки распакованны─
ми,а шрифт - в виде спрайтов букв под раз─
ные раскраски. Но и сейчас можно не распа─
ковывать одну и ту же картинку перед каж─
дым входом в один и тот же уровень, а про─
сто стереть с неё спрайты (с обоих экра─
нов). Попробуйте в исходнике.
3. При наличии копии заднего плана можно
не сохранять фон под спрайтами,а восстана─
вливать его прямо из копии заднего плана.
На пентагоне неактуально (рабочий экран и
копия заднего плана не помещаются в одно
адресное пространство),сработает только на
АТМ. Выигрыш не больше 16 t/байт (сколько
точно - не ясно, т.к.должен быть полностью
переписан RESPR ). И две проблемы: 3.I.
вывод цифр замедляется в полтора раза (ес─
ли считать,что они уже развёрнуты по реко─
мендации 2 ); 3.II. при написании игр со
скроллингом скроллинг замедляется в полто─
ра раза,а он и так должен быть ужасно мед─
ленный (вывод двух экранов,т.е. 48k!). Ко─
нечно, для некоторых игр вообще не нужно
запоминать задний план и восстанавливать
его под спрайтами - можно просто перестра─
ивать весь задний план (на одном экране,
т.е. 24k) в начале каждого цикла вывода
спрайтов.
Alone Coder