XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Академия: форум для вопросов> Желтая пресса: обучающие статьи
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

 
JaBeN_Симфер

offline
Опыт: 20,050
Активность:
Юнит атакован - Юнит получает урон
Изменено
Данная статья подробно опишет способ избавления от утечек при использовании событий Юнит атакован и Юнит получает урон в связке.
Суть в том, что на ГУИ в данном случае при каждой атаке мы создаем новый триггер и новое действие, которые засоряют память, если их не удалять.
Статья требует минимальные знания Jass, чтобы уметь применить этот способ в своей карте.

1. Начало

Создаем новую карту, удаляем стандартный триггер и создаем новый триггер event damaged.
Добавляем в него событие юнит атакован и конвертируем в jass. Получаем:
function Trig_event_damaged_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_event_damaged takes nothing returns nothing
    set gg_trg_event_damaged = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_event_damaged, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddAction( gg_trg_event_damaged, function Trig_event_damaged_Actions )
endfunction
Теперь мы создадим действие, которое будет выполняться при получении урона по атакованному юниту, и условие для выполнения этого действия, и поместим их выше нашего триггера:
function Add_Damage takes nothing returns nothing
// наши действия
endfunction

function Tr_Conditions takes nothing returns boolean
// наше условие
    return true
endfunction

function Trig_event_damaged_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_event_damaged takes nothing returns nothing
    set gg_trg_event_damaged = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_event_damaged, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddAction( gg_trg_event_damaged, function Trig_event_damaged_Actions )
endfunction
Теперь мы создадим действия, которые будут создавать триггер с действиями в Add_Damage и условием Tr_Conditions после события атаки.
function Add_Damage takes nothing returns nothing
// наши действия
endfunction

function Tr_Conditions takes nothing returns boolean
// наше условие
    return true
endfunction

function Trig_event_damaged_Actions takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterUnitEvent(t, GetTriggerUnit(), EVENT_UNIT_DAMAGED)
    call TriggerAddCondition(t, Condition(function Tr_Conditions))
    call TriggerAddAction(t, function Add_Damage)
    set t = null
endfunction

//===========================================================================
function InitTrig_event_damaged takes nothing returns nothing
    set gg_trg_event_damaged = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_event_damaged, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddAction( gg_trg_event_damaged, function Trig_event_damaged_Actions )
endfunction
Данный код можно было выполнить и на ГУИ, с этим ни у кого проблем не возникнет, а проблемы начинаются дальше, когда мы замечаем, что количество утечек быстро растет.
Теперь перед нами возникает задача по удалению созданного триггера и действия после того, как он нам уже не нужен.

2. Сохранение и удаление

Т.к. у атакующего юнита в один момент может быть только одна цель, то мы можем привязать наши действия к его GetHandleId, чтобы потом их удалить.
Нам нужно создать глобальную переменную Hash (в jass udg_Hash) типа hashtable,
создать триггер с событием инициализации, и вставить в Custom script эту строку, чтобы инициализировать таблицу:
set udg_Hash = InitHashtable()
Мы будем сохранять в Hash созданный триггер и действие:
function Trig_event_damaged_Actions takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer h2i = GetHandleId(GetAttacker())
// мы получили номер в массив из уникального номера юнита.
    call TriggerRegisterUnitEvent(t, GetTriggerUnit(), EVENT_UNIT_DAMAGED)
    call SaveTriggerHandle(udg_Hash, h2i, 1, t)
    call TriggerAddCondition(t, Condition(function Tr_Conditions))
    call SaveTriggerActionHandle(udg_Hash, h2i, 2, TriggerAddAction(t, function Add_Damage))
    set t = null
