• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] Noticeable slowdown after numerous spellcasts

Status
Not open for further replies.
Level 4
Joined
Sep 25, 2005
Messages
71
Trying to refine a sort of projectile system I've been working on, I've come across something horrible.

Bear in mind it only seems to happening with large numbers of "Missiles," but a spell which creates a number of projectiles starts to "lag" the game after a certain amount of casts. In particular, this only seems to be happening in an area which has had multiple of the casts. Aiming the spell anywhere else seems to not cause the problem, but ideally, this doesn't happen, y'know, at all.

JASS:
scope Test initializer init

globals

    private constant integer        MISSILE_NUMBER          = 3
    private constant integer        ABIL_ID                 = 'A008'
    private constant integer        DUMMY_ID                = 'o003'
    private constant integer        CASTER_DUMMY_ID         = 0
    private constant integer        CASTER_DUMMY_EX_BUFF    = 0
    private constant integer        CASTER_DUMMY_ABIL_ID    = 0    
    private constant integer        UNIT_HIDE_INTERVAL      = 1   
    
    private constant string         CAST_FX                 = null
    private constant string         DUMMY_CAST_FX           = null
    private constant string         HIT_FX                  = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"
    private constant string         DEATH_MISSILE_FX        = null
    private constant string         DEATH_ENEMY_FX          = null
    private constant string         TRAIL_FX                = null
    private constant string         LIGHTNING_FX            = "AFOD"
    private constant string         FX_ATTACH_POINT         = "origin"
    private constant string         DEATH_FX_ATTACH_POINT   = null   
    private constant string         CASTER_DUMMY_ORDER      = null
    
    private constant real           COLLISION_SIZE          = 150.
    private constant real           DUMMY_EX_TIME           = 1.
    private constant real           FX_RED                  = 1.
    private constant real           FX_GREEN                = 0.65
    private constant real           FX_BLUE                 = 0.
    private constant real           CAST_RANGE              = 150.
    private constant real           RANGE_PER_LEVEL         = 0.
    private constant real           TRANSPARENCY_THRESH     = 1.
    private constant real           TRANSPARENCY_INCREMENT  = 0.
    private constant real           MISSILE_ANGLE           = 20.
    private constant real           MISSILE_ANGLE_LVL_REDUC = 0.
    private constant real           MISSILE_MOVESPEED       = 20.
    private constant real           FX_Z                    = 72.
    private constant real           DAMAGE_AMOUNT           = 85.
    private constant real           DAMAGE_PER_LEVEL        = 0.
	private constant real           TIMING_INTERVAL         = 0.02
    private constant real           CAST_ANGLE_OFFSET       = ((MISSILE_NUMBER * MISSILE_ANGLE) * .5) + (.5 * MISSILE_ANGLE)
    
    private constant boolean        KILLS_TREES             = false
    private constant boolean        HAS_DUMMY_CAST_FX       = false
    private constant boolean        HAS_TRAIL_FX            = false
    private constant boolean        FX_ON_COLLISION         = true
    private constant boolean        FX_ON_DEATH_MISSILE     = false
    private constant boolean        FX_ON_DEATH_ENEMY       = false
    private constant boolean        DIES_ON_COLLISION       = false
    private constant boolean        INDIVID_DIE_ONHIT       = false
    private constant boolean        MISSILES_EXPLODE        = true
    private constant boolean        MISSILES_EXPLODE_ONHIT  = false
    private constant boolean        DEALS_DAMAGE            = true
    private constant boolean        CASTS_SPELL_ON_TARGET   = false
    private constant boolean        CASTS_SPELL_ON_POINT    = false
    private constant boolean        CASTS_SPELL_NOTARGET    = false
    private constant boolean        SPELL_HAS_LEVELS        = false
    private constant boolean        IS_TARGETED             = true
    private constant boolean        IS_UNIT_TARGETED        = false
    private constant boolean        HAS_HOMING              = false
    private constant boolean        HIDING_UNITS            = false
    private constant boolean        HIDING_UNITS_INTERVAL   = false    
    private constant boolean        IS_CIRCLE               = MISSILE_ANGLE * MISSILE_NUMBER == 360.

	private timer TestTimer = CreateTimer()
	private integer TestInstances = 0
	private group TestEnumGroup = CreateGroup()
	private TestData array TestArray
