Из журнала Info Guide #8, Рязань, 12.2005 Написание аркадной игры После сборки 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