endfunction
Числа 1 и 2 в call SaveTriggerHandle(udg_Hash, h2i, 1, t) и SaveTriggerActionHandle(udg_Hash, h2i, 2, TriggerAddAction(t, function Add_Damage)) могут быть другими - это как пример. Мы просто используем ключ ячейки Хеш-таблицы.
Сейчас нам надо сделать удаление сохраненных действий после того, как выполнятся все действия при получении урона.
Для этого создадим новую функцию и будем ее использовать в коде:
function Remove_Trigger takes integer h2i returns nothing
    local trigger t
    if HaveSavedHandle(udg_Hash, h2i, 1) then
        set t = LoadTriggerHandle(udg_Hash, h2i, 1)
        call TriggerRemoveAction(t, LoadTriggerActionHandle(udg_Hash, h2i, 2))
        call DestroyTrigger(t)
        call RemoveSavedHandle(udg_Hash, h2i, 1)
        call RemoveSavedHandle(udg_Hash, h2i, 2)
        set t = null
    endif
endfunction

function Add_Damage takes nothing returns nothing
    local integer h2i = GetHandleId(GetEventDamageSource())

// наши действия
//    local real damage = GetEventDamage()
//    local unit u = GetEventDamageSource()
//    call DisplayTextToPlayer(Player(0), 0.0, 0.0, "+") // проверочное сообщение
//----------------
//    set u = null

    call Remove_Trigger(h2i)
// После нанесения урона выполнится удаление.
endfunction

function Trig_event_damaged_Actions takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer h2i = GetHandleId(GetAttacker())
    call Remove_Trigger(h2i)
// При атаке удалятся предыдущие действия для этого юнита, если они есть.
    call TriggerRegisterUnitEvent(t, GetTriggerUnit(), EVENT_UNIT_DAMAGED)
    call SaveTriggerHandle(udg_Hash, h2i, 1, t)
    call TriggerAddCondition(t, Condition(function Tr_Conditions))
    call SaveTriggerActionHandle(udg_Hash, h2i, 2, TriggerAddAction(t, function Add_Damage))
    set t = null
endfunction
Теперь сделаем такое условие, чтобы не выполнялись действия, если:
  • урон равен 0.0 - это отловит все промахи, нулевые и лишние атаки
  • игрок атакующего юнита не равен игроку атакованного юнита
function Tr_Conditions takes nothing returns boolean
    return R2I(GetEventDamage()) > 0.0 and GetOwningPlayer(GetEventDamageSource()) != GetOwningPlayer(GetTriggerUnit())
endfunction
Оптимизируем событие Юнит атакован, раскрыв BJ-функцию
function InitTrig_event_damaged takes nothing returns nothing
    local integer index = 0
    set gg_trg_event_damaged = CreateTrigger()
    loop
        call TriggerRegisterPlayerUnitEvent(gg_trg_event_damaged, Player(index), EVENT_PLAYER_UNIT_ATTACKED, null)
        set index = index + 1
        exitwhen index == 16
    endloop
    call TriggerAddAction(gg_trg_event_damaged, function Trig_event_damaged_Actions)
endfunction
У нас получился такой код:
function Remove_Trigger takes integer h2i returns nothing
    local trigger t
    if HaveSavedHandle(udg_Hash, h2i, 1) then
        set t = LoadTriggerHandle(udg_Hash, h2i, 1)
        call TriggerRemoveAction(t, LoadTriggerActionHandle(udg_Hash, h2i, 2))
        call DestroyTrigger(t)
        call RemoveSavedHandle(udg_Hash, h2i, 1)
        call RemoveSavedHandle(udg_Hash, h2i, 2)
        set t = null
    endif
endfunction

function Add_Damage takes nothing returns nothing
    local integer h2i = GetHandleId(GetEventDamageSource())
//    local real damage = GetEventDamage()
//    local unit u = GetEventDamageSource()
//    call DisplayTextToPlayer(Player(0), 0.0, 0.0, "+")
//----------------
//    set u = null
    call Remove_Trigger(h2i)
endfunction

function Tr_Conditions takes nothing returns boolean
    return R2I(GetEventDamage()) > 0.0 and GetOwningPlayer(GetEventDamageSource()) != GetOwningPlayer(GetTriggerUnit())
endfunction

function Trig_event_damaged_Actions takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer h2i = GetHandleId(GetAttacker())
    call Remove_Trigger(h2i)
    call TriggerRegisterUnitEvent(t, GetTriggerUnit(), EVENT_UNIT_DAMAGED)
    call SaveTriggerHandle(udg_Hash, h2i, 1, t)
    call TriggerAddCondition(t, Condition(function Tr_Conditions))
    call SaveTriggerActionHandle(udg_Hash, h2i, 2, TriggerAddAction(t, function Add_Damage))
    set t = null