endglobals

private function TreeFilter takes nothing returns boolean
    local integer d = GetDestructableTypeId(GetFilterDestructable())
    return d == 'ATtr' or d == 'BTtw' or d == 'KTtw' or d == 'YTft' or d == 'JTct' or d == 'YTst' or d == 'YTct' or d == 'YTwt' or d == 'JTwt' or d == 'JTwt' or d == 'FTtw' or d == 'CTtr' or d == 'ITtw' or d == 'NTtw' or d == 'OTtw' or d == 'ZTtw' or d == 'WTst' or d == 'LTlt' or d == 'GTsh' or d == 'Xtlt' or d == 'WTtw' or d == 'Attc' or d == 'BTtc' or d == 'CTtc' or d == 'ITtc' or d == 'NTtc' or d == 'ZTtc' /*
    */and GetDestructableLife(GetFilterDestructable()) > 0.405
endfunction

private function KillTree takes nothing returns nothing
	call KillDestructable(GetEnumDestructable())
endfunction

private function MoveUnit takes unit movingUnit, real speed, real angle returns nothing
        local real x
        local real y
        set x = GetUnitX(movingUnit) + speed * Cos(angle * bj_DEGTORAD)
        set y = GetUnitY(movingUnit) + speed * Sin(angle * bj_DEGTORAD)
        call SetUnitBoundedX(movingUnit, x)
        call SetUnitBoundedY(movingUnit, y)
endfunction

