Игровое программирование. Уроки скриптописания, часть 2

Игровое программирование. Уроки скриптописания, часть 2

Самопал — Игровое программирование. Уроки скриптописания, часть 2
Идея этого мода возникла после прохождения игры Postal 2, где можно было отбивать ракеты лопатой. Чем же UT2004 хуже?! Поэтому мы добавим в игру возможность отражать ракеты с помощью энергетического щита
Игроманияhttps://www.igromania.ru/
Самопал
Игровое программирование. Уроки скриптописания, часть 2

В первой статье (см. прошлый номер “Игромании”) мы научились читать несложный программный игровой код на примере мутатора для UT2004, добавляющего в игру новое оружие — пулемет “Мегакиллер”.

Сегодня мы продолжим постигать основы игрового программирования и создадим более сложный мутатор. Идея этого мода возникла после прохождения игры Postal 2, где можно было отбивать ракеты лопатой. Чем же UT2004 хуже?! Поэтому мы добавим в игру возможность отражать ракеты с помощью энергетического щита.

Как всегда, все упоминаемые в статье программы и файлы можно взять с нашего диска.

Принципы объектного взаимодействия

18 Kb

Наглядное представление щита и процесса отражения ракеты от него.

Прежде чем приступить к написанию мода, нам надо разобраться, как происходит взаимодействие разных объектов в игре. В частности — чем выстрел из автомата отличается от выстрела из рокетлаунчера, что собой представляет ракета, на каком этапе и каким объектом инициируется взрыв. Давайте разбираться.

В игре всего два класса выстрелов: выстрел мгновенного поражения (когда скорость пули или луча многократно превосходит скорость движения противника, поэтому считается, что противник увернуться не может) и выстрел, при котором выпускается снаряд. Принципиальное отличие между этими типами стрельбы в том, что (утрируем, но суть от этого не меняется) в первом случае “решение” о нанесении травмы врагу принимается стреляющим (в момент выстрела), во втором случае — снарядом (в момент контакта снаряда и противника).

Любой снаряд в игре представляет собой наследника класса Projectile. Это автономный объект, которому в момент выстрела определяется всего два параметра: начальная скорость и точка пространства, где снаряд должен появиться (для самонаводящихся ракет передается также параметр “цель”). После создания снаряд живет своей жизнью до тех пор, пока не натолкнется на какое-либо препятствие.

Под препятствием надо понимать любой подвижный объект (наследник класса Actor) или геометрию уровня. На столкновение снаряд может реагировать по-разному: осколки из Flack Cannon несколько раз рикошетят от стен, ракеты взрываются, биомасса из Bio Rifle прилипает к стене. Таким образом, поведение снаряда фактически определяется только самим снарядом.

Из этих фактов делаем вывод, что для реализации идеи мутатора — отскакивающих от щита ракет — модифицировать следует не щит (что, казалось бы, логично), а рокетлаунчер и его ракеты.

Программная реализация мутатора

Скриптовая начинка мутатора состоит из шести классов. Классы представлены листингами, расположенными на отдельной странице. При детальном рассмотрении кода мы будем обращаться к отдельным строкам листингов, которые для удобства пронумерованы.

Приступаем к работе. Создаем для UDE новый проект под названием RocketShield, в который и будем добавлять все скрипты. Более детально с UDE, процессом создания проекта и добавлением скриптов можно ознакомиться в первой части статьи (см. прошлый номер).

40 Kb 44 Kb

В нашем моде ракетница не очень опасна, если правильно пользоваться щитом.

Так отражение ракет выглядит глазами жертвы.

41 Kb 42 Kb

Боты используют модифицированный щит не так часто, как хотелось бы.

Иногда танк можно уничтожить и из энергетического ружья.

Классы оружия и стрельбы

33 Kb
12 Kb

(ЛИСТИНГИ 1 и 2)

Класс модифицированной ракетницы RocketShield создаем в виде наследника RocketLauncher (строка 1 листинга 1). Новый рокетлаунчер должен вместо стандартных ракет выпускать переделанные, которые могут отскакивать от щита. За выпускание ракет в родительском классе отвечает метод SpawnProjectile, который мы и перегружаем в строке 2.

Поскольку нам нужно просто подменить объекты-снаряды, поступаем так: берем исходный код этого метода из родительского класса, находим в нем объявления и обращения к требуемым объектам и подменяем их. У ракетницы, как известно, имеются два варианта ракет: обычные и самонаводящиеся. В дальнейшем мы добавим в проект классы для них, а пока нам важны только названия этих классов: RocketShieldProj и SeekingRocketShieldProj.

Подмена объявлений осуществляется в строках 45, обращений — в строках 6 и 7. При обращении вызывается метод Spawn, который создает экземпляр снаряда указанного класса. В строках 813 мы задаем свойства по умолчанию: класс стрельбы и название модифицированного оружия.

В листинге 2 объявляется собственно класс оружия (строка 1). В свойствах по умолчанию мы переопределяем только тип снаряда, выпускаемого при стрельбе (строки 25).

Классы отражаемых ракет

(ЛИСТИНГИ 3 и 4)

47 Kb
16 Kb

Класс отражаемой ракеты RocketShieldProj базируется на RocketProj —классе обычной ракеты рокетлаунчера. Чтобы ракета при касании щита отражалась, нам нужно полностью переработать метод ProcessTouch (строка 2 листинга 3).

Прежде чем рассмотреть программную начинку этого метода, давайте сначала разберемся в алгоритме отражения. Алгоритм расчета использует несколько входных параметров: направление движения ракеты, ее скорость, пространственная ориентация энергетического щита (точнее — нормаль к его поверхности).

