Ну,
я рискну порассуждать о том, как делаются игры action от первого лица. Впрочем,
вид сбоку или сверху на героя (в Duke3D или Blood) - это все одна суть. Просто
передвигаемся в этом случае уже смотря на героя со стороны, а не из его "глаз".
...с чего начать... Пожалуй с Doom'а первого - там нет наворотов со сценами,
как в Quake. Одни плоскости.
Итак. Очевидно, что для создания "мира" нужно загрузить в память уровень (т.е. уровня игры - этажа,
лабиринта) вместе со всеми его атрибутами. Это карта, по которой необходимо
будет смотреть расположение плоскостей (повторяю, я говорю про первые Doom и
Doom2), и которая описывает пассивные объекты (например анимация огня ), активные
объекты - вещи, монстры, герой(ои) и пр.
После загрузки неплохо бы проинициализировать объекты. Конечно,
надо представить, что каждый объект - это структура ( в смысле struct).
Он имеет кучу необходимых атрибутов, типа: координаты, обозначение, количество
"жизни", флажки там всякие и пр. К примеру, на карте герою изначально
определено место с координатами x=100,y=100 (какие-то единицы - это все относительно
и нас волновать не должно - они выбираются разработчиками) и про третью координаты
я умолчу - пока не до нее. Вот они и занесутся в параметры "текущее
положение" объекта герой. Причем, надо отметить, что в общем-то все
объкты равноправные - что монстр, что герой. Только за монстра играет комп,
а за героя - вы. Так что, проинициализированы будут все объекты, вплоть до последней
кнопки.
После этого ( в грубом приближении) запускается цикл (он и называется игровой
петлей). Что делается в цикле? Обрабатывается весь массив уже определенных (и
заново возникающих) объектов. С какого начинать - неважно. Все равно, до рендеринга
и вывода на экран кадра (именно кадра) нужно обсчитать все объекты. Вот
как раз скорость обсчета всех объектов и скорость рендеринга текущего вида (из
глаз) на сцену (я буду употреблять слово "сцена" именно для видимой
части уровня) и определяет количество кадров в секунду - этакое мерило скорости
всей системы - и процессора, и памяти, и видеокарточки. Обсчитывать не все
объекты нельзя - это игра в реальном времени. Монстр, передвигающийся где-то
в далеком (невидимом) участке лабиринта точно так же будет (и должен) обсчитываться,
как и тот, что у вас перед глазами. Поступаться можно лишь на самом ресурсоемком
участке - рендеринге - т.е. обсчете текстур и спрайтов (в Doom все движущиеся
объекты были сделаны спрайтами) или, например, уменьшить детализацию. Или размер
видимой сцены. Вот такая картинка для пояснения, хотя это есть обычное, всем
известное проецирование.