endfunction

//===========================================================================
function InitTrig_event_damaged takes nothing returns nothing
    local integer index = 0
    set gg_trg_event_damaged = CreateTrigger()
    loop
        call TriggerRegisterPlayerUnitEvent(gg_trg_event_damaged, Player(index), EVENT_PLAYER_UNIT_ATTACKED, null)
        set index = index + 1
        exitwhen index == 16
    endloop
    call TriggerAddAction(gg_trg_event_damaged, function Trig_event_damaged_Actions)
endfunction

3. Мертвяки

Чтобы не хранить сохраненные действия мертвых юнитов, мы создадим еще один триггер, который будет их отчищать.
Создаем триггер death, добавляем событие юнит умирает, а в действиях вставляем в Custom Script это:
call Remove_Trigger(GetHandleId(GetDyingUnit()))

4. Утечки

Для того, чтобы видеть, есть ли у нас утечки памяти или нет, существует такая конструкция, которая показывает увеличение количества объектов на карте.
Для этого создадим глобальную переменную i_test типа integer. В нее будет заноситься максимальное число объектов на карте.
Создадим пустой триггер и назовем его check, конвертируем его в jass и вставляем в него этот код:
function Trig_check_Actions takes nothing returns nothing
    local timer Timer = CreateTimer()
    local string S
    local integer i = 0
    if udg_i_test < GetHandleId(Timer) then // !=  <
        set S = ("|cffaaaaaa [Test] H: " + I2S(GetHandleId(Timer) - 1048500 + "|r") // 1048500 - число, нужное для уменьшения числа, выводимого на экран
        call DisplayTextToPlayer(Player(0), 0, 0, S) // сообщение выводится для игрока 1 Player(0)
        set S = null
        set udg_i_test = GetHandleId(Timer)
    endif
    call DestroyTimer(Timer)
    set Timer = null
endfunction

//===========================================================================
function InitTrig_check takes nothing returns nothing
    set gg_trg_check = CreateTrigger()
    call TriggerRegisterTimerEvent(gg_trg_check, 0.1, true)
    call TriggerAddAction(gg_trg_check, function Trig_check_Actions)
endfunction
В игре будут выводиться цифры, если максимальное количество объектов увеличилось.
Триггер будет запускаться каждые 0.1 секунду, этого вполне достаточно, чтобы точно определить наличие утечек.
Прикрепленные файлы
Тип файла: w3x Event Damaged 2 (Hash).w3x (19.0 Кбайт, 193 просмотров )

Отредактировано JaBeN_Симфер, 20.03.2010 в 22:47.
Старый 16.03.2010, 21:01
FEARSTARTER
desert eagle
offline
Опыт: 19,364
Активность:
Полезная статья, год назад она можетбыть спаслабы одну мою карту...
Старый 16.03.2010, 22:00
Alonix
*null*
offline
Опыт: 26,861
Активность:
Хорошая статья для тех,кто задает вопросы:"как избавиться от утечек" и т.п.А также она может помочь обычным картостроителям.В общем,время потраченое на статью потрачено не зря!
Старый 17.03.2010, 05:14
FellGuard
Losyash
offline
Опыт: 39,547
Активность:
Диапазона от 0 до 8192
до 8191
Стоило бы ещё добавить что триггер полностью из памяти всё равно не удаляется, так что перегружать этот механизм не надо, дальше:
constant function Name takes nothing returns integer
endfunction
Обычно заменяется на
constant integer Name = ...
Надо сказать, что массивы udg_Trigger, udg_Condition, udg_Action вообще не нужны, т.к. есть GetTriggeringTrigger, GetEventDamageSource, GetTriggerUnit, этих ф-ий вполне достаточно чтобы приаттачить напрямую к объекту всё что нужно без использования ненадёжной (в данном случае, так как переполнение никак не фиксируется и не "рехешируется") системы массивов. Так что велосипед лучше не изобретать, структуры и/или хеш-таблицы для этого есть.
FellGuard добавил:
Если автор заменит в статье кривые массивы на хеш-таблицы/структуры, статью можно в базу, так-то она хорошая.
Старый 17.03.2010, 08:11
Doc

offline
Опыт: 63,163
Активность:
Да Ну... У меня на группах без хеша/массивов и все работает.
Старый 17.03.2010, 08:45
FellGuard
Losyash
offline
Опыт: 39,547
Активность:
что у тебя на группах? аттач триггера unit takes damage куда и как?
Старый 17.03.2010, 08:49
Doc

offline
Опыт: 63,163
Активность:
Нет. Просто при добавлении события проверяется наличие в группе если нет, то добавляем в группу. Через нужное время тригер пересоздаем.
Старый 17.03.2010, 09:04
XOR

offline
Опыт: 38,159
Активность:
Многабукф ниачем. Статья не нужна имхо, хороший жассер дойдет сам, а гуишники получят еще повод изучить жасс.
Старый 17.03.2010, 09:19
zero index
Фантома же!
offline
Опыт: 886
Активность:
статья очень пригодилась но не по своему назначению, а как урок джасса) все что в статье постараюсь запомнить, автору спс...
Старый 17.03.2010, 14:11
DioD

