Мутатор: первые шаги
Небольшой практический материал для начинающих. Особых знаний не требуется, т.к. весь процесс "от" и "до" мы рассмотрим на примере создания простенького мутатора. Пояснения довольно подробные :)
Основная идея:
При попадании в игрока, наш мутатор будет переводить урон полученный игроком, в энергию движения этого игрока. Т.е. если в игрока попадут из редимера, то он (разумеется игрок) улетит о-о-чень далеко, не получив ни единой царапины. По ходу дела мы еще введем парочку изменений, но общая идея останется прежней. Мутатор не претендует ни на что особенное, он просто выбран для примера. Назовем его - ZeroDamage.
Я не буду использовать UnrealED для написания кода, и вам не советую. Текст скрипта набивайте в любом удобном для вас редакторе. Итак, начнем.
Первое что вам надо знать - UT основан на системе паков (packages), т.е. игровые ресурсы (текстуры, звуки и пр.) распределяются по отдельным файлам - пакам. Например текстуры хранятся в *.utx файлах, звуки в *.uax. Скрипты тоже имеют свои паки - *.u файлы, причем у этих файлов есть одна особенность - кроме скриптов там могут хранится также модели (meshes), текстуры и звуки - эдакий универсальный формат (правда следует сказать что все паки построены по единому принципу и на одном формате, различие по расширениям введено для удобства).
Из всего вышесказанного делаем вывод - чтобы написать мутатор нам нужно создать свой пак со скриптом, что и делаем.
Создайте следующие два каталога:
UnrealTournament | +- ZeroPack | +- Classes
Каталог ZeroPack - это и есть наш будущий пак ZeroPack.u. В каталоге Classes будут храниться файлы скриптов (*.uc), в одном таком файле хранится реализация одного класса. Создайте в этом каталоге файл ZeroDamage.uc - там и будет храниться текст нашего мутатора.
В созданном ZeroDamage "набейте" следующий код:
//------------------------------------ // ZeroDamage mutator //------------------------------------ // Наследуем от класса Mutator class ZeroDamage extends Mutator; var bool initialized; // Эта функция вызывается при инициализации любого актора function PostBeginPlay() { if(initialized) return; initialized = true; // Регистрация damage мутатора Log("------ !c00l! ZeroDamage mutator !c00l! ------"); Level.Game.RegisterDamageMutator(Self); } // Эта функция вызывается для зарегистрированных damage мутаторов // перед подсчетом финального ущерба function MutatorTakeDamage(out int ActualDamage, Pawn Victim, Pawn InstigatedBy, out Vector HitLocation, out Vector Momentum, name DamageType) { // Сообщения для отладки broadcastmessage("Killer: "$InstigatedBy.PlayerReplicationInfo.PlayerName); broadcastmessage("Victim: "$Victim.PlayerReplicationInfo.PlayerName); broadcastmessage("ActualDamage: "$ActualDamage); // Увеличиваем величину момента удара (это вектор), не меняя самого вектора Momentum.x+=ActualDamage; Momentum.y+=ActualDamage; Momentum.z+=ActualDamage; // Обнуляем полученные повреждения ActualDamage = 0; // Передаем изменившиеся параметры следующему damage мутатору if ( NextDamageMutator != None ) NextDamageMutator.MutatorTakeDamage(ActualDamage, Victim, InstigatedBy, HitLocation, Momentum, DamageType ); } defaultproperties { } |
Некоторые пояснения. Первое, зачем следующая строчка:
if (initialized) return; |
До недавнего времени это была одна из особенностей мутаторов (да и вообще всех акторов), для них (Post)BeginPlay функции вызывалась два раза, чтобы код инициализации не выполнялся дважды - мы поставили это простое условие. В последних версиях движка это пофиксено (начиная с 413), однако не у всех игроков всегда последняя версия UT, так что это условие стоит оставить.
Level.Game.RegisterDamageMutator(Self); |
Переменная Level типа LevelInfo - указывает на текущий уровень (данная переменная определена в классе Actor), в классе LevelInfo определена переменная Game типа GameInfo - указатель на текущий тип игры (DeathMatchPlus, CTF и пр.). В GameInfo - определена функция RegisterDamageMutator - которую мы и вызываем, передавая в качестве параметра Self (ссылку на наш мутатор). Регистрировать нам нужно, чтобы движок вызывал функцию MutatorTakeDamage для нашего мутатора. Данная функция вызывается каждый раз, когда какой-либо игрок получил повреждения. Параметры этой функции ясны из их названий, нас пока интересуют: ActualDamage - непосредственный ущерб и Momentum - вектор удара.
BroadcastMessage - текстовые сообщения во время игры, мы используем эту функцию для отладки (просмотр значений). Log - текстовые сообщения в лог игры, здесь также используется для отладки.
Далее, изменив интересующие нас параметры, мы передаем параметры следующему damage мутатору, если таковой есть.
if ( NextDamageMutator != None ) NextDamageMutator.MutatorTakeDamage(ActualDamage, Victim, InstigatedBy, HitLocation, Momentum, DamageType ); |
Теперь пора создать соответствующий пак. Откройте файл UnrealTournament.ini, в нем найдите раздел [Editor.EditorEngine] В этом разделе ищите строчки вида EditPackages=... и в самом конце добавьте строчку EditPackages=ZeroPack, должно быть что-то типа:
[Editor.EditorEngine] ... EditPackages=Botpack EditPackages=UTServerAdmin EditPackages=UTMenu EditPackages=UTBrowser EditPackages=ZeroPack
Теперь запустите ucc make (программа UCC находится в каталоге UT\System). Если вы все сделали правильно, UCC должен выдать что-то типа:
... --------------------ZeroPack-------------------- Analyzing Parsing ZeroDamage Compiling ZeroDamage Success - 0 error(s), 0 warnings
В системном каталоге UT должен появиться файл ZeroPack.u
Остался последний штрих. Нам надо чтобы UT находил наш мутатор и добавлял его в список мутаторов. Для этого создайте файл ZeroPack.int, в который добавьте строчки:
[Public] Object=(Name=ZeroDamage.ZeroDamage,Class=Class,MetaClass=Engine.Mutator,Description="Zero Damage, c00l mutator!") |
Примечание №1: Каждый раз, когда вы будете компилировать пак при помощи ucc, предварительно удаляйте ZeroPack.u из системного каталога UT, иначе компилятор не будет создавать обновленную версию пака.
Примечание №2:Есть достаточно удобные программы автоматизирующие процесс компиляции, смотрите раздел "Файлы" на нашем сайте.
Протестировав мутатор, вы наверное заметили что игрок абсолютно бессмертен, хоть по лаве гуляй или с 10 этажа падай - высвечивается огромный дамаж и все. Пора ввести парочку усовершенствований в наш мутатор.
Во-первых, мы будем проверять свойство InstigatedBy (тот кто нанес дамаж), и во-вторых, мы будем уменьшать повреждения при падении с высоты, но не обнулять его. Изменения в коде мутатора выделены:
function MutatorTakeDamage(out int ActualDamage, Pawn Victim, Pawn InstigatedBy, out Vector HitLocation, out Vector Momentum, name DamageType) { // Увеличиваем момент Momentum.x+=ActualDamage; Momentum.y+=ActualDamage; Momentum.z+=ActualDamage; // Обнуляем полученные повреждения только если киллер - игрок if (InstigatedBy.IsA('Pawn')) ActualDamage=0; // Если падение с высоты, то дамаж уменьшаем, но не обнуляем if (DamageType == 'Fell') ActualDamage/=2; // Передаем параметры следующему damage мутатору if ( NextDamageMutator != None ) NextDamageMutator.MutatorTakeDamage(ActualDamage, Victim, InstigatedBy, HitLocation, Momentum, DamageType ); } |
Мы используем метод IsA для проверки принадлежности InstigatedBy к классу Pawn (это игрок, боты, стационарные пушки и монстряки) и обнуляем ActualDamage. Если это будет что-то иное - ActualDamage останется прежним. К тому же, мы еще проверяем тип дамажа, если игрок упал с высоты - то ActualDamage уменьшается в два раза. Если вы хотите, чтобы игроку наносились его же повреждения, то можно написать что-то типа:
if (Victim != InstigatedBy && InstigatedBy.IsA('Pawn')) ActualDamage=0; |
Исходный код обеих версий мутатора ZeroDamage можно взять здесь.
Вот собственно и все. Изучайте и испытывайте, ничего сложного здесь нет. Испробовав парочку таких штучек вы будете "щелкать" мутаторы как орехи, самое трудное здесь - подобрать идею.
Напоследок, пара слов о применении мутаторов. Назначение мутаторов - небольшое изменение геймплея, в распоряжении мутатормейкера ряд эффективных, но ограниченных средств, т.к. различные мутаторы должны уметь работать вместе. Если у вас глобальные идеи, то мутаторы вам не помогут, тут есть другое средство - создание своего типа игры (GameInfo) - но это совсем другая история...
Автор: Shadow
Mail: shadow_m777@mail.ru