WarCraft 3: 6-7. Устройство триггера в jass

Осваиваем jass (0-1)

6. Устройство триггера с точки зрения jass

Теперь, когда ты уже изучил функции, остановимся подробнее на устройстве триггера. Я уже говорил, что при переводе триггера в текст, он преобразуется в несколько функций. Но что же такое триггер? Просто несколько jass функций? Не совсем так. Правильнее сказать триггер, все его события, условия, действия СОЗДАЮТСЯ при помощи jass-функций. Функции сами по себе, а триггер как бы объединяет их в единую структуру.
Давай рассмотрим этот процесс. Возьмем какой-нибудь триггер:
Триггер sample
События
    Every 5.00 seconds of game time
Условия
    ((Triggering unit) is Здание равно Да
    (Ability being cast) равно «Гальванизация»
Действия
    Wait 2.00 game-time seconds
    Play (no unit)'s stand animation
Вообще говоря, бессмысленный триггер, но важно не это. Во что он превратится, когда мы переведем его в jass? В следующий код:
function Trig_sample_Conditions takes nothing returns boolean
    if ( not ( IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) == true ) ) then
        return false
    endif
    if ( not ( GetSpellAbilityId() == 'AUan' ) ) then
        return false
    endif
    return true
endfunction

function Trig_sample_Actions takes nothing returns nothing
    call PolledWait( 2 )
    call SetUnitAnimation( null, "stand" )
endfunction

//===========================================================================
function InitTrig_sample takes nothing returns nothing
    set gg_trg_sample = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_sample, 5.00 )
    call TriggerAddCondition( gg_trg_sample, Condition( function Trig_sample_Conditions ) )
    call TriggerAddAction( gg_trg_sample, function Trig_sample_Actions )
endfunction
Первая функция - это то во что превратились условия триггера. Можешь проверить, что эта функция возвращает значение ИСТИНА, если условия исходного триггера будут выполняться, и ложь в противном случае. Вторая функция - действия триггера. Об этом и так можно догадаться, если глянуть на названия триггеров "Trig_sample" - т.е. триггер с названием sample "_Conditions" - условия, "_Actions" - действия.
Что касается третьей функции, то у нее свое особое назначение. Посмотри на название "InitTrig_sample". Приставка Init - напоминает слово Initialization, т.е. это что-то связанное с загрузкой карты. Эта функция запускается при инициализации карты. Ее назначение - собрать наш триггер воедино, объединив события, условия и действия. Сейчас посмотрим на строки:
set gg_trg_sample = CreateTrigger(  ) 
Что-то к чему-то прировняли... gg_trg_sample - это разновидность глобальной переменной типа ТРИГГЕР, которая будет отвечать за хранение нашего триггера в памяти компьютера во время игры. Такие переменные автоматически создаются, когда ты создаешь в редакторе новый триггер.
В начале игры все такие переменные пустые. Действие set gg_trg_sample = CreateTrigger( ) приводит к тому, что в игре создается НОВЫЙ ТРИГГЕР - настоящий триггер, который до этого не существовал. В нем пока нет ни условий, ни событий, ни действий. При этом переменная gg_trg_sample будет ссылаться на этот триггер.
Далее идет строка
call TriggerRegisterTimerEventPeriodic( gg_trg_sample, 5.00 ) 
Эта команда приводит к тому, что к нашему пустому триггеру добавляется событие "Every 5.00 seconds of game time". Т.е. у нашего триггера уже есть событие. Для добавления любого события есть своя команда, которую ты можешь посмотреть при переводе триггера в текст. Исключение Map Initialization, но это отдельный разговор.
Далее
call TriggerAddCondition( gg_trg_sample, Condition( function Trig_sample_Conditions ) ) 
Это специальная команда, которая добавляет нашему триггеру условие. Заметь, в качестве аргументов мы указываем триггер и функцию, в которой записано условие триггера.
Далее
call TriggerAddAction( gg_trg_sample, function Trig_sample_Actions ) 
По аналогии с предыдущей командой - эта добавляет в триггер действия.
Когда при загрузке карты будет автоматически выполнена функция InitTrig_sample, только тогда объект триггер будет загружен в память компьютера и начнет работать. Вот такая механика.
Частенько структура триггера может быть сложнее, чем в описанном примере. Например, применение условного оператора (в триггерном виде), действий типа Pick every unit and do <action> приводит к тому, что в триггере создаются новые функции - события и действия. Бывает, что триггер так загромождается ими, что при переводе в jass трудно разобраться, что к чему. Иногда код можно оптимизировать и сделать более простым.
Еще такой интересный вопрос: если мы создаем триггеры при загрузке карты, не можем ли мы воспользоваться теми же командами, чтобы создавать триггер ПРЯМО ВО ВРЕМЯ игры. Ответ положительный - можем. И иногда это бывает очень удобно.
Читатель, если ты прочитал все что было до этого и разобрался в этом, то считай, что ты уже не новичок. Хотя еще не хватает практики собственной работы. И теперь мы сможем приступить к изучению продвинутого jass, который значительно расширяет возможности создания сценариев.

7. Динамическое создание триггера

