|
Всего: 331
Новых за месяц: 1
Новых за неделю: 0
Новых вчера: 0
Новых сегодня: 0| Главная » Статьи » Различные статьи » Создание карт |
В статье мы рассмотрим самую распространённую область её применения в wc3: прикрепление данных к объекту, на простейшем примере. Статья предполагает, что читатель знаком с основами работы таймеров. Пример будет только на обычном Jass, для совместимости (да и не все умеют работать c v/cJass). Если вы знакомы с кэшем в wc3, то принцип работы с ним схож, с принципом работы с хеш-таблицей. Только вместо строковых ключей, хеш-таблица использует целочисленные значения (integer). Допустим, мы хотим создать спелл, в котором врагу на протяжении некоторого времени с малым периодом постоянно наносится урон (для которого wait не подходит). Мы создали триггер (в редакторе триггеров, для простоты объяснения) с событием каста, дали ему условия и действия: Code function Spell takes nothing returns nothing local unit caster = GetSpellAbilityUnit() //Кастер local unit target = GetSpellTargetUnit() //Цель endfunction //Проверка спелла function SpellCond takes nothing returns boolean return GetSpellAbilityId()=='A000' endfunction //=========================================================================== function InitTrig_Spell takes nothing returns nothing set gg_trg_Spell = CreateTrigger() call TriggerRegisterPlayerUnitEvent(gg_trg_Spell,Player(0),EVENT_PLAYER_UNIT_SPELL_CAST,null) call TriggerAddCondition(gg_trg_Spell,Condition(function SpellCond)) call TriggerAddAction(gg_trg_Spell,function Spell) endfunction Далее, нам нужен таймер, который будет периодически вызывать функцию, внутри которой наносится урон: Code function SpellDamage takes nothing returns nothing call UnitDamageTarget(...) endfunction function Spell takes nothing returns nothing local unit caster = GetSpellAbilityUnit() //Кастер local unit target = GetSpellTargetUnit() //Цель local timer t = CreateTimer() //Создаём таймер call TimerStart(t,0.04,true,function SpellDamage) //Стартуем таймер endfunction Но как передать в функцию, кто кому должен наносить урон, и сколько раз? Тут нам на помощь и приходит хеш-таблица. Перед работой нужно создать и инициализировать глобальную хеш-таблицу, желательно при инициализации карты. Хеш-таблица - очень массивный объект и занимает много места в памяти, поэтому рекомендуется создавать только одну на все действия в карте. В противном случае, игра просто может слететь с фаталом или зависнуть от переполнения. Делать это нужно только один раз, например в этом триггере: Code function InitTrig_Spell takes nothing returns nothing set gg_trg_Spell = CreateTrigger() call TriggerRegisterPlayerUnitEvent(gg_trg_Spell,Player(0),EVENT_PLAYER_UNIT_SPELL_CAST,null) call TriggerAddCondition(gg_trg_Spell,Condition(function SpellCond)) call TriggerAddAction(gg_trg_Spell,function Spell) set udg_hash = InitHashtable() //Инициализируем хеш-таблицу endfunction Если в карте имеется несколько спеллов, то рекомендуется инициализировать хеш-таблицу в отдельном действии/триггере при инициализации, чтобы избежать накладок во время редактирования или переноса. Работает хеш-таблица так: [ключ|значение]. Только как ключ мы используем уникальный id объекта, а точнее - id нашего таймера. На него и будем сохранять нужные нам данные: Code function Spell takes nothing returns nothing local unit caster = GetSpellAbilityUnit() //Кастер local unit target = GetSpellTargetUnit() //Цель local timer t = CreateTimer() //Создаём таймер local integer h = GetHandleId(t) //Узнаём id таймера //Сохраняем объекты с ключом - id таймера call SaveUnitHandle(udg_hash,h,1,caster) //Сохраняем кастера со значением 1 call SaveUnitHandle(udg_hash,h,2,target) //Сохраняем цель со значением 2 call SaveInteger(udg_hash,h,3,125) //Сохраняем количество ударов, из расчёта, что урон наносится в течение 5 секунд (5/0.04=125). call TimerStart(t,0.04,true,function SpellDamage) //Стартуем таймер //Не забываем устранять утечки set caster = null set target = null set t = null endfunction Также можно делать без создания переменной h: call SaveUnitHandle(udg_hash,GetHandleId(t),1,caster) Утечек это не вызовет, но немного снизит производительность. Аналогичными действиями сохраняются и другие объекты, например группа: call SaveGroupHandle(udg_hash,h,1,some_group) Думаю, нет смысла перечислять все функции, так как на это существуют function-листы. Некоторые утверждают, что сохранение объектов под ключами 1,2,3... неудобно и неуниверсально, и предлагают сохранять через уникальное значение строки: call SaveUnitHandle(udg_hash,h,StringHash("caster"),caster) С ними можно согласиться, вам не придётся давать и запоминать цифры для значений. Вы можете использовать такой способ для удобства. Готово, данные сохранены, теперь их можно будет достать в функции нанесения урона, на которую запущен таймер. Доставать данные мы будем тоже по id таймера: Code function SpellDamage takes nothing returns nothing local timer t = GetExpiredTimer() //Наш таймер - истёкший local integer h = GetHandleId(t) //Узнаём id таймера local unit caster = LoadUnitHandle(udg_hash,h,1) //Достаём кастера из значения 1 local unit target = LoadUnitHandle(udg_hash,h,2) //Достаём цель из значения 2 local integer counter = LoadInteger(udg_hash,h,3) //Достаём количество ударов if counter>0 then //Если количество ударов больше 0 call UnitDamageTarget(caster,target,1.0,true,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,null) //Наносим урон цели call SaveInteger(udg_hash,h,3,counter-1) //Сохраняем количество ударов, убавленное на 1 else //Иначе call DestroyTimer(t) //Уничтожаем таймер //Очищаем хеш-таблицу, чтобы избежать утечек и наложений call FlushChildHashtable(udg_hash,h) //Очищаем ключ по id endif //Не забываем устранять утечки set caster = null set target = null set t = null endfunction Очень часто встречается неправильная конструкция очистки, приводящая к утечкам: call DestroyTimer(t) call FlushChildHashtable(udg_hash,GetHandleId(t)) В данном случае, очистка произведена не будет, так как таймер уничтожается раньше получения его id, поэтому в функцию очистки будет подано неправильное значение (0). Если вы используете конструкцию без переменной с id, то делать нужно так: call FlushChildHashtable(udg_hash,GetHandleId(t)) call DestroyTimer(t) То есть сначала очищать, а потом удалять таймер. В случае с переменной, порядок этих действий значения не имеет. Спелл готов, данные записываются, достаются и удаляются из хеш-таблицы. Вот что у нас получилось в итоге: Code function SpellDamage takes nothing returns nothing local timer t = GetExpiredTimer() local integer h = GetHandleId(t) local unit caster = LoadUnitHandle(udg_hash,h,1) local unit target = LoadUnitHandle(udg_hash,h,2) local integer counter = LoadInteger(udg_hash,h,3) if counter>0 then call UnitDamageTarget(caster,target,1.0,true,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,null) call SaveInteger(udg_hash,h,3,counter-1) else call DestroyTimer(t) call FlushChildHashtable(udg_hash,h) endif set caster = null set target = null set t = null endfunction function Spell takes nothing returns nothing local unit caster = GetSpellAbilityUnit() local unit target = GetSpellTargetUnit() local timer t = CreateTimer() local integer h = GetHandleId(t) call SaveUnitHandle(udg_hash,h,1,caster) call SaveUnitHandle(udg_hash,h,2,target) call SaveInteger(udg_hash,h,3,125) call TimerStart(t,0.04,true,function SpellDamage) set caster = null set target = null set t = null endfunction function SpellCond takes nothing returns boolean return GetSpellAbilityId()=='A000' endfunction //=========================================================================== function InitTrig_Spell takes nothing returns nothing set gg_trg_Spell = CreateTrigger() call TriggerRegisterPlayerUnitEvent(gg_trg_Spell,Player(0),EVENT_PLAYER_UNIT_SPELL_CAST,null) call TriggerAddCondition(gg_trg_Spell,Condition(function SpellCond)) call TriggerAddAction(gg_trg_Spell,function Spell) set udg_hash = InitHashtable() endfunction | |
| Просмотров: 587 | Рейтинг: 0.0/0 |
| Всего комментариев: 0 | |