Добрый день, пытаюсь написать код для отлова юнитов получивших урон. Для этого создал два массива:
tUnits, чтобы записывать туда всех юнитов, чтобы потом по юниту искать триггер, привязанный к этому юниту и удалять его.
damagedTriggers, чтобы записывать туда триггеры для всех существующих и появляющихся на игровой карте юнитов.
По плану система должна работать так:
-Вначале в массив добавляются все юниты на карте и для каждого создается триггер;
-Когда, в процессе игры, на карте появляется новый юнит, он тоже добавляется в массив (в коде это не реализовано);
-Когда юнит погибает, он удаляется из массива юнитов, удаляя и привязанный к нему триггер, тем самым освобождается место для новых юнитов;
Однако столкнулся с проблемой что при смерти юнита триггер и сам юнит из массивов не удаляются, тем самым забивая и без того не сильно большое пространство. Пример ниже на скриншотах.
Старт игры:
После убийства юнита:
У меня есть два варианта почему так происходит:
  1. У меня ошибки в коде или я использовал не те функции:
  2. Редактор по каким то причинам не удаляет юнитов и триггеры, а из этого следует что память рано или поздно забьется.
Подскажите пожалуйста, почему так происходит и есть ли возможность это исправить.
Код (только эта библиотека, других функций или триггеров нет):
library UnitDamageTaker initializer Init

    globals
        private constant integer ALL_UNITS_COUNT = 2048 //Максимальное количество затригеренных юнитов
        private region mapArea
        private trigger deadTrigger
        private trigger array damagedTriggers[ALL_UNITS_COUNT] //На каждого юнита свой триггер
        private unit array tUnits[ALL_UNITS_COUNT] //чтобы знать какие юниты затригеренны и какйо конкретный триггер удалять
    endglobals
    
    
    //найти незанятую ячейку массива для записи туда юнита с триггерами
    function FindFreeIndex takes nothing returns integer index 
        local integer i = 0
        
        loop
            exitwhen (i==tUnits.size)
            if (tUnits[i] == null) then
                return i
            else
                set i = i + 1
            endif
        endloop
        
        return -1
    endfunction
    
    
    //Найти индекс переданного в функцию юинта в массиве юнитов
    function GetUnitIndex takes unit tUnit returns integer index 
        local integer i = 0
        
        loop
            exitwhen (i==tUnits.size)
            if (tUnit == tUnits[i]) then
                return i
            endif
            set i = i + 1
        endloop
        
        return -1
    endfunction
    
    
    //дебаг функция показывает количество триггеров в массиве, юнитов в массиве, юнитов на карте
    function DebugFunction takes nothing returns nothing
        local integer i = 0
        local integer trgCount = 0
        local integer unitCount = 0
        
        local string tMsg = "Триггеров: "
        local string uMsg = "Юнитов: "
        local string umMsg = "Юнитов на карте: "
        
        loop
            exitwhen (i==damagedTriggers.size)
            
            if (damagedTriggers[i]!=null) then
                set trgCount = trgCount + 1
            endif
            if (tUnits[i]!=null) then
                set unitCount = unitCount + 1
            endif
            
            set i = i + 1
        endloop
        
        set tMsg = tMsg + I2S(trgCount)
        set uMsg = uMsg + I2S(unitCount)
        set umMsg = umMsg + I2S(CountUnitsInGroup(GetUnitsInRectAll(GetPlayableMapRect())))
        
        call BJDebugMsg(tMsg)
        call BJDebugMsg(uMsg)
        call BJDebugMsg(umMsg)
        
    endfunction
    //
    
    
    //Действия при получении юнитом урона
    function DamagedTriggerActions takes nothing returns nothing
        local string msg = "юнит " + GetUnitName(GetEventDamageSource()) + " нанес " + R2S(GetEventDamage()) + " урона юниту " + GetUnitName(GetTriggerUnit())
        call BJDebugMsg(msg)
    endfunction
    
    
    //Действия при смерти юнита
    function DeadTriggerActions takes nothing returns nothing
        local integer deadUnitIndex = GetUnitIndex(GetTriggerUnit())
        
        if (deadUnitIndex == -1) then
            return
        endif
        
        call DestroyTrigger(damagedTriggers[deadUnitIndex])
        call RemoveUnit(tUnits[deadUnitIndex])
        
        //дебаг
        call DebugFunction()
        //
    endfunction
    
    
    //Создание триггера на смерть юнита
    function SetDeadUnitTrigger takes nothing returns nothing
        set deadTrigger = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( deadTrigger, EVENT_PLAYER_UNIT_DEATH )
        call TriggerAddAction(deadTrigger,function DeadTriggerActions)
    endfunction
    
    
    function SetTriggerForUnit takes nothing returns nothing
        local unit tUnit = GetEnumUnit()
        local integer freeIndex = FindFreeIndex()
        
        //Если свободного места нет, выход из функции
        if (freeIndex==-1) then
            return
        endif
        
        set tUnits[freeIndex] = tUnit
        
        //Создать риггер на получение урона юнитом
        set damagedTriggers[freeIndex] = CreateTrigger()
        call TriggerRegisterUnitEvent(damagedTriggers[freeIndex], tUnit, EVENT_UNIT_DAMAGED )
        call TriggerAddAction(damagedTriggers[freeIndex], function DamagedTriggerActions)
        //
        
    endfunction
    
    //Инициализатор библиотеки
    function Init takes nothing returns nothing
        
        //На каждого юнита, находящегося на игровой карте, создается триггер получения урона
        set mapArea = CreateRegion()
        call RegionAddRect(mapArea, GetWorldBounds())
        call ForGroupBJ( GetUnitsInRectAll(GetPlayableMapRect()), function SetTriggerForUnit)
        //
        
        call SetDeadUnitTrigger()
        
        //дебаг
        call DebugFunction()
        //
        
    endfunction