struct TestData

    boolean Missilesexplode
    unit caster
    unit targetedunit
    unit array Missiles[MISSILE_NUMBER]
    player casterOwner
    lightning array beams[MISSILE_NUMBER]
    integer casterLevel
    integer dummySpellLevel
    real distance
    real maxDist
    real transparency
    group TestTargets
   
    private static method TestExecution takes nothing returns nothing
        local boolexpr TreeCheck
        local integer i = 0
        local integer d
        local integer Missilecount = 0
        local location zLoc
        local unit enemy
        local unit casterDummy
        local effect collisionFX
        local effect trailFX
        local effect dummycastFX
        local real x
        local real y
        local real array z
    	local rect area  
        local TestData localTest
        
        loop
            exitwhen i >= TestInstances
            set localTest = TestArray[i]
    
            if localTest.distance < localTest.maxDist then
            if localTest.transparency > 0. and (localTest.distance / localTest.maxDist) > TRANSPARENCY_THRESH then
                set localTest.transparency = localTest.transparency - TRANSPARENCY_INCREMENT
            endif
            loop
                exitwhen Missilecount == MISSILE_NUMBER    
                    call MoveUnit(localTest.Missiles[Missilecount], MISSILE_MOVESPEED, GetUnitFacing(localTest.Missiles[Missilecount]))
                    if HAS_HOMING == true and IS_UNIT_TARGETED == true then
                        call SetUnitFacing(localTest.Missiles[Missilecount], /*
                        */bj_RADTODEG*Atan2((GetUnitY(localTest.targetedunit)-GetUnitY(localTest.Missiles[Missilecount])), /*
                        */(GetUnitX(localTest.targetedunit)-GetUnitX(localTest.Missiles[Missilecount]))))
                    endif
                    set x = GetUnitX(localTest.Missiles[Missilecount])
                    set y = GetUnitY(localTest.Missiles[Missilecount])
                    set TreeCheck = Filter(function TreeFilter)
                    set area =  Rect(x - COLLISION_SIZE, y - COLLISION_SIZE, x + COLLISION_SIZE, y + COLLISION_SIZE)
                    set zLoc = Location(x, y)
                    set z[Missilecount] = FX_Z + GetLocationZ(zLoc)
                    call SetUnitFlyHeight(localTest.Missiles[Missilecount], FX_Z, 0.)
                    if HAS_TRAIL_FX == true then
                        set trailFX = AddSpecialEffect(TRAIL_FX, GetUnitX(localTest.Missiles[Missilecount]), GetUnitY(localTest.Missiles[Missilecount]))
                        call DestroyEffect(trailFX)
                    endif
                    if ModuloInteger(Missilecount, UNIT_HIDE_INTERVAL) == 0 and HIDING_UNITS == true then
                        call SetUnitVertexColor(localTest.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), 0)
                    else 
                        if ModuloInteger(Missilecount, UNIT_HIDE_INTERVAL) != 0 and HIDING_UNITS_INTERVAL == true then
                            call SetUnitVertexColor(localTest.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), 0)
                        else
                            call SetUnitVertexColor(localTest.Missiles[Missilecount], R2I(255. * FX_RED), R2I(255. * FX_GREEN), R2I(255. * FX_BLUE), R2I(localTest.transparency*255))   
                        endif
                    endif
                    if Missilecount != MISSILE_NUMBER then
                        call MoveLightningEx(localTest.beams[Missilecount], true, GetUnitX(localTest.Missiles[Missilecount-1]), GetUnitY(localTest.Missiles[Missilecount-1]), z[Missilecount-1], GetUnitX(localTest.Missiles[Missilecount]), GetUnitY(localTest.Missiles[Missilecount]), z[Missilecount])
                        if localTest.beams[0] != null then
                            call MoveLightningEx(localTest.beams[0], true, GetUnitX(localTest.Missiles[0]), GetUnitY(localTest.Missiles[0]), z[0], GetUnitX(localTest.Missiles[Missilecount]), GetUnitY(localTest.Missiles[Missilecount]), z[Missilecount])
                        endif
                    endif
                    call SetLightningColor(localTest.beams[Missilecount],FX_RED,FX_GREEN,FX_BLUE,localTest.transparency)
                    if KILLS_TREES == true then
                        call EnumDestructablesInRect(area, TreeCheck, function KillTree)
                    endif
                    call GroupEnumUnitsInRange(TestEnumGroup, GetUnitX(localTest.Missiles[Missilecount]), GetUnitY(localTest.Missiles[Missilecount]), COLLISION_SIZE, null)
                    loop
                        set enemy = FirstOfGroup(TestEnumGroup)
                        exitwhen enemy == null
                        if ValidAliveTargetGroupedGround(enemy, localTest.caster, localTest.TestTargets) == true then
                            call GroupAddUnit(localTest.TestTargets, enemy)
                            if DEALS_DAMAGE == true then
                                call UnitDamageTarget(localTest.caster, enemy, DAMAGE_AMOUNT + (DAMAGE_PER_LEVEL * localTest.casterLevel), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, null)
                            endif
                            if CASTS_SPELL_ON_TARGET == true then
                                set casterDummy = CreateUnit(localTest.casterOwner,CASTER_DUMMY_ID,/*
                                */GetUnitX(enemy),/*
                                */GetUnitY(enemy),/*
                                */bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localTest.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localTest.Missiles[Missilecount])))
                                call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
                                if SPELL_HAS_LEVELS == true then
                                    call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localTest.dummySpellLevel)
                                endif
                                call IssueTargetOrder(casterDummy, CASTER_DUMMY_ORDER, enemy)
                                if HAS_DUMMY_CAST_FX == true then
                                    set dummycastFX = AddSpecialEffect(DUMMY_CAST_FX, GetUnitX(casterDummy),GetUnitY(casterDummy) )
                                    call DestroyEffect(dummycastFX)
                                endif
                                call UnitApplyTimedLife(casterDummy, CASTER_DUMMY_EX_BUFF, DUMMY_EX_TIME)
                            endif
                            if CASTS_SPELL_ON_POINT == true then
                                set casterDummy = CreateUnit(localTest.casterOwner,CASTER_DUMMY_ID,/*
                                */GetUnitX(enemy),/*
                                */GetUnitY(enemy),/*
                                */bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localTest.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localTest.Missiles[Missilecount])))
                                call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
                                if SPELL_HAS_LEVELS == true then
                                    call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localTest.dummySpellLevel)
                                endif
                                call IssuePointOrder(casterDummy, CASTER_DUMMY_ORDER, GetUnitX(enemy), GetUnitY(enemy))
                                if HAS_DUMMY_CAST_FX == true then
                                    set dummycastFX = AddSpecialEffect(DUMMY_CAST_FX, GetUnitX(casterDummy),GetUnitY(casterDummy) )
                                    call DestroyEffect(dummycastFX)
                                endif
                                call UnitApplyTimedLife(casterDummy, CASTER_DUMMY_EX_BUFF, DUMMY_EX_TIME)
                            endif
                            if CASTS_SPELL_NOTARGET == true then
                                set casterDummy = CreateUnit(localTest.casterOwner,CASTER_DUMMY_ID,/*
                                */GetUnitX(enemy),/*
                                */GetUnitY(enemy),/*
                                */bj_RADTODEG*Atan2(GetUnitY(enemy)-GetUnitY(localTest.Missiles[Missilecount]),GetUnitX(enemy)-GetUnitX(localTest.Missiles[Missilecount])))
                                call UnitAddAbility(casterDummy, CASTER_DUMMY_ABIL_ID)
                                if SPELL_HAS_LEVELS == true then
                                    call SetUnitAbilityLevel(casterDummy, CASTER_DUMMY_ABIL_ID, localTest.dummySpellLevel)
                                endif
                                call IssueImmediateOrder(casterDummy, CASTER_DUMMY_ORDER)
                                if HAS_DUMMY_CAST_FX == true then
                                    set dummycastFX = AddSpecialEffect(DUMMY_CAST_FX, GetUnitX(casterDummy),GetUnitY(casterDummy) )
                                    call DestroyEffect(dummycastFX)
                                endif
                                call UnitApplyTimedLife(casterDummy, CASTER_DUMMY_EX_BUFF, DUMMY_EX_TIME)
                            endif
                            if FX_ON_COLLISION == true then
                                set collisionFX = AddSpecialEffectTarget(HIT_FX, enemy, FX_ATTACH_POINT)
                                call DestroyEffect(collisionFX)
                            endif
                            if DIES_ON_COLLISION == true then
                                if FX_ON_DEATH_MISSILE == true then
                                    set collisionFX = AddSpecialEffect(DEATH_MISSILE_FX, GetUnitX(localTest.Missiles[Missilecount]),GetUnitY(localTest.Missiles[Missilecount]) )
                                    call DestroyEffect(collisionFX)
                                endif
                                if FX_ON_DEATH_ENEMY == true then
                                    set collisionFX = AddSpecialEffectTarget(DEATH_ENEMY_FX, enemy, DEATH_FX_ATTACH_POINT)
                                    call DestroyEffect(collisionFX)
                                endif
                                if MISSILES_EXPLODE_ONHIT == true then
                                    set localTest.Missilesexplode = true
                                endif
                                set localTest.distance = localTest.maxDist
                            endif
                            if INDIVID_DIE_ONHIT == true then
                                if FX_ON_DEATH_MISSILE == true then
                                    set collisionFX = AddSpecialEffect(DEATH_MISSILE_FX, GetUnitX(localTest.Missiles[Missilecount]),GetUnitY(localTest.Missiles[Missilecount]) )
                                    call DestroyEffect(collisionFX)
                                endif
                                if FX_ON_DEATH_ENEMY == true then
                                    set collisionFX = AddSpecialEffectTarget(DEATH_ENEMY_FX, enemy, DEATH_FX_ATTACH_POINT)
                                    call DestroyEffect(collisionFX)
                                endif
                                if MISSILES_EXPLODE_ONHIT == true then
                                    set localTest.Missilesexplode = true
                                endif
                                
                                if localTest.Missilesexplode == false then
                                    call RemoveUnit(localTest.Missiles[Missilecount])
                                endif
                                if localTest.Missilesexplode == true then
                                    call KillUnit(localTest.Missiles[Missilecount])
                                endif
                                set localTest.Missiles[Missilecount] = null
                            endif
                        endif
                        call GroupRemoveUnit(TestEnumGroup, enemy)
                    endloop
                                  
               set Missilecount = Missilecount + 1
            endloop
                set Missilecount = 0
                set localTest.distance = localTest.distance + MISSILE_MOVESPEED
            endif
            
            if localTest.distance >= localTest.maxDist then
                set TestInstances = TestInstances - 1
                set TestArray[i] = TestArray[TestInstances]
                call DestroyGroup(localTest.TestTargets)
                call localTest.destroy()
            endif	
            call RemoveRect(area)
            call DestroyBoolExpr(TreeCheck)
            set area = null
            set casterDummy = null
            set zLoc = null
            set trailFX = null
            set collisionFX = null
            set dummycastFX = null
            set i = i + 1
        endloop

        if TestInstances == 0 then
            call PauseTimer(TestTimer)
        endif
    endmethod
    
    static method create takes unit whichUnit, real range returns TestData
        local integer i = 0
        local TestData data = TestData.allocate()
        local effect casterFX
        local location targetLoc = GetSpellTargetLoc()
        local real targetLocX = GetLocationX(targetLoc)
        local real targetLocY = GetLocationY(targetLoc)
        local real MissileFacing = (bj_RADTODEG*Atan2(targetLocY-GetUnitY(whichUnit),targetLocX-GetUnitX(whichUnit))) - CAST_ANGLE_OFFSET
        if IS_TARGETED == false then
            set targetLoc = Location(GetUnitX(whichUnit), GetUnitY(whichUnit))
            set targetLocX = GetLocationX(targetLoc)
            set targetLocY = GetLocationY(targetLoc)
            set MissileFacing = GetUnitFacing(whichUnit) - CAST_ANGLE_OFFSET
        endif
        if IS_UNIT_TARGETED == true then
            set data.targetedunit = GetSpellTargetUnit()
            set targetLoc = Location(GetUnitX(data.targetedunit), GetUnitY(data.targetedunit))
            set targetLocX = GetLocationX(targetLoc)
            set targetLocY = GetLocationY(targetLoc)
            set MissileFacing = (bj_RADTODEG*Atan2(targetLocY-GetUnitY(whichUnit),targetLocX-GetUnitX(whichUnit))) - CAST_ANGLE_OFFSET
        endif
        set data.TestTargets = CreateGroup()
        set data.caster =  whichUnit
        set data.casterLevel = GetUnitAbilityLevel(data.caster, ABIL_ID)
        if SPELL_HAS_LEVELS == true then
            set data.dummySpellLevel = GetUnitAbilityLevel(data.caster, ABIL_ID)
        endif
        set data.casterOwner = GetOwningPlayer(data.caster)
        set data.distance = 0.
        set data.maxDist = range + (RANGE_PER_LEVEL * data.casterLevel)
        set data.transparency = 1.
        set data.Missilesexplode = MISSILES_EXPLODE
        
        set casterFX = AddSpecialEffect(CAST_FX, GetUnitX(whichUnit), GetUnitY(whichUnit))
        call DestroyEffect(casterFX)
        set casterFX = null     
        loop
            exitwhen i == MISSILE_NUMBER
            
            call SetUnitPathing(data.caster, false)
            set data.Missiles[i] = CreateUnit(data.casterOwner, DUMMY_ID, GetUnitX(data.caster), GetUnitY(data.caster), (MissileFacing))
            call SetUnitPathing(data.Missiles[i], false)
            call SetUnitX(data.Missiles[i], GetUnitX(data.caster))
            call SetUnitY(data.Missiles[i], GetUnitY(data.caster))
            call SetUnitPathing(data.caster, true)
            call UnitAddAbility(data.Missiles[i], 'Amrf')
            call UnitRemoveAbility(data.Missiles[i], 'Amrf')
            call SetUnitVertexColor(data.Missiles[i], 255, 255, 255, 255)
            set MissileFacing = MissileFacing+(MISSILE_ANGLE - (MISSILE_ANGLE_LVL_REDUC * data.casterLevel))
            call SetUnitFacing(data.Missiles[i], MissileFacing)
            if IS_CIRCLE == true then
                set data.beams[0] = AddLightningEx(LIGHTNING_FX, true, GetUnitX(data.Missiles[0]), GetUnitY(data.Missiles[0]), FX_Z, GetUnitX(data.Missiles[i]), GetUnitY(data.Missiles[i]), FX_Z)
            endif
            if i != 0 then
                set data.beams[i] = AddLightningEx(LIGHTNING_FX, true, GetUnitX(data.Missiles[i-1]), GetUnitY(data.Missiles[i-1]), FX_Z, GetUnitX(data.Missiles[i]), GetUnitY(data.Missiles[i]), FX_Z)
            endif
            
            set i = i + 1
        endloop
                
        set TestArray[TestInstances] = data        
        
    	if TestInstances <= 0 then
            call TimerStart(TestTimer, TIMING_INTERVAL, true, function TestData.TestExecution)
        endif
        
        set TestInstances = TestInstances + 1
        set targetLoc = null
        
        return data
    endmethod
    
    method onDestroy takes nothing returns nothing
    local integer i = 0
    if .Missilesexplode == false then
    loop
        exitwhen i == MISSILE_NUMBER
        call RemoveUnit(.Missiles[i])
        set .Missiles[i] = null
        set i = i + 1
    endloop
    endif
    if .Missilesexplode == true then
    loop
        exitwhen i == MISSILE_NUMBER
        call KillUnit(.Missiles[i])
        set .Missiles[i] = null
        set i = i + 1
    endloop
    endif
    set i = 0
    loop
        exitwhen i == MISSILE_NUMBER
        call DestroyLightning(.beams[i])
        set .beams[i] = null
        set i = i + 1
    endloop
    endmethod
    
