Need help optimising 3d projectile system

MasterOfRa

New Member
Reaction score
10
I'm working on a projectile system for a game, and it works fine, but it is too laggy. I don't know what to do to optimize it.

edit: did minor updates,

The projectile Library handles the projectile dummies, and the collisions
(CJass used)
JASS:
library Projectile initializer init uses Recycler
    // uses Cjass
    
        private group dummyPool  // Used for recycling
        
    private function NewDummy takes real x, real y returns unit
        unit u = FirstOfGroup(dummyPool) // Get first of group
        if u == null then
            // if group was empty, create a new one
            u = CreateUnit(Player(15), UID_DUMMY,x,y,0)
            UnitAddAbility(u,AID_FLY)    // Enable HeightChange
            UnitRemoveAbility(u,AID_FLY)
            UnitAddAbility(u,AID_LOCUST)
        else
            // otherwise, set location, and remove unit from group
            SetUnitX(u,x)
            SetUnitY(u,y)
            GroupRemoveUnit(dummyPool, u)
            ShowUnit(u,true)
            UnitAddAbility(u,AID_LOCUST)
        endif
        return u
    endfunction
    
    private function ReleaseDummy takes unit u returns nothing
        GroupAddUnit(dummyPool,u)
        ShowUnit(u,false)
        UnitRemoveAbility(u,AID_LOCUST)
        // Add Dummy to Group
    endfunction
    
    struct Projectile
        implement PAM_Property
        //instance list
        readonly static integer num = 0
        readonly static Projectile array list
        readonly integer listIndex

        //unit
        readonly unit u     = null // The Dummy
        readonly unit owner = null // The Dummy
        readonly effect sfx = null // The Effect
        readonly real dx    = 0 // MomentumX
        readonly real dy    = 0 // MomentumY
        readonly real dz    = 0 // MomentumX
        readonly real x     = 0 // CurrentX
        readonly real y     = 0 // CurrentY
        readonly real z     = 0 // CurrentZ
        readonly real radius= 0 // CollisionRadius
        
        readonly real damage // Damage
        readonly real aoe    // area of effect
        readonly Shield shieldHit
        ///Updates all Projectiles by one second
        public static method CatchTick takes nothing returns nothing
            integer i
            Projectile t
            //Apply Momentum
            loopForIntBelow(i, Projectile.num)
                t = Projectile.list<i>
                t.ApplyMomentum()
                if Projectile.list<i> != t then
                    i--
                endif
            endloop

            integer z = Projectile.num
            //Check Collision
            loopForIntBelow(i, Projectile.num)
                t = Projectile.list<i>
                t.CheckCollision()
                z--
                if Projectile.list<i> != t then
                    i--
                endif
            endloop
        endmethod
        
        method CheckCollision takes nothing returns nothing
            MoveLocation(TempLoc,.x,.y)
            if .z - GetLocationZ(TempLoc) &lt;= 0 then // Check for ground hit.
                DestroyEffect(AddSpecialEffect(FID_lightninghit,.x,.y))
                .destroy()
                return
            endif
            unit u
            Unit unitData
            Shield shieldData =  Shield.ProjectileCollision(this)
            if shieldData != 0 then
                .shieldHit = shieldData
                DestroyEffect(AddSpecialEffect(FID_shieldhit,.x,.y))
                .destroy()
                return
            endif
            
            GroupEnumUnitsInRange(g,.x,.y,UnitEnumRange + ProjectileEnumRange,Filter_AliveUnit())
            loop
                u = FirstOfGroup(g)
                exitwhen u == null
                GroupRemoveUnit(g,u)
                unitData = Unit<u>
                if u != .owner then
                    if unitData != 0 then
                        if unitData.IsSphereIntersecting(.x,.y,.z,.radius)
                            DestroyEffect(AddSpecialEffect(FID_lightninghitkill,.x,.y))
                            .destroy()
                            return
                        endif
                    endif
                endif
            endloop
            
        endmethod
        
        method ApplyMomentum takes nothing returns nothing
            .x+= .dx * PERIOD// offset Location
            .y+= .dy * PERIOD//
            .z+= .dz * PERIOD//
            SetUnitX(.u,.x)// Move the unit
            SetUnitY(.u,.y)
            MoveLocation(TempLoc,.x,.y)
            SetUnitFlyHeight(.u,.z - GetLocationZ(TempLoc),0) // Set Height
            SetUnitFacing(.u,Atan2(.dy,.dx)*bj_RADTODEG)      // Set facing
            
            real dxy = SquareRoot(.dx*.dx+.dy*.dy)
            integer angle = R2I(Atan2(dxy,.dz)*bj_RADTODEG)
            if angle &lt; 0 then
                angle *= -1
            endif
            angle = 180 - angle
            SetUnitAnimationByIndex(.u,angle+1) // Set AngleV
            
            .dz -= GRAVITY*PERIOD
        endmethod
            
        ///Creates a Projectile structure for a given unit
        public static method create takes string SFX ,\ // Attached Effect
                                          real scale ,\ // Size
                                          real radius,\
                                          real x     ,\ // StartLocation
                                          real y     ,\
                                          real z     ,\
                                          real dx    ,\ // StartMomentum
                                          real dy    ,\
                                          real dz    ,\
                                          real damage,\
                                          real aoe   ,\
                                          unit owner  \
                                          returns thistype
            
            Projectile this
            macro_AllocCheck(this, Projectile)
            .u = NewDummy(x,y) // Get a new dummy unit
            SetUnitScale(.u,scale,scale,scale) // Set Size
            .sfx = AddSpecialEffectTarget(SFX,.u,&quot;origin&quot;) // Attach the Effect
            .x = x // Set Stored Location
            .y = y
            .z = z
            .dx = dx // Set Momentum
            .dy = dy
            .dz = dz
            .radius    = radius
            .damage    = damage
            .aoe       = aoe
            .shieldHit = 0
            .owner     = owner
            
            real offset = RMaxBJ(radius,aoe)
            ProjectileEnumRange = RMaxBJ(ProjectileEnumRange,offset)
            
            MoveLocation(TempLoc,x,y)
            SetUnitFlyHeight(.u,z-GetLocationZ(TempLoc),0) // Set Height
            SetUnitFacing(.u,Atan2(.dy,.dx)*bj_RADTODEG)
            
            real dxy = SquareRoot(dx*dx+dy*dy)
            integer angle = R2I(Atan2(dxy,dz)*bj_RADTODEG)
            if angle &lt; 0 then
                angle *= -1
            endif
            SetUnitAnimationByIndex(.u,angle+1) // Set AngleV
            

            
            .listIndex = Projectile.num
            Projectile.list[Projectile.num] = this
            Projectile.num += 1
            Projectile[.u] = this
            return this
        endmethod

        ///Cleans up properly
        private method onDestroy takes nothing returns nothing
            assert(this != 0)
            
            //Explode
            unit u
            Unit data
            integer z
            Shield s
            ShieldArray shieldArray = shieldArray.create()
            loopForIntBelow(z, Shield.num)
                s = Shield.list[z]
                SphereSphereInterection(s.x,s.y,s.z,s.size,.x,.y,.z,.aoe)
                if TempBool_C and (s != .shieldHit) then
                    shieldArray.AddShield(s)
                endif
                if TempBool_B and TempBool_C and s != .shieldHit then
                    s.TakeDamage(.damage)
                endif
            endloop
            
            if .shieldHit != 0 then
                .shieldHit.TakeDamage(.damage)
            endif
            GroupEnumUnitsInRange(g,.x,.y,UnitEnumRange + ProjectileEnumRange,Filter_AliveUnit())
            loop
                u = FirstOfGroup(g)
                exitwhen u == null
                GroupRemoveUnit(g,u)
                data = 0
                data = Unit<u>
                if data != 0 then
                    if u != .owner then
                        if data.IsSphereIntersecting(.x,\
                                                     .y,\
                                                     .z,\
                                                     .aoe)
                            data.UpdateShields()
                            data.shieldArray.Compair(shieldArray)
                            if TempInt_A == 0 then
                                data.TakeDamage(.u,.damage)
                            endif
                        endif
                    endif
                endif
            endloop
            
            
            
            //remove from global array
            Projectile.num -= 1
            Projectile.list[Projectile.num].listIndex = .listIndex
            Projectile.list[.listIndex] = Projectile.list[Projectile.num]
            Projectile.list[Projectile.num] = 0

            //destroy other stuff
            Projectile.RemoveKey(.u)
            DestroyEffect(.sfx)
            schedule_unit(Action_unit.ReleaseDummy, 5, .u)
            .sfx = null
            .u = null
            shieldArray.destroy()
        endmethod
    endstruct
    
    private function init takes nothing returns nothing
        TempLoc = Location(0.0,0.0) // Create the Temp Location
        dummyPool = NewGroup()      // Create the dummyPool
        integer z = 0
        loop
            unit u = CreateUnit(Player(15), UID_DUMMY,0,0,0)
            UnitAddAbility(u,AID_FLY)    // Enable HeightChange
            UnitRemoveAbility(u,AID_FLY)
            UnitAddAbility(u,AID_LOCUST)
            ReleaseDummy(u)
            z++
            exitwhen z &gt; 35
        endloop
        u = null
        TriggerSleepAction(.01)
        timer t = CreateTimer()
        TimerStart(t,PERIOD,true,function Projectile.CatchTick)
        t = null

    endfunction