Сама плоскость, на которую проецируются объекты сцены - это и
есть экран монитора. Естественно, он играет роль глаза. Ну, а так как даже при
небольшом разрешении (320х240 пикселов) точек получается много, а следовательно,
прорисовка (и обсчет) займет больше времени, то во-первых, оставляли именно
такое разрешение для первых игр, чтобы очеспечить более-меннее приемлимое число
кадров в секунду, а во вторых, этот размер можно не только менять, но и счтать
каждую точку как четыре - получается 160х120точек - размер экрана остается тот
же, а точка занимает 4 пиксела. Но в принципе, и 320х240 прекрасно тянуло и
на 486 машинах.
Итак, игра началась. Что в петле делается: каждый объект имеет
какие-то свойства, которые могут меняться от разных обстоятельств. К примеру,
первым объектом на очереди будет герой, перед ним стоит монстр, вы нажали кнопку
- стреляем. Клавиатурный обработчик, ясное дело, "сидит" на прерывании,
забрасывает в стек нажатие клавиш (и отпускание тоже) - они поднимают разные
глобальные или локальные "флажки". Кроме изменения состояния объекта
"герой" (нужно же ведь гранатометом дернуть - там на много кадров
анимации) необходимо создать объект "ракета" и поставить его в общий
цикл на обработку. Сделали. Проверяется стек клавиш - допустим, при этом была
нажата еще и клавиша "вбок" - меняем позицию героя относительно текущей.
Не забыть отнять у объекта "герой" единичку из переменной "количество
ракет"... и т.п.
С остальными объектами - то же самое. Только монстрами управляет
компьютер - там некоторый AI заставляет их совершать те или иные действия. После
того, как у ракеты на следующем цикле прибавятся координаты (она же летит) происходит
обязательная проверка на совпадение координат с другими объектами. Причем проверка
нетривиальная - иначе комп только на то время и тратил бы, что сравнивал координаты
всех объектов друг с другом. Допустим, ракета попала в монстра - т.к. известно,
сколько ракета должна отнять жизни у монстра и сколько у данного типа монстра
ее на данный момент есть, происходит удаление объекта "ракета", изменение
свойств монстра, создание объекта "взрыв" и еще кучка разных мелочей.
Если жизни у монстра было на один несильный чих, то в свойства монстру заносится
- "умирающий" - на следующем цикле петли будет развиваться в течении
нескольких кадров сценарий "умирающий монстр". После чего объект "монстр_такой-то"
примет свойство "труп" - но не уничтожится как объект - как вы помните,
во 2-м Doome Archvile мог восстанавливать монстров - просто свойство "труп"
убиралось у объекта вместе с кое-какими добавками.
А мы побежали дальше. Немного напомню про звук - он в игровую петлю не входит.
Это отдельная программа, "висящая" на таймере (библиотека, о которой
я упоминал в прошлом номере настолько хороша, что не изменялась относительно
долго и даже целиком использовалась во многих играх). Она хватает события,
которые ей передаются из игровой петли посредством семафоров или флагов. Выстрелил
- поставился флажок "играть выстрел". Если в это же время по сценарию
кто-то из монстров должен хрюкнуть - тоже поставился флажок "хрюкать"
- и все, а звуковой комбайн в виде программы обработки событий это дело все
смикширует, подаст на разные каналы - чтоб стерео было и т.п. И будет играть.
По таймеру, конечно. Ему дела нет до того, успело там все обсчитаться, не успело
и что там вообще происходит - он только флажки смотрит от петли.
Все, один цикл всех подвигали, поизменяли свойства объектов и
пр. - подошли к прорисовке (цикл обработки еще не закончился). На экране надо
показать сцену. В первом и втором Doom'ах как вы помните, существуют плоскости,
которые имеют различные свойства - прозрачность, текстуру и т.д. Обсчет текстур
является достаточно ресурсоемкой задачей - это ведь картинки, их надо рисовать
в разных проекциях.
Без 3D карточек, которые сами это делают, приходилось считать программно. Представьте,
что плоскость стены повернута к вам боком - дальняя часть будет меньше (по законам
перспективы) а ближняя - больше. Т.е. где-то надо сжать, а где-то растянуть.
К тому же не забыть про "перекрытие" - т.е. невидимые части не то
что рисовать, их и считать не стоит. И еще не забыть про освещенность - это
тоже одно из свойств плоскостей в Doom'е. Все это рисуется где-то в куске памяти
(не на экране), после этого, накладыаются прямо на готовую сцену спрайты - опять
же, монстр может быть не виден целиком, его может загораживать другой... в общем,
дел у прорисовщика хватает. Хоть и алгоритмы готовые есть, всегда найдется что
пооптимизировать.
В Doom все движущиеся объекты сделаны спрайтами - на несколько
позиций - 8 штук (вид с разных боков). К тому же, объект может стрелять (еще
8 штук), двигать конечностями (еще...) и т.д. Объемы уходят большие. Зато -
быстро. Ничего считать не надо - только рисуй и все. С другой стороны - детализация
и реалистичность - не та, которую хотелось бы. Как вы помните, на лежащее оружие
в Doom можно было смотреть только с одной его стороны - потому что спрайт был
всего один. (в Blood сделали побольше - там с этим порядок).
Конечно, начиная с первого Quake активные объекты стали все натуральными 3D
- т.е. сделаны из плоскостей - или треугольников - ведь для задания плоскости
нужно всего 3 точки, да и варьируя стороны треугольника и количество треугольников,
можно создавать разные фигуры. Между прочим, и это не идеал - а идеал - воксель.
Т.е. 3-х мерная точка. Скопище таких точек может образовывать обекты, которые
максимально приближены к реальности - берешь, например, ударяешь по щиту - а
от него отлетят осколки (которые тоже - воксели) и появится _уникальная_ выщербина.
Кармак, как я уже как-то писал, экспериментировал с вокселями и весьма лестно
об этом отзывался. Правда, все это память жрет со свистом и ресурсов требует
много, но оно того стоит.
Так, на чем я остановился... ага, накладываются спрайты активных объектов. После
того, как вся сцена прорисуется, сверху накладывается рамочка (статус
героя, количество патронов и пр.), рисуется рука с пистолетом (прямо сверху),
надписи в углу экрана и пр. мелочи. Все. Это прошла подготовка кадра (всего
одного из необходимых 20-25 в секунду). Теперь просто кусок памяти выбрасывается
в видеопамять и.... поехал цикл по-новой. Успеваешь за 1 сек сделать дай Бог
2 кадра? Ну чтож, либо вини алгоритм, либо скорость машины :)
Вполне можно сказать, что объект "герой" в общем-то никуда и не бежит.
Просто изменятеся точка лабиринта, с которой мы его (лабиринт) рассматриваем
в данный момент, а программа просчитывает окружающий "пейзаж" и рисует
его на экране. Т.е. главное - это сделать быстрый рендеринг того мира, который
будет основой. Чтоб можно было смотреть с любой точки - и все. Помещай героя
последовательно в каждую из точек траектории - вот тебе и "побежал".
А монстры и пр. шелупонь - это все суета :))
Что с сетевой игрой: особых сложностей нет. Скажем, две машины
соединены по Ethernet'у друг с другом. Уровень (лабиринт) должен быть у каждого,
естественно, один и тот же (быть в памяти и обрабатываться петлей). Просто для
каждого игрока его оппонент будет таким же объектом, как скажем, монстр. А координаты
его (ну, и не только координаты) будут приходить по сетке. Вот и все. Выстрелил
оппонент - объект "ракета" создается на обеих машинах, собственно,
как и все остальные - уровень должен же быть одинаков во всех деталях для обоих
игроков.
пожалуй, на сегодня хватит....
Кучу замечаний и добавлений сюда.
|