offline
Опыт: 45,134
Активность:
говно запостили, данный пример это то как НИ В КОЕМ СЛУЧАЕ нельзя делать.
мало того что куча совершенно ненужного, так еще и технология отстала на 5 лет, учитесь у буржуев.
  1. Юнит входит на карту, мы его записываем.
  2. Юнит умер, мы его удаляем.
  3. Если юнита можно воскресить мы его удаляем не сразу, а через 89 секунд (смотреть константы) если его тип вернулся как нолик, это говорит о том, что юнит удалён и не будет воскрешен никогда уже.
  1. PROFIT
ах да, это всё делается одним триггеров в джаз и двумя на гуи.
Старый 17.03.2010, 15:34
JaBeN_Симфер

offline
Опыт: 20,050
Активность:
constant integer Name = ...
Это в JNGP? ну тогда сами исправьте, т.к. это не существенно, да и понадобился он тут только для объявления 2х глобалок.
FellGuard:
Надо сказать, что массивы udg_Trigger, udg_Condition, udg_Action вообще не нужны, т.к. есть GetTriggeringTrigger, GetEventDamageSource, GetTriggerUnit, этих ф-ий вполне достаточно чтобы приаттачить напрямую к объекту всё что нужно без использования ненадёжной (в данном случае, так как переполнение никак не фиксируется и не "рехешируется") системы массивов.
Буду рад, если ты поможешь, а точнее объяснишь принцип реализации.
Многабукф ниачем. Статья не нужна имхо, хороший жассер дойдет сам, а гуишники получят еще повод изучить жасс.
Если бы кто-то в свое время написал эту статью для меня, я был бы очень благодарен, т.к. сам зная джасс долго не мог найти решение, и уверен, что найдутся те, кто еще не раз задаст вопрос о том, как тут избавиться от утечек. Хорошая статья или плохая - ваше мнение, в любом случае, она наталкивает на определенные мысли, а способ реализации - это дело каждого. Я же не сказал, что это единственный и идеальный способ, просто для меня он удобен по сей день.
DioD, ты напиши пример, и всех научи, как надо делать, а то мы тут все отстали на 5 лет, а другой статьи на эту тему пока нет.
Сейчас ввел в поиск "юнит атакован" и на первой же странице нашел более 10 близких вопросов, кроме этого можно найти еще множество конкретных вопросов по этой теме.
К тому же теперь есть конкретный пример, на который можно направить тех, кто задает вопрос: как узнать, если у меня утечки или нет?
Для того, чтобы видеть, есть ли у нас утечки памяти или нет, существует такая конструкция, которая показывает увеличение количества объектов на карте.
Если доказательство того, что глобальные массивы быстрее, чем хеш - верно, то может возникнуть ситуация, когда быстродействие такой конструкции будет важнее красивого способа реализации.

Отредактировано JaBeN_Симфер, 17.03.2010 в 17:22.
Старый 17.03.2010, 16:53
sonicscream