endlibrary
</u></u></i></i></i></i>


The Unit Library handles units, the collision part is important.
JASS:
library Unit initializer init uses Recycler, Special, Constants
    
    constant integer    MAX_PROTECTING_SHIELDS = 25 // per array
    constant integer    MAX_ATTACKS            = 5  // per unit
    
    struct Unit
        implement PAM_Property
        //instance list
        readonly static integer num = 0
        readonly static Unit array list
        readonly integer listIndex
        
        //Collision
        readonly integer CollisionType = 0
        readonly real Radius           = 0
        readonly real SizeX            = 0
        readonly real SizeY            = 0
        readonly real SizeZ            = 0
        readonly real CollisionXOffset = 0
        readonly real CollisionYOffset = 0
        readonly real CollisionZOffset = 0
        
        //Attacks
        readonly real LanchAngleXY
        readonly real LanchXY
        readonly real LanchZ
        readonly integer AttackNum
        readonly Attack array Attacks[MAX_ATTACKS]
        readonly real   array LastAttack[MAX_ATTACKS]

        //Shielding
        readonly ShieldArray shieldArray
        
        //unit
        readonly unit u = null // The Unit
        readonly UnitData data
        readonly boolean air
        
        readonly real lifeGenDelay = 0
        readonly real baseLifeGen  = 0
        readonly real lastDamage   = 0
        
        readonly real manaGenDelay = 0
        readonly real baseManaGen  = 0
        readonly real lastManaUsed = 0
        
        method AddAttack takes Attack attack returns nothing
            .Attacks[.AttackNum] = attack
            .LastAttack[.AttackNum] = 0
            .AttackNum++
        endmethod
        
        method RemoveAttack takes Attack attack returns nothing
            integer z
            loopForIntBelow(z, .AttackNum)
                if .Attacks[z] == attack then
                    .Attacks[z] = .Attacks[.AttackNum]
                    .Attacks[.AttackNum] = 0
                    .AttackNum--
                endif
            endloop
        endmethod
        
        method InShield takes Shield s returns boolean
            if s.active then
                real x = GetUnitX(.u) + .CollisionXOffset
                real y = GetUnitY(.u) + .CollisionYOffset
                MoveLocation(TempLoc,x,y)
                real dx = s.x - x
                real dy = s.y - y
                real dz = s.z - (GetLocationZ(TempLoc)+.CollisionZOffset)
                real dxyz = ((dx*dx)+(dy*dy)+(dz*dz))
                if (dxyz &lt; (s.size * s.size)) then
                    return true
                endif
            endif
            return false
        endmethod
        
        method TakeDamage takes unit u, real amount returns nothing
            SetUnitState(.u,UNIT_STATE_LIFE, GetWidgetLife(.u) - amount)
            set .lastDamage = TimerGetElapsed(GameTimer)
        endmethod
        
        method IsSphereIntersecting takes \
               real x,    \ // Xcoord of the projectiles sphere
               real y,    \ // Ycoord of the projectiles sphere
               real z,    \ // Zcoord of the projectiles sphere
               real radius\ // Radius of the projectiles sphere
        returns boolean     // True if the objects overlap
            // Get current angle of the unit
            
            // Get current x,y,z of the unit.
            real unitXCoord = GetUnitX(.u)
            real unitYCoord = GetUnitY(.u)
            MoveLocation(TempLoc,unitXCoord,unitYCoord)
            real unitZCoord = GetLocationZ(TempLoc) + .CollisionZOffset
            
            // Get distance between the two centers
            real deltaX = x - unitXCoord
            real deltaY = y - unitYCoord
            real deltaZ = z - unitZCoord
            
            real angle = Atan2(deltaY,deltaX)
            
            unitXCoord += (.CollisionXOffset * Cos(angle))
            unitYCoord += (.CollisionYOffset * Sin(angle))
            
            deltaX = x - unitXCoord
            deltaY = y - unitYCoord
            
            real distanceBetweenPoints = 0.0
            real maxDistanceBetweenPoints    = 0.0
            real deltaXy  = 0.0
            real ax       = 0.0
            real ay       = 0.0
            real az       = 0.0
            
            if .CollisionType == COLLISION_TYPE_SPHERE then
                return SphereSphereInterection(x,y,z,radius,unitXCoord,unitYCoord,unitZCoord,.Radius)
                
            elseif .CollisionType == COLLISION_TYPE_CYLINDER then
                deltaXy = (deltaX * deltaX) + (deltaY * deltaY)
                boolean inCircle = deltaXy &lt;= (.Radius * .Radius)
                if z &gt; (unitZCoord + .SizeZ) then
                    az = unitZCoord + .SizeZ
                elseif z &lt; unitZCoord then
                    az = unitZCoord
                else
                    az = z
                endif
                if inCircle then
                    ax = x
                    ay = y
                else
                    ax = unitXCoord + (x * ((radius * radius) / deltaXy))
                    ay = unitYCoord + (y * ((radius * radius) / deltaXy))
                endif
                deltaX = x - ax
                deltaY = y - ay
                deltaZ = z - az
                distanceBetweenPoints = ((deltaX * deltaX) + \
                                         (deltaY * deltaY) + \
                                         (deltaZ * deltaZ))
                maxDistanceBetweenPoints = radius * radius
                if distanceBetweenPoints &lt;= (maxDistanceBetweenPoints) then
                    return true
                else
                    return false
                endif
            elseif .CollisionType == COLLISION_TYPE_CUBE
            
                deltaXy = SquareRoot((deltaX * deltaX) + (deltaY * deltaY))
                x = unitXCoord + deltaXy * Cos(angle - GetUnitFacing(.u))
                y = unitYCoord + deltaXy * Sin(angle - GetUnitFacing(.u))
                
                if x &gt; (unitXCoord + (.SizeX / 2)) then
                   ax = unitXCoord + (.SizeX / 2)
                elseif x &lt; (unitXCoord - (.SizeX / 2)) then
                       ax = unitXCoord - (.SizeX / 2)
                    else
                       ax = x
                endif
                
                if y &gt; (unitYCoord + (.SizeY / 2)) then
                   ay = unitYCoord + (.SizeY / 2)
                elseif y &lt; (unitYCoord - (.SizeY / 2)) then
                       ay = unitYCoord - (.SizeY / 2)
                else
                    ay = y
                endif
                
                if z &gt; (unitZCoord + .SizeZ) then
                   az = unitZCoord + .SizeZ
                elseif z &lt; (unitZCoord) then
                       az = unitZCoord
                else
                    az = z
                endif
                
                deltaX = x - ax
                deltaY = y - ay
                deltaZ = z - az
                
                distanceBetweenPoints = ((deltaX * deltaX) + \
                                         (deltaY * deltaY) + \
                                         (deltaZ * deltaZ))
                maxDistanceBetweenPoints = radius * radius
                
                if distanceBetweenPoints &lt;= (maxDistanceBetweenPoints) then
                    return true
                else
                    return false
                endif
            endif
            
            return false
        endmethod
        
        ///Updates all Units by one second
        public static method CatchTick takes nothing returns nothing
            integer i
            Unit t

            
            loopForIntBelow(i, Unit.num)
                t = Unit.list<i>
                t.ApplyPeriodic()
            endloop
            
            loopForIntBelow(i, Unit.num)
                t = Unit.list<i>
                t.UpdateShields()
            endloop
            
            loopForIntBelow(i, Unit.num)
                t = Unit.list<i>
                t.Attack()
            endloop
            
        endmethod
        
        method Attack takes nothing returns nothing
            integer z
            Attack a
            Unit target = 0
            loopForIntBelow(z, .AttackNum)
                a = .Attacks[z]
                if (.LastAttack[z] + a.cooldown) &lt;= TimerGetElapsed(GameTimer) then
                    GroupEnumUnitsInSector(g,GetUnitX(.u),GetUnitY(.u),GetUnitFacing(.u)*bj_DEGTORAD,a.range,30*bj_DEGTORAD,Filter_AliveEnemyUnit(GetOwningPlayer(.u)))
                    target = Unit[FirstOfGroup(g)]
                    GroupClear(g)
                    if target != 0 then
                        if GetUnitState(.u,UNIT_STATE_MANA) &gt;= a.manaCost then
                            SetUnitState(.u,UNIT_STATE_MANA,GetUnitState(.u,UNIT_STATE_MANA) - a.manaCost)
                            if a.manaCost &gt; .5 then
                                .lastManaUsed = TimerGetElapsed(GameTimer)
                            endif
                            SetUnitAnimation(.u,&quot;attack&quot;)
                            a.Attack(this,target)
                            .LastAttack[z] = TimerGetElapsed(GameTimer)
                        endif
                    endif
                endif
            endloop
        endmethod
        
        method UpdateShields takes nothing returns nothing
            integer z
            Shield s
            loopForIntBelow(z, Shield.num)
                s = Shield.list[z]
                if .shieldArray.ContainsShield(s) then
                    if not .InShield(s) then
                        .shieldArray.RemoveShield(s)
                    endif
                else
                    if .InShield(s) then
                        .shieldArray.AddShield(s)
                    endif
                endif
            endloop
        endmethod
        
        method ApplyPeriodic takes nothing returns nothing
            real life
            if (.lastDamage + .lifeGenDelay) &lt;= TimerGetElapsed(GameTimer) then
                life = GetWidgetLife(.u)
                life += .baseLifeGen
                SetUnitState(.u,UNIT_STATE_LIFE,life)
            endif
            
            if (.lastManaUsed + .manaGenDelay) &lt;= TimerGetElapsed(GameTimer) then
                life = GetUnitState(.u,UNIT_STATE_MANA)
                life += .baseManaGen
                SetUnitState(.u,UNIT_STATE_MANA,life)
            endif
            
        endmethod
            
        ///Creates a Unit structure for a given unit
        public static method create takes unit u\
                                          returns thistype
            
            Unit this
            macro_AllocCheck(this, Unit)
            .shieldArray = ShieldArray.create()
            .u = u
            .data = UnitData[GetUnitTypeId(u)]
            setUnitMaxLife(u,.data.life)
            setUnitMaxMana(u,.data.mana)
            .lifeGenDelay     = .data.lifeGenDelay
            .baseLifeGen      = .data.lifeGen * PERIOD
            .manaGenDelay     = .data.manaGenDelay
            .baseManaGen      = .data.manaGen * PERIOD
            .CollisionType    = .data.CollisionType
            .Radius           = .data.Radius
            .SizeX            = .data.SizeX
            .SizeY            = .data.SizeY
            .SizeZ            = .data.SizeZ
            .CollisionXOffset = .data.CollisionXOffset
            .CollisionYOffset = .data.CollisionYOffset
            .CollisionZOffset = .data.CollisionZOffset
            .LanchAngleXY     = .data.LanchAngleXY
            .LanchXY          = .data.LanchXY
            .LanchZ           = .data.LanchZ
            
            loopForIntBelow(.AttackNum, .data.AttackNum)
                .Attacks[.AttackNum] = .data.Attacks[.AttackNum]
                .LastAttack[.AttackNum] = 0
            endloop
            
            
            .listIndex = Unit.num
            Unit.list[Unit.num] = this
            Unit.num += 1
            Unit[.u] = this
            return this
        endmethod

        ///Cleans up properly
        private method onDestroy takes nothing returns nothing
            assert(this != 0)

            .shieldArray.destroy()
            //remove from global array
            Unit.num -= 1
            Unit.list[Unit.num].listIndex = .listIndex
            Unit.list[.listIndex] = Unit.list[Unit.num]
            Unit.list[Unit.num] = 0

            //destroy other stuff
            Unit.RemoveKey(.u)
            .u = null
            
        endmethod
    endstruct
    
    private function CatchDeath takes nothing returns nothing
        Unit t = Unit[GetTriggerUnit()]
        if t != 0 then; t.destroy(); endif
    endfunction

    
    private function init takes nothing returns nothing
        TriggerSleepAction(.01)
        timer t = CreateTimer()
        TimerStart(t,PERIOD,true,function Unit.CatchTick)
        macro_AddUnitEventHandler(EVENT_PLAYER_UNIT_DEATH, function CatchDeath)
        
        
        TriggerSleepAction(2)
        unit u
        Unit.create(CreateUnit(Player(0),UID_RIFLEMAN    , -350,  175,90))
        Unit.create(CreateUnit(Player(0),UID_SIEGE_ENGINE, -175,  350,90))
        Unit.create(CreateUnit(Player(0),UID_SORCERESS   , -350,  350,90))
        
        Unit.create(CreateUnit(Player(1),UID_RIFLEMAN    , -350, -175,90))
        Unit.create(CreateUnit(Player(1),UID_SIEGE_ENGINE, -175, -350,90))
        Unit.create(CreateUnit(Player(1),UID_SORCERESS   , -350, -350,90))
        
        Unit.create(CreateUnit(Player(2),UID_RIFLEMAN    ,  350,  175,90))
        Unit.create(CreateUnit(Player(2),UID_SIEGE_ENGINE,  175,  350,90))
        Unit.create(CreateUnit(Player(2),UID_SORCERESS   ,  350,  350,90))
        
        Unit.create(CreateUnit(Player(3),UID_RIFLEMAN    ,  350, -175,90))
        Unit.create(CreateUnit(Player(3),UID_SIEGE_ENGINE,  175, -350,90))
        Unit.create(CreateUnit(Player(3),UID_SORCERESS   ,  350, -350,90))
        
        Unit.create(CreateUnit(Player(4),UID_SORCERESS,    -1024,-1024,90))
        Unit.create(CreateUnit(Player(4),UID_SORCERESS,    -1024, 1024,90))
        Unit.create(CreateUnit(Player(4),UID_SORCERESS,     1024,-1024,90))
        Unit.create(CreateUnit(Player(4),UID_SORCERESS,     1024, 1024,90))
        
        u = CreateUnit(Player(4),UID_SORCERESS,    -1536,    0,90)
        Unit.create(u)
        u = CreateUnit(Player(4),UID_SORCERESS,    -1536,  128,90)
        Unit.create(u)
        u = CreateUnit(Player(4),UID_SORCERESS,    -1536, -128,90)
        Unit.create(u)
        //Shield.create(Player(4),u,-1536,0,GetLocationZ(TempLoc) + 0,100,100,0,2,25)
        
        u = CreateUnit(Player(4),UID_SORCERESS,     1536,    0,90)
        Unit.create(u)
        u = CreateUnit(Player(4),UID_SORCERESS,     1536,  128,90)
        Unit.create(u)
        u = CreateUnit(Player(4),UID_SORCERESS,     1536, -128,90)
        Unit.create(u)
        //Shield.create(Player(4),u, 1536,0,GetLocationZ(TempLoc) + 0,100,100,0,2,25)
        
        u = CreateUnit(Player(4),UID_SORCERESS,        0,-1536,90)
        Unit.create(u)
        u = CreateUnit(Player(4),UID_SORCERESS,      128,-1536,90)
        Unit.create(u)
        u = CreateUnit(Player(4),UID_SORCERESS,     -128,-1536,90)
        Unit.create(u)
        //Shield.create(Player(4),u,0,-1536,GetLocationZ(TempLoc) + 0,100,100,0,2,25)
        
        u = CreateUnit(Player(4),UID_SORCERESS,        0, 1536,90)
        Unit.create(u)
        u = CreateUnit(Player(4),UID_SORCERESS,      128, 1536,90)
        Unit.create(u)
        u = CreateUnit(Player(4),UID_SORCERESS,     -128, 1536,90)
        Unit.create(u)
        //Shield.create(Player(4),u,0, 1536,GetLocationZ(TempLoc) + 0,100,100,0,2,25)
        
        u = null
    endfunction
