- 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.
[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]
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]