Обратите внимание на один принципиальный момент. Несмотря на то что щит визуально имеет выпуклую форму и, казалось бы, в разных точках поверхности должен иметь разные нормали, будем считать, что нормали во всех местах щита совпадают с направлением взгляда игрока. Ввиду этого факта, всеми нужными данными мы располагаем.

По задумке разработчиков игры щит обеспечивает защиту игрока на 136 градусов как по высоте, так и по ширине относительно ориентации щита. Таким образом, условие отражения от щита заключается в том, чтобы направление движения ракеты с направлением взгляда игрока образовывало угол не больше 68 градусов. При этом точка касания ракетой игрока рассчитывается движком на основе геометрии моделей, так что если щит включен и направлен в нужном направлении, ракета сквозь него никак не пролетит.

Теперь обратимся к реализации алгоритма. В строках 48 определяем локальные переменные для хранения следующих данных: потенциальная жертва ракеты (p), нормаль щита (HitNormal), вектор движения ракеты (HitDir) и модуль скорости движения ракеты (VMag). Затем мы сохраняем значение модуля вектора скорости в момент столкновения (строка 9), для того чтобы корректно восстановить скорость при отражении. В строках 1013 мы проверяем, что объект попадания — другой игрок. После чего выясняем, не использует ли он в альтернативном режиме стрельбы энергетический щит (строки 1416).

Когда мы проверили все требуемые условия, применяем наш алгоритм отражения. Вычисляем нормаль щита, задействуя свойство Controller противника (строка 17). Затем находим вектор движения ракеты, используя пространственные координаты жертвы и стрелка (строка 18). Выясняем, выполняется ли условие отражения ракеты от щита (строка 19). Операция dot означает скалярное произведение векторов. Фактически в этой строке проверяется несколько иное условие — косинус угла между найденными векторами меньше косинуса 68 градусов, взятого с обратным знаком (эти векторы должны быть противонаправленными). Краткую справку по использованным операциям с векторами можно найти в блоке “Немного математики”. Алгоритм отражения наглядно демонстрирует Рис. 1.

Если условие отражения выполняется, считаем отраженный вектор с помощью встроенной функции MirrorVectorByNormal (строка 22) и, используя ранее найденное значение VMag, восстанавливаем скорость ракеты в отраженном направлении. Визуально поворачиваем ракету в новое направление (если этого не сделать, ракеты будут забавно отлетать хвостом вперед) в строке 23. Наконец, в строках 2526 создаем физическую отдачу для стрелка. Поскольку в случае отдачи взрыва происходить не должно, применяем зарезервированное слово return, означающее безусловный возврат из метода.

Если же ракета в щит не попала, тогда устраиваем взрыв, обращаясь к методу Explode (строка 30).

Класс самонаводящейся ракеты создаем аналогичным образом — всего с двумя отличиями. Отличие первое состоит в том, что родительским классом назначаем SeekingRocketProj (строка 1 листинга 4), второе — после того, как мы создали отдачу, перенацеливаем ракету в стреляющего (строка 26). Вот и все, ракеты готовы к бою!

Класс мутатора

(ЛИСТИНГИ 5 и 6)

Перед созданием мутатора надо задать класс события RocketShieldPickup, когда игрок берет в руки модифицированный нами рокетлаунчер (строка 1 листинга 5). Переопределению подвергаем только параметр InventoryType — тип оружия, с которым ассоциируется событие (строка 4).

14 Kb
56 Kb

Теперь можно создавать сам класс мутатора RocketShieldArena (строка 1 листинга 6). Детально рассматривать общую структуру мутатора мы уже не будем, но подробно прокомментированные исходники и первая часть статьи новичкам существенно помогут (см. наши диски — разделы “По журналу”/“Игрострой” и “ИнфоБлок”).

Мы лишь остановимся на ключевом отличии данного мутатора от созданного в прошлом номере. В строках 711 вводится принудительная экипировка каждого игрока модифицированным рокетлаунчером и энергетическим щитом, поэтому уже с самого начала горячий бой обеспечен!

Немного математики

Проведем небольшой экскурс в векторную математику для тех, кто не слишком хорошо помнит пройденное в школе. Самые важные моменты:

Вектор — точка трехмерного пространства, описываемая тремя координатами — X, Y и Z (формула б).

Модуль вектора — его длина, вычисляемая по формуле (формула в).

Единичный вектор — длина которого равна 1. Любой вектор можно преобразовать к единичному, разделив каждую его координату на длину.

Нормаль к поверхности в точке — вектор, перпендикулярный поверхности в этой точке.

Скалярное произведение двух векторов — длина проекции одного вектора на другой, вычисляемая по соотношению (г).

Угол между двумя векторами вычисляется по косинусу этого угла (формула а). А косинус считается как отношение скалярного произведения векторов к произведению их длин. Эта формула использовалась нами в алгоритме. Заметим, для единичных векторов косинус равен просто скалярному произведению.

Два вектора сонаправлены, если угол между ними меньше 90 градусов, в противном случае они либо взаимно ортогональны, либо противонаправлены.

27 Kb 15 Kb

Некоторые полезные формулы.

Разность двух векторов - это вектор, который строится, как показано на рисунке ниже. Смысл разности векторов прост: если стрелок - вектор b, жертва - вектор a, тогда a-b - направление полета ракеты.

* * *

Мы создали весьма сложный мод, задействующий не просто примитивную подмену параметров, а настоящую математическую модель. Разумеется, аналогичным образом можно модифицировать любые типы Projectile-выстрелов, что дает невероятный простор для креатива.

В одном из ближайших выпусков “Самопала” мы двинемся дальше и создадим еще более сложный мутатор, заточенный под... сетевую игру. Не пропустите!

Комментарии
Загрузка комментариев