JASS – триггеры в виде кода.
Недавно я подумал, что уже довольно давно не писал статей и решил написать статью на тему триггеры в понимании JASS.
Собственно, вот то, что из этого вышло.
Итак, триггер. Вспомним, из чего он состоит.
- События.
- Условия.
- Действия.
И начнём мы с общего анализа триггера в текстовом виде. Для примера создадим триггер с названием
Sample. Добавим в него событие
Игрок красный выигрывает (
Игрок - Victory). Теперь – условие:
Игрока красного контролирует человек (
Player Controller Comparison). Ну и в качестве действия выведем на экран строку: «
Вы выиграли!!!» (действие
Игра – Text Message). Теперь у нас есть триггер. Переведя его в текст, мы получим это:
Code |
function Trig_Sample_Conditions takes nothing returns boolean <br /> if ( not ( GetPlayerController(Player(0)) == MAP_CONTROL_USER ) ) then <br /> return false <br /> endif <br /> return true <br /> endfunction <p> function Trig_Sample_Actions takes nothing returns nothing <br /> call DisplayTextToForce( GetPlayersAll(), "Вы выиграли!!!" ) <br /> endfunction <p> //======================================================== <br /> function InitTrig_Sample takes nothing returns nothing <br /> set gg_trg_Sample = CreateTrigger( ) <br /> call TriggerRegisterPlayerEventVictory( gg_trg_Sample, Player(0) ) <br /> call TriggerAddCondition( gg_trg_Sample, Condition( function Trig_Sample_Conditions ) ) <br /> call TriggerAddAction( gg_trg_Sample, function Trig_Sample_Actions ) <br /> endfunction |
У нас есть три функции. Начать стоит с самой последней (
InitTrig_Sample). Как видно, её название состоит из приставки
InitTrig_ и названия триггера.
InitTrig – инициализация триггера. В этой функции триггер регистрируется в игре. Это происходит в первой троке:
Code |
set gg_trg_Sample = CreateTrigger( ) |
gg_trg_Sample – это что то вроде глобальной переменной (
ubg_), но используется она только для триггеров. То есть создаётся новый триггер, который записывается в переменную.
Следующая строка – регистрация события. Собственно, чтобы добавить событие, нужно вызвать соответствующую функцию (в нашем случае
TriggerRegisterPlayerEventVictory) и передать в неё все параметры (
gg_trg_Sample, Player(0)).
*Примечание.
Событие Map initialization – особый случай и о нём мы поговорим позднее.
Далее идёт добавление условия:
Code |
call TriggerAddCondition( gg_trg_Sample, Condition( function Trig_Sample_Conditions ) ) |
Здесь принцип несколько другой, чем для добавления событий. Функция добавления условий одна (TriggerAddCondition). В неё мы передаём триггер и функцию с условиями. Теперь посмотрим на саму функцию с условиями.
Там всё чрезвычайно запутанно. Но ведь в этом и цель ВЕ – запутать всех и вся =). В конце концов весь этот код можно свести к одной строчке:
Code |
return GetPlayerController(Player(0)) == MAP_CONTROL_USER |
*Примечание.
Добавлять условия в функцию InitTrig_*** ВЕ будет только тогда, когда эти условия добавлены именно в блок с условиями триггера. Если же, например, мы использовали действие if / then / else, то функция с условиями создастся, но в функцию инициализации триггера она не добавится. В триггере можно создавать сколько угодно отдельных функций, которые не должны нигде указываться.
Добавлено (19-04-2007, 11:03 Pm)
---------------------------------------------
С условиями разобрались. Посмотрим теперь на действие. Собственно, здесь всё аналогично условию (за исключением названия функции =). Так что объяснять всё подробно нет смысла.
Вот вкратце об устройстве триггеров. Теперь немного поговорим об оптимизации. Я уже немного говорил об этом (условие), но теперь стоит сказать зачем это вообще надо.
Ну, первый факт, это конечно удобство. Ведь если каждое условие будет создавать новую функцию, в которой будут тонны ненужных операторов… Всё это в конце концов приведёт к хронической головной боли. Далее. Лишняя нагрузка – лишний геморрой. Да и к тому же гораздо проще разобраться в триггере (постороннему человеку) когда он решит поподробней рассмотреть наработку =).
Но вот хуже всего в ВЕ реализованы циклы, которые перебирают всех юнитов в определённой области. Предположим, мы хотим убить всех юнитов на карте. Опущу подробности такого сложного триггера =) и приведу его сразу на JASS:
Code |
function Trig_KillAllUnit_Func001A takes nothing returns nothing <br /> // Здесь действия цикла <br /> call KillUnit( GetEnumUnit() ) <br /> endfunction <p> function Trig_KillAllUnit_Actions takes nothing returns nothing <br /> // Здесь действия функции <br /> call ForGroupBJ( GetUnitsInRectAll(GetPlayableMapRect()), function Trig_KillAllUnit_Func001A ) <br /> endfunction |
К сожалению, объединить данную конструкцию в одну функцию нельзя. Но можно сделать её несколь-ко красивее. Например, так:
Code |
function KILL takes nothing returns nothing <br /> call KillUnit(GetEnumUnit()) <br /> endfunction <p> function Trig_KillAllUnit_Actions takes nothing returns nothing <br /> call ForGroupBJ( GetUnitsInRectAll(GetPlayableMapRect()), function KILL ) <br /> endfunction |
В такие функции-циклы нельзя передавать параметры. То есть, для передачи данных в них надо использовать переменные (или аналогичные способы передачи данных, о которых я расскажу в следующих статьях).
Теперь отдельный разговор о событии Map Initialization. На самом деле, это даже не событие. Оно никак и нигде не регистрируется. Такие триггеры исполняются во время инициализации потоков игры. Соответственно серьёзные ошибки в таких триггерах могут привести к краху этих самых потоков.
И в конце ещё одно примечание.
Код из действия Custom Script остаётся неизменным при переводе с GUI на JASS.
Вот я и рассказал о триггерах. Если дойдут руки до следующей части о триггерах, опишу их создание динамически (что очень часто бывает чрезвычайно полезным). А пока на этом всё. Жду комментов.