Вопрос, какая система снарядов будет наиболее производительной?
Хочу рассмотреть вообще абсолютно все варианты, под Bullet Hell я имею ввиду стадию босса когда миллион снарядов летит от 1 или нескольких источников и игрок должен уклоняться от всего этого месива.

Какие я знаю варианты:

  1. Дефолтный скилл рексара стадо ящериц, он там проблемы с углом поворота и настройками в целом, самый примитивный но норм
  2. Система снарядов:
    • 1 таймер на движение всех снарядов
    • перебор группы в которой ищется враг вокруг снаряда в определённом радиусе
  3. Система снарядов для 1 игрока
    • тот же перебор таймером
    • но столкновение определяется через IsunitInrange (снаряд, наш герой)
  1. Система снарядов + аура жара (мой фаворит):
  • снаряды снова летят на таймере
  • в качестве столкновения используется событие получения урона 131 патча, сами же снаряды излучают жар преисподни (постоянный)
  1. Медленные волны силы/ тёмные стаи:
  • 1 дамми кастует заклинание в указанную точку
  • к сожелению период урона в таком случае странный и снаряд не будет умирать при столкновении с героем
Из требований будут скорее всего такие параметры:
  • одновременное число снарядов от 10 до 300
  • скорость снарядов от 200 до 1000
Я понимаю, что работать будет прекрасно даже если каждый из 100 снарядов посадить на отдельный таймер, но всё же... какой способ самый оптимальный для слабых пк.
Если есть другие варианты реализации - пишите в комменты

то скорость разная, то вылетают не из героя а из какой то псевдо центральной точки
в общем я полностью добился желаемого результата, никакого прерывания, ни каких лагов и странных поведений (то что снаряды врезаются в трупы так и задумано =))
вот мой код
//! beginusercode

--какие то общие функции
function MoveX (x,  Dist,  Angle)
    return x+Dist*Cos(Angle*0.0175)
end
function MoveY (x,  Dist,  Angle)
    return x+Dist*Sin(Angle*0.0175)
end
function AbilityId(id)
    return id:byte(1) * 0x1000000 + id:byte(2) * 0x10000 + id:byte(3) * 0x100 + id:byte(4)
end

function Out(x,y)
    return ( ( GetRectMinX(bj_mapInitialPlayableArea) <= x ) and ( x <= GetRectMaxX(bj_mapInitialPlayableArea) ) and ( GetRectMinY(bj_mapInitialPlayableArea) <= y ) and ( y <= GetRectMaxY(bj_mapInitialPlayableArea) ) ) or IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) == false
end

GetTerrainZ_location = Location(0, 0)
function GetTerrainZ(x,y)
    MoveLocation(GetTerrainZ_location, x, y);
    return GetLocationZ(GetTerrainZ_location);
end

function ehandler( err )
    print( "ERROR:", err )
end
--/////// глобалки (хотя какая разница где объявить то)
perebor=CreateGroup()

--/////// триггер

    local trigger = CreateTrigger()
    for i = 0, bj_MAX_PLAYER_SLOTS - 1, 1 do
        TriggerRegisterPlayerUnitEvent(trigger, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT)
    end
    TriggerAddCondition(trigger, Condition(function() return
        GetOwningPlayer(GetTriggerUnit()) == Player(0)
    end))
local d=0
TriggerAddAction(trigger, function()
    local u=GetTriggerUnit()
    local z=GetTerrainZ(GetUnitX(u),GetUnitY(u))
        print("perodstart")
        TimerStart(CreateTimer(), 0.1, true, function()
        d=d+1

 --print("abiclick "..d)
 -- будущая фукция запуска снаряда
 local x=GetUnitX(u)
 local y=GetUnitY(u)
 local eff=AddSpecialEffect("Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl", x, y)
 local d2=1000
 local a=GetUnitFacing(u)

 TimerStart(CreateTimer(), 0.032, true, function()
 d2=d2-10
 x=MoveX(x,25,a)
 y=MoveY(y,25,a)
 BlzSetSpecialEffectPosition(eff, x, y, GetTerrainZ(x,y)+30)
-- урон
local e=nil
GroupEnumUnitsInRange(perebor,x,y,80,null)
while true do
	e = FirstOfGroup(perebor)
	if e == nil then break end
if IsUnitEnemy(e, GetOwningPlayer(u)) then
    UnitDamageTarget( u, e, BlzGetUnitBaseDamage(u, 1), false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS )
   -- print("наносим урон")
    DestroyEffect(eff)
    eff=nill 
end

	GroupRemoveUnit(perebor,e)
end

--print(d2)

 if d2<=0 or  Out(x,y)==false or eff==nil then
  --  print("УМРИ!")
    DestroyEffect(eff)
    DestroyTimer(GetExpiredTimer())
 end

 end)

    end)
end)