Читатель, давай рассмотрим пример, который покажет некоторые возможности jass, недоступные в редакторе триггеров.
Вот например, известно, что определить, когда юнит получает повреждения можно лишь при событии Unit takes damage, которое можно создать лишь для конкретного юнита. Жуть как неудобно. А если возникает задача по ходу игры узнать, когда ударили юнит и сколько повреждений нанесли? Попробуем решить эту задачу исходя из того, что мы узнали о jass. Предлагаю скачать пример, который я выслал в данном сообщении и посмотреть его устройство.
Пример называется Magic shield. В нем реализовано заклинание волшебной брони для создания защиту, которая поглощает определенное число повреждений юнита, а затем исчезает. Причем юнитов с броней может быть сколько угодно. Как это реализовать?
Если мы можем динамически создавать новые триггеры прямо по ходу игры, почему бы не сделать так, что когда юнит применяет заклинание защиты, мы СОЗДАДИМ триггер, который будет отлавливать, повреждения. Делается это достаточно просто:
делаем триггер Magic Shield, который сработает при произнесении заклинания брони. Для юнита создается триггер с событием unit takes damage где в качестве проверяемого юнита выступает кастер. В качестве действия для нового триггера, нужно указать какую-нибудь функцию. Я использовал функцию "Adv_Trig_Actions", которая записана в специальном месте для пользовательских функций. Эта функция определяет действия, которые произойдут, когда юнит получит повреждения.
Правда имеются с триггером и некоторые сложности. Во-первых, где-то надо хранить информацию о том, что у такого-то юнита имеется такой-то запас брони. Локальные переменные тут не годятся, т.к. информацию мы сохраняем в одном триггере, а действие реализовано в другом. Поэтому в качестве хранилища информации пришлось использовать массивы. При применении заклинания, в массив MS_units заносится кастер, в массив MS_power заносится количество повреждений, которые может поглотить броня, в массив MS_trigs заносится триггер, созданный для отлавливания повреждений кастера. В переменной MS_num – общее число юнитов с данной защитой. Т.е. к примеру, произнес юнит заклинание брони. Мы проверяем. Не ли такого уже в массиве. Если нет – увеличиваем MS_num на 1, а затем заносим данные об этом новом юните в элементы массивов MS_units[MS_num], MS_power[MS_num] , MS_trigs[MS_num] . Если же юнит уже находится внутри массива под номером N, это значит он уже применил ранее заклинание щита. Тогда нам не нужно заново создавать для него триггер, отлавливающий повреждения. Мы просто обновим уровень защиты в переменной MS_power[N] .
В этом смыл действий основного триггера, который происходит во время применения заклинания защиты. А как насчет дополнительного триггера, создаваемого по ходу игры?
Прежде всего, когда мы отловили дополнительным триггером нанесенные повреждения юниту, нужно найти номер этого юнита в массиве. Скажем, его номер N. Далее, мы сопоставляем полученные юнитом повреждения и уровень защиты юнита MS_power[N] . Если защиты больше – мы просто уменьшаем уровень защиты на количество повреждений, а затем восстанавливаем юниту потерянную жизнь. Если же уровень защиты меньше количества повреждений, то мы должны восстановить юниту число повреждений, равное остатку защиты, после чего удалить триггер MS_trigs[N] .
Чтобы удалить данные из N-того элемента массива, мы просто заменяем значение N-того элемента на значения последнего элемента с номером MS_num. После чего уменьшаем MS_num на 1 – ведь элементов стало меньше.
Кстати, при каждом ударе по юниту с защитой, также появляется спецэффект.
Обрати внимание на действие
call DestroyTrigger(<ссылка на триггер>)
У него нет аналога для обычных триггеров. Это действие уничтожает выбранный триггер, убирая его из памяти компьютера прямо во время игры.
Вообще-то пример получился не очень простым. Для работы заклинания требуются 3 массива и еще одна переменная. При каждом запуске или ударе по юниту с броней происходит цикл, в котором проверяется, есть ли такой-то юнит в массиве... Неудобно. Но что делать – как напрямую сопоставить юниту какие-то значения? Вообще-то сопоставить можно. Есть custom value, но для нашего примера его явно недостаточно.
На самом деле есть на jass есть замечательный прием, который позволяет сопоставить любому игровому объекту какие-то значения. Но для того, чтобы узнать как, нам придется углубиться в jass. И тогда мы сможем этот пример значительно улучшить.

Просмотров: 21 019

» Лучшие комментарии


sonicscream #1 - 7 лет назад 3
Архив в котором пример повреждён 8(
перезалейте пожалуста
Kakolookia #2 - 6 лет назад 1
У меня все норм)
map_maiker #3 - 5 лет назад 1
гуд статья
AlisherYch #4 - 5 лет назад 1
Очень полезная статья! Спасибо :)
z1i2p3 #5 - 4 года назад 1
сложнно описано, первые статьи были намноого легче для восприятия.
ScorpioT1000 #6 - 4 года назад 1
z1i2p3:
сложнно описано, первые статьи были намноого легче для восприятия.
Почитай тогда это
Gerhop #7 - 3 года назад 1
По-идее,если хп юнита с броней меньше атаки,то он умрет,не успев использовать щит.Разве нет?Ведь идет восстановление после нанесения урона...
nvc123 #8 - 3 года назад (отредактировано ) 1
Gerhop, нанесение урона и отнимание хп это 2 разные вещи
сначала наносится урон
и только через 0,001 секунды отнимаются хп
так что мы восстанавливаем хп до того как он умрёт