Из журнала Adventurer#13, Ярославская область, г.Рыбинск, ? Research/Volgasoft Как написать 3D игру типа DOOM В этой статье я собираюсь поведать (ну и показать рабочий пример, естествен- но) о том, как сделать (как делались) трехмерную гаму на 3d движке с секторной организацией. В свое время Доктор STS (теперь более известный как Доктор Луже- ная Глотка :))) ) занимался этим вопросом и написал пару не сильно отличавшихся 3d движков (code name: ZX Duke Nukem). В принципе, об аналогах я не слышал, может кто-то и сделал что-то подобное, а раз таковых нет, то будем рассматривать един- ственный существующий экземпляр. Дабы не дать издохнуть шедевру, я приспособил его в дему, выпущенную под названием <Love Gun>. Если Вы ее видели, значит поймете, о чем идет речь. Сразу хочу сказать огро- мное спасибо другому известному <отцу> - дяде Darkу из ныне усопшего X-Trade. Он тоже в огромной степени причастен к напи- санию движка, как и STS. В принципе, нем- ного посодействовал материалами и аппара- тными средствами и я. Все, что здесь бу- дет затронуто, базируется на материалах двух вышеупомянутых "трехмерных пап", опубликованых в электронном журнале "Spe- ctrum Expert". Начните с него, иначе ни фига не поймете. Итак, приступим: В начале рассмотрим, что же это за секторная организация такая. Под секторами будем понимать некую замкнутую трехмерную фигуру, из любой то- чки которой беспрепятственно видны другие точки этой фигуры. Грубо говоря, это две сваи, одна из которых вбита в пол, а дру- гая в потолок, и все это оклеено обоями, а мы внутри оставшегося между сваями про- странства. Сектор описывается координата- ми точек, из которых он состоит, высотой (координатой) потолка, высотой (координа- той) полов, схемой соединения точек (стен), ну и естественно, номеров заливок каждой стены (грани), полов и потолка. Вот кусочек карты LoveGun.
Итак, имеем уровень, разбитый на се- ктора. Как выглядят сектора, Вы уже, на- верное, догадались. Смотрим на сектор S0. Он состоит из точек с номерами 0,1,2,3,4,5,6,7,8,9,0. Точки соединены по часовой стрелке и образуют стены: 0-9, 9- 8, 8-7 и т.д. Набросав такой восьмигран- ник, мы получим возможность по нему пок- рутиться. Но: встает проблема, а че даль- ше? На одном секторе игру не построить. Даже на двух или десяти. Чего от них тол- ку, если это независимые друг от друга помещения? Ведь их как-то сцеплять надо. Вот эти проблемы и выделены на картинке жирными линиями. Называются они межсекто- рными соединениями. Итак, наши сектора имеют общую грань - 0-9 для S0 и 9-0 для S1. А это означает большое ведро гвоздей в заднице в плане кода и вычислений. При- мер - когда я делал подъем (сектора S4-S9), то хотел сделать его гладеньким и впиндюрил 15 ступенек. Скорпион в резуль- тате повис в жопу, эмулятор Шалаева выжи- мающий 300 тысяч тактов за frame (на Cy- rix200) решил, что тут на спектруме mp4 показывают. Подумав, что это все-таки не глюк, я обошелся пятью ступеньками. <Нау- чное> объяснение сего будет ниже. Переключаемся на исходник <мирка> урезанного под наш кусочек карты (world5.c). Таблица указателей на сектора: SECTB _DW S0,S1,S2,S3,S4,S5,S6,S7,S8,S9 Указатели располагаются в том же по- рядке, в котором лежат сами сектора. А вот и сектора. Мне понадобилось какое-то время, чтобы расрюхать чего там STS понаделал: ORG $^ SECTORS ;FLOOR'z,CIELING'z ;SLOPE FLOOR,SLOPE CIELING ;HITAG,LOTAG,EXTRA ;FLOORPIC,CIELINGPIC ;пример сектора: S0 DW 300,-400,0,0,0,0,0:DB 22,15 ; шапка сектора ;DW высота потолка, пола, потом ;незадействованные поля (под всякие ; спецэффекты), потом: ;DB заливка полов, потолка ;кстати - потолок <внизу> ;Затем идет список узлов, из которых состоит сектор ; POINT, FIL, NUM, CONNECT, SEC ;DW номер точки: DB заливка стены <этот узел - ;следующий за ним>, номер данной стенки, ; номер стенки общей с соседним сектором ;(если 0 - стена не прозрачная) (<сцепка>), ; номер сектора с которым происходит сцепка ; (-1 - нет соединения). DW 9:DB 1,0,0,-1 DW 8:DB 14,1,0,-1 DW 7:DB 6,2,0,-1 DW 6:DB 12,3,0,-1 DW 5:DB 20,4,0,-1 DW 4:DB 12,5,0,-1 DW 3:DB 6,6,0,-1 DW 2:DB 14,7,0,-1 DW 1:DB 1,8,0,-1 DW 0:DB 25,9,12,1 ;соединяемся с 12-м ;ребром сектора S1 DW -1; конец сектора, ;соединение 0-го и 9-го узлов ;пример второго сектора S1 _DW 300,-400,0,0,0,0,0:DB 14,19 _DW 12:DB 13,10,14,2; соединяемся ;с 14-м ребром сектора S2 DW 10:DB 27,11,0,-1 _DW 9 :DB 4,12,9,0; соединяемся с ;9-м ребром сектора S0 DW 0 :DB 27,13,0,-1 DW -1; конец сектора, ;соединение 0-го и 12-го узлов А вот и сами точки из которых клеят- ся сектора: ORG $^ ;DW X,Y; X=-16384,16383 Y ;тоже, если мне не изменяет ;память DOTS DW 5500,4000;0 S0 DW 5000,4000 DW 4000,3000 DW 4000,1000 DW 5000,0 DW 7000,0 DW 8000,1000 DW 8000,3000 DW 7000,4000 DW 6500,4000;9 DW 6500,6500;10 S1 DW 5000,6500;11 DW 5500,5500;12 S2 DW 5000,5500;13 Как было уже отмечено выше, такая идея организации позаимствована с писюка и естественно подправлена под возможности спектрума. С устройством секторов все. Теперь переключимся на то, как это все заливает- ся. Сейчас я Вам продемонстрирую пару картинок: Эту можно назвать <Глазами зрителя>
А эту <Истина>
Видите? Ну, каково, не передернуло? А все дело в том, что друг наш спекки не умеет быстро считать. Да, да:. Залить стенку целиком от пола до потолка полу- чается быстрее, чем обсчитывать участок действительно видимый зрителю, и затем заливать его, причем обычно в два раза. Обдумав эту картинку, STS понял, что в принципе для ускорения движка, заливок потребуется две: первая - обычная (проце- дура fdrpoly), вторая - заливка прямоуго- льников от пола до потолка - специально для стен (процедура filv). Кстати она це- ликом стековая и ускорению фактически не подлежит. Про заливки остается добавить, что они заливают по столбцам - так быст- рее, но вынос буфера на экран вида: _POP BC: LD (HL),C: INC H:LD (HL),B: INC H жрет порядочно быстродействия. Это не LD HL,#0000: PUSH HL или POP HL: LD (SCREEN),HL какой-нибудь. С другой стороны - это неи- збежность. Вот поэтому-то 15 ступенек и не получилось. Подводим небольшой итог. Итак, обработчик секторов использует самые прогрессивные на сегодняшний день процедуры для обсчета (с использованием логарифмов, примерных вычислений и т.д.). К тому же, в нем задействована рекурсия. Посему он оптимизации почти не подлежит (скорее, он подлежит только дописке). Другое дело блок заливок. Заливки, как и сортировки, всегда были <русским полем оптимизации>. Ведь на мелкие деталюшечки столько тратится! Было бы, наверное, пра- вильнее сделать не две, а все три или бо- лее заливок. Сейчас у меня вертится в го- лове одна идея, тока не знаю, насколько она реализуема, посему пока промолчу. Была, помнится, идея сделать этот движок проволкой. В принципе, а почему бы и нет? Вопрос только в невидимых линиях, а это опять математика. На последок опять вернемся к исход- нику. Я оставляю всe as is. Обычно STS не разбивал программы на логические модули (в шторме с ними работать не больно-то удобно), а напихивал столько, сколько в текстовую банку влазит. Я позволил себе наглость нарисовать некоторые коммента- рии. Разобраться с тем, что там делается, дело, наверное, недели (у меня ушло в це- лом дня четыре). Еще подбрасываю библиотеку GRAFLIB, составленную STS'ом из процедур Дарка и своих собственных. Она пригодится любому 3D <писателю>. Разжевывать дальше не имеет смысла => на этом пожалуй все.