//! endusercode
а вот и карта
Выражаю огромную благодарность NazarPunk, и Prog за оказанную помощь и наставления
Выводы:
Более навороченные (в техническом плане) способы не всегда самые оптимальные
Точно также можно двигать эффекты и на мемхаке, так что 126 пат так же может удостоится высокой производительностью для огромного количества снарядов
Мой комп держит на 1 экране около 700 объектов в режиме 60+ FPS (с отключенной вертикальной синхронизацией, это когда макс фпс за 200)
Загруженные файлы
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
22
а вы пробуй через цикл делать движение без таймера =)
33
pro100master, по подробней, а на луа есть какие нить аналоги таймеров? или в цикл можно wait засунуть?
30
а на луа есть какие нить аналоги таймеров?
Есть Sleep Function, но врятли они работать будут.
33
Ох, ну вот и руки дошли до проверки системы, да печально всё, но буду ковырять код и попробую перенести на отдельный таймер по снаряду.
А может кто напомнить, какой лимит поток, и какой лимит операций на потоке для 126? и на сколько всё улучшили в 131?
30
перенести на отдельный таймер по снаряду
Я тоже так думаю.
Bergi_Bear:
какой лимит операций на потоке
Если мне не изменяет память то:
  • 1.26: ~4000
  • 1.30: ~32000
33
NazarPunk, а если учесть, что время почти всегда случайное, когда умирает не может ли сборщик мусора косячить и сгребать таймер на помойку?
30
не может ли сборщик мусора косячить и сгребать таймер на помойку?
Скорее всего, глобальный таймер нагребает на себя кучу накладных расходов и прибивается по оплимиту. Посему лучше наплодить таймеров и пусть сами умирают.
33
NazarPunk, Проверил, переделал на ручной каст (но движение на 1 таймере), и получается что блок создания эффекта даже перестаёт работать, полный игнор события и вот этого блока кода
    local trigger = CreateTrigger()
    for i = 0, bj_MAX_PLAYER_SLOTS - 1, 1 do
        TriggerRegisterPlayerUnitEvent(trigger, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT)
    end
    TriggerAddCondition(trigger, Condition(function() return
        GetOwningPlayer(GetTriggerUnit()) == Player(0)
    end))
    TriggerAddAction(trigger, function()
        local caster = GetTriggerUnit()
        local x
        local y
        local angle
        --UnitRemoveAbility(caster, GetSpellAbilityId())

        x = GetUnitX(caster)
        y = GetUnitY(caster)
        angle = Deg2Rad(GetUnitFacing(caster))

        table.insert(BULLETS, {
            effect = AddSpecialEffect('units\\nightelf\\Wisp\\Wisp.mdl', x, y),
            caster = caster,
            x = x,
            y = y,
            angle = angle,
            cos = Cos(angle),
            sin = Sin(angle)
        })

    end)
когда падает таймер, то эффекты продолжают создаваться под ногами героя
100% не только таймер, вот вручную кастовал до 400 накликал, и всё исчезло, перестало работать событие (проверял принтом)... всё событие уничтожается и более не существует =(
30
когда падает таймер, то эффекты продолжают создаваться под ногами героя
Потому что таймеры эффектов не падают
33
Вот, потыкай раз 300 на Q и увидишь, что падает не таймер, а событие EVENT_PLAYER_UNIT_SPELL_EFFECT
Загруженные файлы
28
Bergi_Bear, попробуй сделать по старинке, объяви функции действия не в замыкании, а создание триггера в специальной для этой функции, что предоставляет WE.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.