endstruct

//===================================/ Condition /===========================================================

private function checkTest takes nothing returns boolean
    local TestData localTest
	if GetSpellAbilityId() == ABIL_ID then
        set localTest = TestData.create(GetTriggerUnit(), CAST_RANGE)
	endif
	return false
endfunction

//===================================/ Init /================================================================

private function init takes nothing returns nothing
	local trigger localTrigVar = CreateTrigger()
	call TriggerRegisterAnyUnitEventBJ(localTrigVar, EVENT_PLAYER_UNIT_SPELL_EFFECT)
	call TriggerAddCondition( localTrigVar, Condition(function checkTest))
	set localTrigVar = null
endfunction

endscope

[youtube]
http://www.youtube.com/watch?v=pY6pbJBIowU&feature=youtu.be
[/youtube]

The problem doesn't seem to be there when missile count is, say, 1-3, but it was getting up there in 7-10 casts with it at 24.

The slowdown persists whenever a spell is recast. Like I said, it only seems to be happening when we're talking double digits. I try to recreate using multiple of the units casting simultaneously.

Also, oddly enough, it only seems to persist in the same area. Moving off screen and casting the ability renders no slow.

Pausing the game/tabbing out seem to as well help "alleviate" the problem. Keep in mind I've tried this on multiple computers to make sure it wasn't a my-side problem. Perhaps I'm just doing stuff I shouldn't be doing with the amount of things involved? I mean, I had no problem with single projectiles, but the moment we get into 12+ it seems to choke.

[youtube]
http://www.youtube.com/watch?v=Z5YCMA3Wfgo
[/youtube]
 

Attachments

  • MvMAbilityDump.w3x
    63.3 KB · Views: 41
Level 37
Joined
Mar 6, 2006
Messages
9,240
You might want to use a tree detection system from the spell database.
if boolean == true then -> if boolean then
You can do DestroyEffect(AddSpecialEffect(...))
The missiles should have Locust ability, so you don't need to disable their pathing
And more...

Make sure the dummies are set to Can't raise, does not decay in the object editor so the dummies are removed when they die and they do not decay there for 90 seconds.
 
Status
Not open for further replies.
Top