offline
Опыт: 773
Активность:
Статья не только отличная, но ещё и уникальная. На этом сайте аналогов не видел...
Старый 17.03.2010, 17:44
DioD

offline
Опыт: 45,134
Активность:
к буржуям на сайт сходи, и статья на системы не пишут, выкладывают систему, а в системе комменты и этого достаточно.
а то что вы страдаете говном всяким это ваши проблемы.
вот вам карта, ей три года (если не четыре) смотрите и учитесь как надо делать.
хотя сейчас технологии шагнули куда дальше, очень многое теперь делается проще и красивее благодаря хеш таблицам.
Прикрепленные файлы
Тип файла: w3x (12) Indigo's Way v0.10680.w3x (496.8 Кбайт, 120 просмотров )
Старый 17.03.2010, 21:05
JaBeN_Симфер

offline
Опыт: 20,050
Активность:
DioD, ты видимо у буржуев часто бываешь, а сам что-нибудь можешь?
Вот возьми свой прикрепленный шлак и сделай из него систему, запости ее в барахолке, и пусть пылится.
Старый 17.03.2010, 21:10
agentex

offline
Опыт: 34,834
Активность:
Данная статья подробно опишет способ избавления от утечек при использовании событий Юнит
атакован и Юнит получает урон в связке.
в этой связке никогда не было утечек, или я чегото пропустил О_О
или вы юзаете динамик тригеры и потом удивляетесь что откудато появляются утечки? ололо одним словом.
Старый 17.03.2010, 21:22
DioD

offline
Опыт: 45,134
Активность:
ты почитай кредиты у буржуев, внезапно обнаружишь мой ник в половине систем, то что ты запостил говно, а нубы хвалят не значит что ты сделал чтото хорошее.
да и еще, в барахолке пылятся гораздо более весёлые наработки, ты видимо просто не искал (руки кривоваты)
DioD добавил:
эту тупейшую связку всегда юзают на гуи, а так как известно утечек нет и никогда не было.
Старый 17.03.2010, 21:24
JaBeN_Симфер

offline
Опыт: 20,050
Активность:
внезапно обнаружишь мой ник в половине систем
DioD, в отличии от тебя, я не ставил перед собой задачу удивить всех, какие я умею делать наработки, а просто захотел помочь нескольким людям, и если мой труд кому-то помог, то значит он проделан не зря, а он помог.
ты видимо просто не искал
я не ищу, наработки для себя я делаю сам.
agentex, тут речь и идет о динамик триггерах, т.к. не все знают, как бороться с возникшими утечками.
Старый 17.03.2010, 21:30
DioD

offline
Опыт: 45,134
Активность:
ты даже не знаешь что триггеркондишины не создают утечек, как ты может помочь людям если толкаешь им ложные сведения, ты ведь даже не тестировал свой код.

Отредактировано Кет, 19.03.2010 в 00:58.
Старый 17.03.2010, 21:37
sonicscream

offline
Опыт: 773
Активность:
я видел то, што ты выложил, это никак не поможет тем, кто кто плохо шарит в jass, а JaBeN_Симфер разжевал (если можно так сказать) решение актуальной задачи, возникающей перед мапмэйкером.
два: легче простого написать кг/ам, что ты и сделал + рекламнул свои системы
три: нубов всегда больше, продвинутым джазерам вот [плохое слово] твои системы
[+] 1 пункт от Кет: 1.1 (ненормативная лексика)

Отредактировано Кет, 19.03.2010 в 00:58.
Старый 17.03.2010, 23:08
Sebra

offline
Опыт: 5,603
Активность:
DioD, хамить изволите, аяяй.
Если при событии получения повреждений невозможно определить каким путём получено повреждение (атака, способность, предмет, аура ...) то всё построение держится на предположении, что снаряд долетит до цели раньше, чем атакующий сделает что либо ещё.
Использованием орб-эффекта можно обойти эту проблему. Без орба вроде никак.
И ещё: наугад метить в ограниченный массив излишне самонадеянно.
Старый 17.03.2010, 23:29

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы можете скачивать файлы

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 15:41.