endlibrary
</i></i></i>

The Shield Library handles shields, bubbles that absorb projectiles that hit the outside.
JASS:
library Shields initializer init uses Recycler

    private group dummyPool  // Used for recycling
    
    private function NewDummy takes real x, real y, real z, real size, player owner returns unit
        local unit u = FirstOfGroup(dummyPool) // Get first of group
        if u == null then
            // if group was empty, create a new one
            u = CreateUnit(owner, UID_SHIELD,x,y,0)
            SetUnitX(u,x)
            SetUnitY(u,y)
            call UnitAddAbility(u,AID_FLY)    // Enable HeightChange
            call UnitRemoveAbility(u,AID_FLY)
        else
            // otherwise, set location, and remove unit from group
            SetUnitOwner(u,owner,true)
            SetUnitX(u,x)
            SetUnitY(u,y)
            GroupRemoveUnit(dummyPool, u)
            ShowUnit(u,true)
        endif
        UnitAddAbility(u,AID_LOCUST)
        MoveLocation(TempLoc,x,y)
        SetUnitFlyHeight(u,z-GetLocationZ(TempLoc),0)
        SetUnitScale(u,size,size,size)
        UnitAddAbility(u,AID_LOCUST)
        return u
    endfunction
    
    private function ReleaseDummy takes unit u returns nothing
        GroupAddUnit(dummyPool,u)
        ShowUnit(u,false)
        UnitRemoveAbility(u,AID_LOCUST)
        // Add Dummy to Group
    endfunction
    
    struct Shield
        implement PAM_Property
        //instance list
        readonly static integer num = 0
        readonly static Shield array list
        readonly integer listIndex
        
        readonly unit Dummy          = null // The shield
        readonly unit Generator      = null // The shield Generator
        readonly boolean active      = false
        readonly real lifeGenDelay   = 0
        readonly real lifeGen        = 0
        readonly real lastDamage     = 0
        readonly real life           = 0
        readonly real lifeMax        = 0
        readonly real size           = 0    // The Shields radius
        readonly real x              = 0    // Where the shield is
        readonly real y              = 0    // 
        readonly real z              = 0    //
        readonly real xOffset        = 0    // from the Generator
        readonly real yOffset        = 0    //
        readonly real zOffset        = 0    //
        readonly real sizeThreshhold = 0    // A factor in Operating Costs
        readonly real sizeMult       = 0    // A factor in Operating Costs
        readonly real regenCost      = 0    // The cost of regenerating life from 0 to max
        
        method TakeDamage takes real amount returns nothing
            .life -= amount
            set .lastDamage = TimerGetElapsed(GameTimer)
            if .life &lt;= 0 then
                .Deactivate()
            endif

            SetUnitVertexColor(.Dummy,255,255,255,R2I(.life/.lifeMax * 255))
        endmethod
        
        method Deactivate takes nothing returns nothing
            .active = false
            ShowUnit(.Dummy,false)
            UnitRemoveAbility(.Dummy,AID_LOCUST)
        endmethod
        
        method Activate takes nothing returns nothing
            .active = true
            ShowUnit(.Dummy,true)
            UnitAddAbility(.Dummy,AID_LOCUST)
        endmethod
        
        public static method CatchTick takes nothing returns nothing
            integer i
            Shield t
            loopForIntBelow(i, Shield.num)
                t = Shield.list<i>
                t.ApplyPeriodic()
            endloop
        endmethod
        
        method ApplyPeriodic takes nothing returns nothing
            if .Generator != null then
                if GetWidgetLife(.Generator) &lt;= 4.05 then
                    .life -= (.lifeMax * PERIOD / 10)
                    if .life &lt;= 0 then
                        .destroy()
                    endif
                endif
                .x = GetUnitX(.Generator) + .xOffset
                .y = GetUnitY(.Generator) + .yOffset
                MoveLocation(TempLoc,GetUnitX(.Generator),GetUnitY(.Generator))
                .z = GetLocationZ(TempLoc) + .zOffset
                SetUnitX(.Dummy,.x)
                SetUnitY(.Dummy,.y)
                SetUnitFlyHeight(.Dummy,.z,2)
                
                Projectile t
                integer x
                integer z = Projectile.num
                //Check Collision
                loopForIntBelow(x, Projectile.num)
                    t = Projectile.list[x]
                    t.CheckCollision()
                    z--
                    if Projectile.list[x] != t then
                        x--
                    endif
                endloop
            endif
            
            if (.lastDamage + .lifeGenDelay) &lt;= TimerGetElapsed(GameTimer) then
                if .active then
                    .life += (.lifeGen * PERIOD)
                else
                    .life += (.lifeGen * PERIOD * 10)
                endif
            endif
            
            .life = RMinBJ(.life,.lifeMax)
            if not .active and .life == .lifeMax then
                .Activate()
            endif
            
            SetUnitVertexColor(.Dummy,255,255,255,R2I(.life/.lifeMax * 255))
            // This regen is free, but later charge .sizeMult * life / 1000 energy or somthing like that

        endmethod
        
        private method ProjectileCollisionChild takes Projectile data returns boolean
            boolean intersects     = SphereSphereInterection(.x,.y,.z,.size,data.x,data.y,data.z,data.radius)
            boolean touchesShell   = TempBool_A
            boolean willIntersect  = SphereSphereInterection(.x,.y,.z,.size,data.x + (data.dx * PERIOD),\
                                                                            data.y + (data.dy * PERIOD),\
                                                                            data.z + (data.dz * PERIOD),\
                                                                            data.radius)
            boolean willTouchShell = TempBool_A
            if willIntersect and not intersects then
                return true
            endif
            return false
        endmethod
        
        static method ProjectileCollision takes Projectile data returns Projectile
            integer i
            Shield t
            loopForIntBelow(i, Shield.num)
                t = Shield.list<i>
                if t.active then
                    if t.ProjectileCollisionChild(data) then
                        return t
                    endif
                endif
            endloop
            return 0
        endmethod
        
        ///Creates a Projectile structure for a given unit
        public static method create takes player owner       ,\
                                          unit generator     ,\
                                          real x             ,\
                                          real y             ,\
                                          real z             ,\
                                          real sizeThreshhold,\
                                          real size          ,\ 
                                          real lifeGenDelay  ,\ 
                                          real lifeGen       ,\ 
                                          real life           \
                                          returns thistype
            
            Shield this
            macro_AllocCheck(this, Shield)
            .Dummy          = NewDummy(x,y,z,size/SHIELD_SIZE,owner)
            .Generator      = generator
            .active         = true
            .x              = x
            .y              = y
            .z              = z
            if generator != null then
                .xOffset        = x - GetUnitX(generator)
                .yOffset        = y - GetUnitY(generator)
                MoveLocation(TempLoc,GetUnitX(generator),GetUnitX(generator))
                .zOffset        = z - GetLocationZ(TempLoc)
            else
                .xOffset = 0
                .yOffset = 0
                .zOffset = 0
            endif
            .size           = size
            .lifeGenDelay   = lifeGenDelay
            .lifeGen        = lifeGen
            .lifeMax        = life
            .life           = life
            .sizeThreshhold = sizeThreshhold
            .sizeMult       = size/sizeThreshhold
            ShieldEnumRange = RMaxBJ(ShieldEnumRange,size)
            if .sizeMult &gt; 1 then
                .sizeMult *= .sizeMult
            endif
            .regenCost     = .sizeMult * life / 10
            .listIndex = Shield.num
            Shield.list[Shield.num] = this
            Shield.num += 1
            Shield[.Dummy] = this
            return this
        endmethod

        ///Cleans up properly
        private method onDestroy takes nothing returns nothing
            assert(this != 0)
            
            //remove from global array
            Shield.num -= 1
            Shield.list[Shield.num].listIndex = .listIndex
            Shield.list[.listIndex] = Shield.list[Shield.num]
            Shield.list[Shield.num] = 0
            //destroy other stuff
            Shield.RemoveKey(.Dummy)
            
        endmethod
    endstruct
    
    struct ShieldArray
        
        readonly integer ShieldCount = 0
        readonly Shield array Shields[MAX_PROTECTING_SHIELDS]
        
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            return this
        endmethod
        
        method onDestroy takes nothing returns nothing
            loop
                exitwhen .ShieldCount == 0
                .Shields[.ShieldCount] = 0
                .ShieldCount--
            endloop
        endmethod
        
        method AddShield takes Shield s returns nothing
            .Shields[.ShieldCount] = s
            .ShieldCount++
        endmethod
        
        method RemoveShield takes Shield s returns nothing
            integer z = 0
            loop
                if .Shields[z] == s then
                   .ShieldCount--
                   .Shields[z] = .Shields[.ShieldCount]
                   .Shields[.ShieldCount] = 0
                endif
                exitwhen z &gt;= .ShieldCount
                z++
            endloop
        endmethod
        
        method ContainsShield takes Shield s returns boolean
            integer z = 0
            loop
                if .Shields[z] == s then
                    return true
                endif
                exitwhen z &gt;= .ShieldCount
                z++
            endloop
            return false
        endmethod
        
        method Compair takes ShieldArray that returns nothing
            integer x = 0
            integer y = 0
            boolean array thisMatched
            boolean array thatMatched

            loop
                exitwhen x &gt;= .ShieldCount
                thisMatched[x] = false
                x++
            endloop

            loop
                exitwhen y &gt;= that.ShieldCount
                thatMatched[y] = false
                y++
            endloop
            x = 0
            y = 0
            loop
                exitwhen x &gt;= .ShieldCount
                y = 0
                loop
                    exitwhen y &gt;= that.ShieldCount
                    if .Shields[x] == that.Shields[y] then
                        thisMatched[x] = true
                        thatMatched[y] = true
                    endif
                    y++
                endloop
                x++
            endloop
            x = 0
            TempInt_A = 0 // how many of this&#039;s shields had no match
            y = 0
            TempInt_B = 0 // how many of that&#039;s shields had no match
            loop
                exitwhen x &gt;= .ShieldCount
                if not thisMatched[x] then
                    TempInt_A++
                endif
                x++
            endloop
            loop
                exitwhen y &gt;= that.ShieldCount
                if not thatMatched[y] then
                    TempInt_B++
                endif
                y++
            endloop
        endmethod
    endstruct
    
    private function init takes nothing returns nothing
        dummyPool = CreateGroup()
        TriggerSleepAction(.01)
        timer t = CreateTimer()
        TimerStart(t,PERIOD,true, function Shield.CatchTick)
        MoveLocation(TempLoc,0,0)
        unit u = CreateUnit(Player(0),UID_SHIELD_GENERATOR    , 0, 0,270)
        Unit.create(u)
        Shield.create(Player(0),u,0,0,GetLocationZ(TempLoc) + 0,500,400,5,2,100)
        u = null
    endfunction