endlibrary

Принятый ответ

но опять же что делать со старым триггером.
Попробуй написать set damagedTriggers[deadUnitIndex] = null после DestroyTrigger(damagedTriggers[deadUnitIndex]) ._.

Я уже проверял тему с периодическим уничтожением триггера дабы избавиться от событий несуществующих юнитов, всё работает, события уничтожаются триггер тоже

раскрыть
globals
	private region rectRegion
	private timer Timer = CreateTimer()
	group TempG = CreateGroup()
	// trigger gg_trg_ElectricCharge
endglobals

private function ElectricCharge_Actions takes nothing returns boolean
	...
	return false
endfunction
//========================================================
private function RegistEventA takes nothing returns boolean
    call TriggerRegisterUnitEvent(gg_trg_ElectricCharge,GetFilterUnit(),EVENT_UNIT_DAMAGED)
    return false
endfunction
private function RegistEvent takes nothing returns nothing
    if gg_trg_ElectricCharge != null then
        call DestroyTrigger(gg_trg_ElectricCharge)
    endif
    set gg_trg_ElectricCharge = CreateTrigger(  )
    call GroupEnumUnitsInRect(TempG,bj_mapInitialPlayableArea,Condition(function RegistEventA))
    call TriggerAddCondition(gg_trg_ElectricCharge,Condition(function ElectricCharge_Actions))
    call TimerStart(Timer,600.,false,function RegistEvent)
endfunction
private function RegistEventB takes nothing returns boolean
    if GetUnitAbilityLevel(GetTriggerUnit(),'Aloc') == 0 then
        call TriggerRegisterUnitEvent(gg_trg_ElectricCharge,GetTriggerUnit(),EVENT_UNIT_DAMAGED)
    endif
    return false
endfunction
function InitTrig_ElectricCharge takes nothing returns nothing
    local trigger t = CreateTrigger()
    call RegistEvent()
    set rectRegion = CreateRegion()
    call RegionAddRect(rectRegion,bj_mapInitialPlayableArea)
    call TriggerRegisterEnterRegion(t,rectRegion,null)
    call TriggerAddCondition(t,Condition(function RegistEventB))
	set t = null
endfunction
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
1
27
3 года назад
1
PT153, ему нужно уничтожать события, висящие на юнитах, когда они умирают

мне достаточно обычной регистрации получения урона на один триггер при инициализации и входе на карту, и периодического пересоздавания триггера раз в 10 минут

