Начну я, пожалуй, с небольшого лирического отступления. Примеры я буду давать на классическом Jass 2 от близзард (без надстроек типа cJass и vJass напару с ZINC). Теперь изучим суть MUI. MUI - это возможность одновременного каста одного и того же заклинания разными боевыми единицами (суть состоит в отсутствии перезаписи значений переменных, как это может быть с глобальными переменными). Это достигается за счет сохранения нужных локальных переменных в хэш (по правде, можно и не заносить некоторые ссылки в переменные и сохранять их сразу, но об этом позже). Пока у вас не разбежались глаза, я подкину нам практической работы. Давайте создадим способность, которая у нас будет работать триггерно. Для этого мы создадим способность на основе заклинания "Канал" (можно брать и любые другие, но лучший вариант: Канал для активных заклинаний и Бонус аттрибутов для пассивных). Настройке способности я внимания уделять не буду. Следующий шаг - редактор триггеров. Открываем. Давайте создадим переменную типа хэштаблица (hashtable) с незамысловатым названием hash. Создали? Отлично. Далее нам надо инициализировать ее, для этого в наш триггер инициализации надо вставить такую строчку [code=jass]set udg_hash = InitHashtable()[/code]
После этого действия мы сможем сохранять значения в хэш. Первая часть закончена!
Часть 2
Давайте создадим новый триггер и назовем его... Kill Unit. Незамысловато, но точно отражает суть того, на чем мы будем учиться. Думаю, что все уже поняли - мы будем создавать заклинание, которое будет убивать цель. В качестве события выбираем "Юнит привел способность в действие" (A unit Starts the effect of an ability), добавляем первое же условие (оно нужно для упрощения работы далее) и переводим триггер в текст при помощи "Правка - Перевести в текст" (Edit - Convert to Custom Text).
Лирическое отступление номер два: я не буду парить вам мозг о красоте кода и манере скриптописания, для этого вам стоит почитать статью Ajaccio, я стараюсь делать код читабельным, но не нагружать лишними символами, поэтому я рьярно избавляюсь от стандартного комментария между действиями и инициализацией, а так же от пробелов в функциях инициализации. В итоге мой "девственный" код превращается в это: [code=jass]function Trig_Kill_Unit_Conditions takes nothing returns boolean endfunction
function Trig_Kill_Unit_Actions takes nothing returns nothing endfunction
function InitTrig_Kill_Unit takes nothing returns nothing set gg_trg_Kill_Unit = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(gg_trg_Kill_Unit, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(gg_trg_Kill_Unit, Condition(function Trig_Kill_Unit_Conditions)) call TriggerAddAction(gg_trg_Kill_Unit, function Trig_Kill_Unit_Actions) endfunction[/code] После этих нехитрых манипуляций с кодом мы приступаем к реализации задуманного. Итак, для того, чтобы убить юнита, мы воспользуемся тривиальной функцией
[code=jass]call KillUnit(GetSpellTargetUnit())[/code] где GetSpellTargetUnit()- юнит-цель заклинания. Но сначала нам надо ввести условие срабатывания триггера. Для этого в условие мы пишем [code=jass]return GetSpellAbilityId() == 'A000'[/code] где 'A000'- равкод вашего заклинания (нажатие сочетания Ctrl+D в РО заменит названия заклинаний на их равкоды). В итоге наш код приобретет следующий вид:
function Trig_Kill_Unit_Actions takes nothing returns nothing call KillUnit(GetSpellTargetUnit()) endfunction
function InitTrig_Kill_Unit takes nothing returns nothing set gg_trg_Kill_Unit = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(gg_trg_Kill_Unit, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(gg_trg_Kill_Unit, Condition(function Trig_Kill_Unit_Conditions)) call TriggerAddAction(gg_trg_Kill_Unit, function Trig_Kill_Unit_Actions) endfunction[/code]
Сохраняем, тестируем. Работает? Ну куда ж ему деться с подводной лодки. Но вы спросите: "А где же тут муи?". Муи этого спелла - убийство юнита без занесения его в глобальную переменную. Никакой перезаписи, все отлично. Но давайте усложним задачу - убьем юнита через определенный промежуток времени. На гуи это решалось просто - "Ждать Н сек.", но это плохой способ. Очень. Поэтому юзаем таймер. Для начала создадим его: [code=jass]local timer t = CreateTimer()[/code] и возьмем его уникальный идентификатор (Ид хэндла): [code=jass]local integer hid = GetHandleId(t)[/code] на это значение мы будем сохранять нужные данные в хэш. Сохраняем юнита в хэш: [code=jass]call SaveAgentHandle(udg_hash, hid, 0, GetSpellTargetUnit())[/code] Замечу, что с помощью этой функции можно (и нужно) сохранять не только юнитов, но и группы, эффекты, таймеры, декорации и все остальные объекты. Далее запускаем таймер: [code=jass]call TimerStart(t, 3., false, function Trig_Kill_Unit_Timer)[/code] где t - наш таймер; 3. - время, через которое он истечет; false (или true) - периодичность таймера (true - периодичесские действия, false - однократный таймер); function Trig_Kill_Unit_Timer - функция, которая выполнится при истечении таймера. Не забываем обнулять локальные переменные: [code=jass]set t = null[/code] Далее нам надо написать само тело таймера. Займемся этим:
[code=jass]function Trig_Kill_Unit_Timer takes nothing returns nothing local timer t = GetExpiredTimer() local integer hid = GetHandleId(t) local unit target = LoadUnitHandle(udg_hash, hid, 0) call KillUnit(target) call FlushChildHashtable(udg_hash, hid) call DestroyTimer(t) set t = null set target = null endfunction[/code]
Глаза разбегаются? Изучаем! Функция тамера ничего не берет и не возвращает. Заносим в локальные переменные истекающий таймер (GetExpiredTimer()), берем его уникальный идентификатор (GetHandleId(t)). Дальше - интереснее: в локальную переменную загружаем юнита из хэша (call LoadUnitHandle()) и... Маньячно убиваем его. Далее странная функция.... Все просто - чистим хэш от всех сохраненных значений (call FlushChildHashtable(udg_hash, hid)), уничтожаем таймер (call DestroyTimer(t)) и обнуляем локальные переменные. Все! При касте спелла цель умрет через 3 секунды. Снова закономерный вопрос: "А где муи?". Отвечаю: муи данного спелла - сохранение юнита (точнее всех данных, но об этом дальше) в хэш на ид таймера. При каждом касте создается новый таймер, ид каждого уникален, перезаписи значений нет, а следовательно не будет стандартных "гуишных лагов". На этом я заканчиваю первую статью из своего мелкого цикла "Муи спеллы". Пожелания, вопросы и исправления - в тему. Всем спасибо за внимание! П.с. Извините за скудное оформление - нет во мне дизайнерской жилки :(
by Max Karelov aka Ty3uK
Просмотров: 2677
Добавил: Ty3uK
Добавлено: 15 Марта 2013 в 20:26:28
Оформление:5/5 Использованы картинки, цветовые выделения, ВВ-коды и спойлеры. В этом пункте придраться вообще не к чему в буквальном смысле слова. Несмотря на то, как автор заканчивает статью, я скажу, что жилка оформителя здесь проскакивает очень чётко.
Содержание:5/5 Лаконично и подробно. Тема раскрыта полностью (учитывая, что автор говорит о том, что возможно продолжение). Объём статьи соответствует норме.
Орфография:5/5 Всё написано строго согласно правилам русского языка.
Всего: 5/5 Таких статей надо больше! Модерацию статья проходит.
Добавлять комментарии могут только зарегистрированные пользователи. [ Регистрация | Вход ]
Tropical Towerwars интересная карта в которой нужно играть друг против друга, посылая противникам монстров, и строя башни отбиваться в свою очередь от их атак. Всё это лучше делать командами 3х3. Качайте, будет интересно.