endlibrary
</i></i>

The UnitDatabase Library handles unit types.
JASS:
library UnitDatabase uses General, PAM
    struct UnitData
        implement PAM_RawProperty
        
        integer tintRed = 255
        integer tintBlue = 255
        integer tintGreen = 255
        // Unit
        readonly integer unitId
        readonly integer life
        readonly integer mana
        readonly real lifeGenDelay
        readonly real lifeGen
        readonly real manaGenDelay
        readonly real manaGen
        // Collision
        readonly integer CollisionType
        readonly real    Radius
        readonly real    SizeX
        readonly real    SizeY
        readonly real    SizeZ
        readonly real    CollisionXOffset
        readonly real    CollisionYOffset
        readonly real    CollisionZOffset
        
        // Attack
        readonly real    LanchAngleXY
        readonly real    LanchXY
        readonly real    LanchZ
        
        readonly integer AttackNum = 0
        readonly Attack array Attacks[MAX_ATTACKS]
        
        method AddAttack takes Attack attack returns nothing
            .Attacks[.AttackNum] = attack
            .AttackNum++
        endmethod
        
        public static method create takes integer unitId       ,\
                                          integer life         ,\
                                          integer mana         ,\
                                          real lifeGenDelay    ,\
                                          real lifeGen         ,\
                                          real manaGenDelay    ,\
                                          real manaGen         ,\
                                          integer CollisionType,\
                                          real Radius          ,\
                                          real SizeX           ,\
                                          real SizeY           ,\
                                          real SizeZ           ,\
                                          real CollisionXOffset,\
                                          real CollisionYOffset,\
                                          real CollisionZOffset,\
                                          real LanchX          ,\
                                          real LanchY          ,\
                                          real LanchZ           \
                                          returns thistype
            assert(unitId != 0)
            
            UnitData this
            macro_AllocCheck(this, UnitData)
            
            .unitId  = unitId
            .life = life
            .mana = mana
            .lifeGenDelay = lifeGenDelay
            .lifeGen = lifeGen
            .manaGenDelay =  manaGenDelay
            .manaGen = manaGen
            .CollisionType   = CollisionType
            .Radius           = Radius
            .SizeX            = SizeX
            .SizeY            = SizeY
            .SizeZ            = SizeZ
            .CollisionXOffset = CollisionXOffset
            .CollisionYOffset = CollisionYOffset
            .CollisionZOffset = CollisionZOffset
            .LanchAngleXY     = Atan2(LanchY,LanchX)
            .LanchXY          = SquareRoot((LanchX * LanchX) + (LanchY * LanchY))
            .LanchZ           = LanchZ
            
            real offset
            if Radius &gt; 0 then
                offset  = Radius
                offset += SquareRoot((CollisionXOffset * CollisionXOffset) + (CollisionYOffset * CollisionYOffset))
                UnitEnumRange = RMaxBJ(UnitEnumRange,offset)
            endif
            if SizeX &gt; 0 or SizeY &gt; 0 then
                real dx = SizeX + CollisionXOffset
                real dy = SizeY + CollisionYOffset
                offset  = SquareRoot((dx + dx) + (dy + dy))
                UnitEnumRange = RMaxBJ(UnitEnumRange,offset)
            endif

            UnitData[unitId] = this
            return this
        endmethod
        
        private method onDestroy takes nothing returns nothing
            assert(this != 0)
            UnitData[.unitId] = 0
        endmethod
    endstruct