Кстати, а тут у тебя поток обрывается если что))
        loop
            exitwhen (i==damagedTriggers.size)
            
            if (damagedTriggers[i]!=null) then
                set trgCount = trgCount + 1
            endif
            if (tUnits[i]!=null) then
                set unitCount = unitCount + 1
            endif
            
            set i = i + 1
        endloop

лимит операций колеблется 800~ на поток, а у тебя это
private constant integer ALL_UNITS_COUNT = 2048
private trigger array damagedTriggers[ALL_UNITS_COUNT]
1
28
3 года назад
1
ему нужно уничтожать события, висящие на юнитах, когда они умирают
Это настолько незначительная утечка, что ею можно пренебречь.
0
2
3 года назад
0
rsfghd, Спасибо, не знал про поток. Идея с пересозданием хороша, но опять же что делать со старым триггером.

PT153, Классная идея кстати, так и триггеров меньше гораздо и свободные искать не надо, возьму на заметку. Но мне интересно почему удаленные дестройнутые триггеры и ремувнутые юниты не уходят из памяти?
1
28
3 года назад
1
XoxloHunter, скорее всего где-то ошибка в коде или, как сказал rsfghd, лимит потока, из-за которого некоторые ячейки не очищаются.
1
27
3 года назад
Отредактирован rsfghd
1
но опять же что делать со старым триггером.
Попробуй написать set damagedTriggers[deadUnitIndex] = null после DestroyTrigger(damagedTriggers[deadUnitIndex]) ._.

Я уже проверял тему с периодическим уничтожением триггера дабы избавиться от событий несуществующих юнитов, всё работает, события уничтожаются триггер тоже

раскрыть
globals
	private region rectRegion
	private timer Timer = CreateTimer()
	group TempG = CreateGroup()
	// trigger gg_trg_ElectricCharge
endglobals

private function ElectricCharge_Actions takes nothing returns boolean
	...
	return false
endfunction
//========================================================
private function RegistEventA takes nothing returns boolean
    call TriggerRegisterUnitEvent(gg_trg_ElectricCharge,GetFilterUnit(),EVENT_UNIT_DAMAGED)
    return false
endfunction
private function RegistEvent takes nothing returns nothing
    if gg_trg_ElectricCharge != null then
        call DestroyTrigger(gg_trg_ElectricCharge)
    endif
    set gg_trg_ElectricCharge = CreateTrigger(  )
    call GroupEnumUnitsInRect(TempG,bj_mapInitialPlayableArea,Condition(function RegistEventA))
    call TriggerAddCondition(gg_trg_ElectricCharge,Condition(function ElectricCharge_Actions))
    call TimerStart(Timer,600.,false,function RegistEvent)
endfunction
private function RegistEventB takes nothing returns boolean
    if GetUnitAbilityLevel(GetTriggerUnit(),'Aloc') == 0 then
        call TriggerRegisterUnitEvent(gg_trg_ElectricCharge,GetTriggerUnit(),EVENT_UNIT_DAMAGED)
    endif
    return false
endfunction
function InitTrig_ElectricCharge takes nothing returns nothing
    local trigger t = CreateTrigger()
    call RegistEvent()
    set rectRegion = CreateRegion()
    call RegionAddRect(rectRegion,bj_mapInitialPlayableArea)
    call TriggerRegisterEnterRegion(t,rectRegion,null)
    call TriggerAddCondition(t,Condition(function RegistEventB))
	set t = null
endfunction
Принятый ответ
0
28
3 года назад
0
Для динамических триггеров нельзя указывать действие, ибо они никак не удаляются. По крайней мере, так говорят.
0
27
3 года назад
0
PT153, там заново действие в триггер добавляется через кондишен. Да и когда я проверял всё работало как надо. Я тебе вроде даже говорил о результатах, ведь именно ты сказал, что уничтожение триггера стирает события, хоть это и не имело особого толку из-за незначительности события
2
2
3 года назад
2
rsfghd, тоже думал о том чтобы вручную нулить уже уничтоженных юнитов и триггеров, но мне казалось что это должны делать функции. Помечу твой ответ как лучший.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.