rsfghd, благодаря этому пованивающему мусору я многому научился))) В твоём коде очень много примеров того как делать надо, и как делать категорически НЕНАДО XD
rsfghd, действительно пованивает, утекает и лагает, использует глобалки близард чтобы обойти отсутствие возможности передать аргументы через предикаты, из-за чего половина стрел при малом интервале не обнулится, утекает и юнит и локация ну и т.д. Да и урон через волны такое себе. Фильтр и дамаг в луп засунуть осталось и через firstofgroup обрабатывать чтобы избавиться от приватной глобалки и не было конкурентности. Оно сейчас с 1000 стрел х 12 кастеров плавно едет. В целом спасибо, пока думал как заставить это плавно без тормозов работать написал полезную вспомогательную библиотеку. Ну а это почищеный код с переработаной логикой до моих шаманств с оптимизацией, моя версия сейчас без лагов поддерживает 2000 снарядов с интервалом 0.010, и каждый снаряд гарантированно обрабатывается, но оно уже требует самописную библиотеку.
Открыть
scope ArrowsSc
globals
//=============================== КОНФИГУРАЦИЯ СПОСОБНОСТИ =====================================
private constant integer aID = 'A000' // ID способности
private constant integer dID = 'u000' // ID дамми-юнита для снаряда и источника урона
private constant real Damage = 50 // Базовое значение урона (на стрелу)
private constant real MaxR = 230 // Радиус действия заклинания (спавна стрел)
private constant real MinR = 65 // Минимальный радиус спавна стрел (от центра)
private constant real DmgR = 70 // Радиус урона стрелы
private constant integer ArrowsCount = 1000 // Количество стрел (снарядов)
private constant real SpwnInt = 0.020 // Минимальный интервал между появлением стрел (к нему прибавится рандомный разброс)
private constant real SpwnIntR = 0.020 // Максимальный разброс интервала
private constant real ArrowHeight = 700 // Начальная высота спавна стрел
private constant real ArrowSpeed = 1500 // Скорость падения (SetUnitFlyHeight)
private constant real ArrowGround = 0 // Конечная высота падения (земля)
private constant attacktype AttackType = ATTACK_TYPE_PIERCE
private constant damagetype DamageType = DAMAGE_TYPE_NORMAL
private constant weapontype WeaponType = WEAPON_TYPE_WHOKNOWS
private constant string ArrowEffect = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl"
private constant string ArrowAttachPoint = "chest"
private constant real ArrowEffectRemoveTime = 1.00
// Глобальные переменные
private unit TempArrow = null
endglobals
//==========================================================================
private function IsUnitDead takes unit u returns boolean
return IsUnitType(u,UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0
endfunction
private function filt takes nothing returns boolean
return not IsUnitDead(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempArrow)) and not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)
endfunction
private function remeff takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer i = GetHandleId(t)
call DestroyEffect(LoadEffectHandle(udg_Hash,i,0))
call FlushChildHashtable(udg_Hash,i)
call DestroyTimer(t)
set t = null
endfunction
private function dmgf takes nothing returns nothing
local timer t = null
local effect e = null
call UnitDamageTarget(TempArrow,GetEnumUnit(),Damage,false,true,AttackType,DamageType,WeaponType)
set t = CreateTimer()
set e = AddSpecialEffectTarget(ArrowEffect,GetEnumUnit(),ArrowAttachPoint)
call SaveEffectHandle(udg_Hash,GetHandleId(t),0,e)
call TimerStart(t,ArrowEffectRemoveTime,false,function remeff)
set t = null
set e = null
endfunction
private function ArrowDamage takes nothing returns nothing
local timer t = GetExpiredTimer()//Получаем таймер снаряда
local integer apk = GetHandleId(t)//Получаем arrow parent key
local unit u = LoadUnitHandle(udg_Hash, apk, 0) //Получаем снаряд (источник урона)
local player Owner = LoadPlayerHandle(udg_Hash, apk, 1) // Владелец
local group g = null
local real ux
local real uy
// 1. Получаем точные координаты снаряда в момент срабатывания
set ux = GetUnitX(u)
set uy = GetUnitY(u)
// 3. Создаем группу целей
set g = CreateGroup()
set TempArrow = u
// 4. Группируем цели и наносим урон
call GroupEnumUnitsInRange(g, ux, uy, DmgR, Condition(function filt))
call ForGroup(g, function dmgf)
// 5. КРИТИЧЕСКИ ВАЖНО: Очистка
call DestroyGroup(g)
set TempArrow = null
set g = null
set u = null
// 6. Финальная очистка хэш-таблицы и таймера
call FlushChildHashtable(udg_Hash, apk)
call DestroyTimer(t)
set t = null
set Owner = null
endfunction
private function SpawnArrows takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer pk = GetHandleId(t)
local integer apk
local unit Caster
local player Owner
local real x = LoadReal(udg_Hash, pk, 2) //X
local real y = LoadReal(udg_Hash, pk, 3)//Y
local integer AS = LoadInteger(udg_Hash, pk, 4)//Arrows Shooted
local location Tloc = null
local real RandR
local real Interval
local timer FlyTime
local unit u
local real lifetime = (ArrowHeight-ArrowGround)/ArrowSpeed
if AS == ArrowsCount then//Условие выхода из цикла, остановка и очистка.
local location l = LoadLocationHandle(udg_Hash, pk, 5) //Загружаем хендл локации
call PauseTimer(t)
call RemoveLocation(l) //Удаление объекта Location из памяти игры
call FlushChildHashtable(udg_Hash, pk) // Очистка ссылок из хэша
call DestroyTimer(t)
set t = null
set l = null
return
endif
//Инициализируем переменные
set Caster = LoadUnitHandle(udg_Hash,pk,0) //Кастер
set Owner = LoadPlayerHandle(udg_Hash,pk,1) //Овнер
set Tloc = LoadLocationHandle(udg_Hash,pk,5) //TempLocation
set RandR = GetRandomReal(MinR,MaxR) //Рандомный радиус между минимальным и максимальным радиусом
//Получаем рандомную точку в радиусе
call MoveLocation(Tloc, x+RandR*Cos(GetRandomReal(0,360)*bj_DEGTORAD), y+RandR*Sin(GetRandomReal(0,360)*bj_DEGTORAD))
//создание юнита-стрелы
set u = CreateUnitAtLoc(Owner,dID,Tloc,GetRandomReal(0,360))
// Установка TimedLife. Это удалит юнит после lifetime, проиграв звук.
call UnitApplyTimedLife(u,'BTLF',lifetime)
call SetUnitFlyHeight(u,ArrowHeight,0)
call SetUnitFlyHeight(u,ArrowGround,ArrowSpeed)
set FlyTime = CreateTimer() //Таймер для стрелы
set apk = GetHandleId(FlyTime) //arrow parent key
//Что передаём функции урона:
call SaveUnitHandle(udg_Hash,apk,0,u)//apk 0 - стрела
call SavePlayerHandle(udg_Hash,apk,1,Owner)//apk 1 ее владелец
call TimerStart(FlyTime, lifetime - 0.036, false, function ArrowDamage) //нанесение урона и спецэфектов перед "смертью" стрелы.
// снаряд запущен
call SaveInteger(udg_Hash,pk,4,AS+1)//Запущено снарядов +1
set Interval = SpwnInt + GetRandomReal(0.001, SpwnIntR)//Интервал с разбросом
call TimerStart(t, Interval, true, function SpawnArrows)//Таймер запуска снарядов
//утилизация
set Tloc = null
set u = null
set Caster = null
set FlyTime = null
set Owner = null
endfunction
// --- ИНИЦИАЛИЗАЦИЯ ТАБЛИЦЫ ---
private function Actions takes nothing returns nothing
local unit u = GetSpellAbilityUnit()
local timer t = CreateTimer()
local integer pk = GetHandleId(t) //ключ таблицы
local player o = GetOwningPlayer(u)
local location l = Location(0,0)
call SaveUnitHandle(udg_Hash,pk,0,u) //Caster (слот 0)
call SavePlayerHandle(udg_Hash,pk,1,o) //Owner (слот 1)
call SaveReal(udg_Hash,pk,2,GetSpellTargetX()) // X (слот 2)
call SaveReal(udg_Hash,pk,3,GetSpellTargetY()) // Y (слот 3)
call SaveInteger(udg_Hash,pk,4,0) //Выпущено стрел (слот 4)
call SaveLocationHandle(udg_Hash,pk,5,l) //Точка локации используемая для этого каста
call TimerStart(t,0.01,false,function SpawnArrows)
set l = null
set o = null
set u = null
set t = null
endfunction
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == aID
endfunction
function InitTrig_arrows takes nothing returns nothing
local trigger t = CreateTrigger()
local integer index = 0
loop
call TriggerRegisterPlayerUnitEvent(t,Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(t,Condition(function Conditions))
call TriggerAddAction(t,function Actions)
set t = null
endfunction
endscope
DiZzicH, увы ни один счетчик хэндлов не подскажет при заведомо правильно составленном коде что именно протечёт в самом движке во время игры. И без мистических хк опытным путём воочию видно при стресс-тесте какой-нибудь системы с дамиками как за 5 минут проседает и начинает тормозить игра. Сам юнит то может и правильно убит/обнулён, но за его недолгую, но счастливую жизнь какую-то память о себе (не то чтобы светлую) он все равно оставит. Да, своего рода костыль для решения проблемы которой по идее не должно существовать, если бы джасовский Garbage Collector честно отрабатывал свой хлеб, но увы.
Этот метод позволил добиться стабильности и производительности нескольких систем в которых дамми-юнитов требовалось много, без всяких пересчетов-перепроверок и добиваний того, что и так должно было быть добито.
» Блог им. rsfghd / Новый мусор, ура!
Ред. AnimoMori
» Блог им. rsfghd / Новый мусор, ура!
» WarCraft 3 / DPM SYSTEM (Dummy Pool Manager)
Этот метод позволил добиться стабильности и производительности нескольких систем в которых дамми-юнитов требовалось много, без всяких пересчетов-перепроверок и добиваний того, что и так должно было быть добито.