endlibrary
 

Jesus4Lyf

Good Idea™
Reaction score
397
Well, since only the first line is really readable to me, I'll comment on that.

>[LJASS]library Projectile initializer init uses Recycler, TT[/LJASS]
-->
>[LJASS]library Projectile initializer init uses Recycle, T32[/LJASS]

For a system like this there is nearly no doubt you should either use T32 or code the timer stuff yourself.

As for the rest: cJass... :p
You're using macros and stuff or something and God knows what it compiles to. I can't help. We don't all know cJass.

But I'll say that you shouldn't use any unit groups anywhere in your code.
Colision detection is a unit enters range trigger, recycling dummies is an array with an index global.
 

Nestharus

o-o
Reaction score
84
I'll give you optimization tips in a few...

need to finish some c# stuff, I'll edit this post soon.


To say the least I looked at your code earlier and there is a lot that can be improved... like j4l, the unit group is a "big" one ><.


So many projectile systems at this point too (eep).

Edit
Ok, let's go through this

JASS:
library Projectile initializer init uses Recycle, T32

jesus4lyf is very right here.

JASS:

            group g = NewGroup()
            unit u


Colision detection is a unit enters range trigger,

Locust means that that can't be done, unless he were to register every unit on the map that didn't have locust.. He has to do a group enumeration.

Get rid of every single group in all of your code and just use one global group for your group enumeration

JASS:

loop
                u = FirstOfGroup(g)
                exitwhen u == null
                GroupRemoveUnit(g,u)
                unitData = Unit<u>
                if u != .owner then
                    if unitData != 0 then
                        if unitData.IsSphereIntersecting(.x,.y,.z,.radius)
                            DestroyEffect(AddSpecialEffect(FID_lightninghitkill,.x,.y))
                            .destroy()
                            return
                        endif
                    endif
                endif
            endloop
</u>


Do that in your filter

JASS:

private method onDestroy takes nothing returns nothing


That's a trigger evaluation through destroy. Use destroy instead

JASS:

GetUnitState(.u,UNIT_STATE_MANA) &gt;= a.manaCost


Replace all of your unit state stuff with WidgetLife etc

JASS:

MoveLocation(TempLoc,GetUnitX(generator),GetUnitX(generator))


If you already set the vars, then use the vars instead of the natives. Keep your function calls to a minimum to reduce lag.

Furthermore, store common values. Try to do as little work and try to make as little function calls as possible. In any system like this that can have a huge amount of instances, optimization is really important as you probably know by now.

Now, the fact is you have so much code in there that it'd just take forever to look over ><.

Fix everything me 'n jesus4lyf told you to fix and we'll get started on the rest. These fixes alone should reduce a lot of your lag ; ).
 

MasterOfRa

New Member
Reaction score
10
I have a group recycler, which lets me reuse groups at all times.
JASS:
group g = NewGroup()
ReleaseGroup(g)
g = null


I also just noticed that i haven't been nulling my group variables, which is a major leakage.

I also was not even using TT for anything other than a period variable, so it has been removed.

As for the filter optimization, i tried that, and i screwed up badly, and it took a while to fix it back to how it was.
 

Viikuna

No Marlo no game.
Reaction score
265
Actually, if you recycle your groups, they cant leak handle ids, so nulling them is not neccesary really.
 

Jesus4Lyf

Good Idea™
Reaction score
397
I also was not even using TT for anything other than a period variable, so it has been removed.
See, I knew that. I didn't say you were using it.
JASS:
macro_AddPeriodicHandler(TT_PERIOD, function Projectile.CatchTick) // Start a periodic timer

I saw that and gave up trying to help you, and just blanketly said "use T32". :thup:
Reason is, I can't tell what you are using, but chances are T32 is what you should use.

>I also just noticed that i haven't been nulling my group variables, which is a major leakage.
Stop using groups? Why are you using groups? Who cares about groups...
>Get rid of every single group in all of your code and just use one global group for your group enumeration
That.

>it took a while to fix it back to how it was.
Backup your work. :)

>Locust means that that can't be done, unless he were to register every unit on the map that didn't have locust.. He has to do a group enumeration.
As if projectiles should hit locusted units! :eek: XD
Ofc, if projectiles should hit other projectiles, there's another story...
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top