HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Method causing lag?

02-19-2010, 04:50 AM#1
Tastingo
Well to start this off, I'm just starting to learn VJASS. I know Java and am sort of using it as how I would use that, so I hope its presentable enough :). If not let me know what I should fix to make it more readable.

Anyway the struct I made stores data for multiple paintballs for each player with methods inside it. I am not using physics, and of course the paintballs are acting like missiles. The problem is with the move method. There is a trigger that I made that runs every .03 seconds and checks to see if a struct is not null and if there are units in the group, if so it runs the move method. I commented it a lot to help anyone that would read it. I also created a library with some math functions and ForGroup functions.

I would appreciate any help with why the game would be lagging so bad :/.

Collapse JASS:
library paintballFunctions
    function Distance takes real x, real y, real x2, real y2 returns real
        return SquareRoot((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y))
    endfunction
    
    function Angle takes real x, real y, real x2, real y2 returns real
        return bj_RADTODEG * Atan2(y2 - y, x2 - x)
    endfunction
    
    function paint takes string color, unit u returns nothing
        if color == "red" then
            call SetUnitVertexColor(u, 255, 0, 0, 255)
        elseif color == "blue" then
            call SetUnitVertexColor(u, 0, 0, 255, 255)
        elseif color == "teal" then
            call SetUnitVertexColor(u, 0, 200, 255, 255)
        elseif color == "purple" then
            call SetUnitVertexColor(u, 100, 0, 255, 255)
        elseif color == "yellow" then
            call SetUnitVertexColor(u, 100, 0, 255, 255)
        elseif color == "orange" then
            call SetUnitVertexColor(u, 255, 100, 0, 255)
        elseif color == "green" then
            call SetUnitVertexColor(u, 0, 255, 0, 255)
        elseif color == "pink" then
            call SetUnitVertexColor(u, 255, 0, 255, 255)
        elseif color == "gray" then
            call SetUnitVertexColor(u, 100, 100, 100, 255)
        elseif color == "light blue" then
            call SetUnitVertexColor(u, 75, 75, 255, 255)
        endif
    endfunction
    
    function enumUnit takes nothing returns nothing
        if(GetUnitState(GetEnumUnit(), UNIT_STATE_LIFE) > 0) then   //Check to see if not dead
            call tempStruct.splat(GetEnumUnit(), null)
        endif
    endfunction
    
    function enumDestructable takes nothing returns nothing
        if GetDestructableTypeId(GetEnumDestructable()) != 'B000' then  //Check to see if not mini barrel
            call tempStruct.splat(null, GetEnumDestructable())
        endif
    endfunction
endlibrary

Collapse JASS:
struct paintball
    private real array x[8] //Keeps track of x coordinates
    private real array y[8] //Keeps track of y coordinates
    private real array angle[8] //Keeps track of angle when shot
    private unit array paintballs[8]    //Keeps track of untis
    private unit owner  //The unit casting the spell
    private integer id  //The player's id
    private integer inGroup = 0 //Quick response to counting units in group
    private string color    //Color of paintball
    private group onField   //Units that are not hidden and are being used (I attempt to recycle)
    private integer array movements[8]  //The amount of movements the paintball has moved
    private integer maxMovements = 40   //The max amount of movements till kill is called
    
    static method create takes real x, real y, real angle, unit owner, string color returns paintball   //Sets everything up
        local paintball newBall = paintball.allocate()
        local integer i = 0
        
        
        set newBall.owner = owner
        set newBall.id = GetPlayerId(GetOwningPlayer(owner))
        set newBall.color = color
        set newBall.onField = CreateGroup()
        
        loop
            set newBall.x[i] = x
            set newBall.y[i] = y
            call newBall.createBalls(i)
            call ShowUnit(newBall.paintballs[i], false)
            set newBall.angle[i] = angle
            call paint(newBall.color, newBall.paintballs[i])
            set i = i + 1
            exitwhen i > 7
        endloop
        
        set owner = null
        return newBall
    endmethod
    
    private method createBalls takes integer i returns nothing  //Creates the paintballs, would not work in create
        set this.paintballs[i] = CreateUnit(Player(this.id), 'h000', this.x[i], this.y[i], this.angle[i])
    endmethod
    
    method shootPaintball takes real angle returns nothing  //Fake creating of paintballs
        local integer i = 0
        
        loop
            if( IsUnitInGroup(this.paintballs[i], this.onField) == false ) then //Check to see if already used
                set this.movements[i] = 0
                set this.angle[i] = angle
                set this.inGroup = this.inGroup + 1
                call GroupAddUnit(this.onField, this.paintballs[i]) //Add to moving units
                
                ////////////Move to caster///////////
                set this.x[i] = GetUnitX(this.owner)
                set this.y[i] = GetUnitY(this.owner)
                call SetUnitX(this.paintballs[i], this.x[i])
                call SetUnitY(this.paintballs[i], this.y[i])
                /////////////////////////////////////
                
                call ShowUnit(this.paintballs[i], true)
                call UnitAddAbility(this.paintballs[i], 'Aloc')
                set i = 10
            endif
            set i = i + 1
            exitwhen i > 7
        endloop
    endmethod
    
    method kill takes unit u, integer i returns nothing //Fake kill of paintballs
        call GroupRemoveUnit(this.onField, u)
        
        set this.inGroup = this.inGroup - 1
        set this.x[i] = 3945
        set this.y[i] = -3468
        call SetUnitX(this.paintballs[i], this.x[i])
        call SetUnitY(this.paintballs[i], this.y[i])
        
        call ShowUnit(this.paintballs[i], false)
    endmethod
    
    method count takes nothing returns integer  //Quick return of count units in group
        return this.inGroup
    endmethod
    
    method move takes nothing returns nothing   //Moves the paintball
        local real dist = 30
        local integer i = 0
        
        loop    //Move all units
            if(IsUnitInGroup(this.paintballs[i], this.onField) == true) then //Move only onField units
                set this.x[i] = this.x[i] + dist * Cos(this.angle[i] * bj_DEGTORAD)
                set this.y[i] = this.y[i] + dist * Sin(this.angle[i] * bj_DEGTORAD)
                call SetUnitX(this.paintballs[i], this.x[i])
                call SetUnitY(this.paintballs[i], this.y[i])
                
                set this.movements[i] = this.movements[i] + 1
                
                if this.movements[i] == this.maxMovements then //Check to see when done moving.
                    call this.kill(this.paintballs[i], i)
                endif
            endif
            set i = i + 1
            exitwhen i > 7
        endloop
        
        set tempStruct = this
        call ForGroup(allPlayers, function enumUnit)    //Check Distance Units
        call EnumDestructablesInRect(bj_mapInitialPlayableArea, null, function enumDestructable)    //Check Distance Destructables
    endmethod
    
    method changeColor takes string color returns nothing   //Change color of paintball
        local integer i = 0
        set this.color = color
        
        loop
            call paint(this.color, this.paintballs[i])
            set i = i + 1
            exitwhen i > 7
        endloop
    endmethod
    
    method splat takes unit u, destructable d returns nothing  //Checks to see if paintball hits a unit
        local integer i = 0

            if u == null then   //Checks to see if destructable
                loop
                    if Distance(this.x[i], this.y[i], GetDestructableX(d), GetDestructableY(d)) <= 70 then    //Check Distance 
                    
                    //////////Sound Effects///////////
                        if GetDestructableTypeId(d) == 'LTbr' then  //If barrel
                            call StartSound( gg_snd_wood )
                        elseif GetDestructableTypeId(d) == 'B001' then  //If tree
                            call StartSound( gg_snd_tree )
                        endif
                    /////////////////////////////////
                    
                        call this.kill(this.paintballs[i], i)
                    endif
                    
                    set i = i + 1
                    exitwhen i > 7
                endloop
            elseif GetUnitTypeId(u) != 'h000' and u != this.owner then    //Checks to see if unit and not a paintball and not owner
                loop
                
                    if Distance(this.x[i], this.y[i], GetUnitX(u), GetUnitY(u)) <= 65 then    //Check Distance
                        call paint(this.color, u)
                        call SetUnitState(u, UNIT_STATE_LIFE, GetUnitState(u, UNIT_STATE_LIFE) - 1)
                        
                        //////////Sound Effect///////////
                        call StartSound( gg_snd_flesh )
                        //////////////////////////////////
                        
                        call this.kill(this.paintballs[i], i)
                    endif
                    
                    set i = i + 1
                    exitwhen i > 7
                endloop
            endif
        
        set u = null
        set d = null
    endmethod
endstruct
02-22-2010, 03:32 PM#2
Tastingo
Does anyone happen to know, or do I need to explain my problem better?
02-22-2010, 03:50 PM#3
Earth-Fury
Function calls in JASS are slow. (vJass method calls compile down to function calls.) Avoid them at all costs in code that needs to be optimized.

Only optimize code that needs to be optimized. (Don't heavily optimize shit like stuff that runs on map init or a few times a minute or the like. That is almost completely pointless, and very insane.)

Don't use periodic triggers. Use timers.

The best way to do a lot of stuff on a very quick periodic timer is to build a list of active objects, and iterate through that list in a single periodic timer using one giant ass function to do all the heavy lifting.

Avoid using unit groups for the most part, as there are much quicker (and often times better) ways to do something without them. (There are some exceptions to this, of course.)

I'm really too lazy to heavily look over your code and give more specific examples, but this should be enough information to hopefully get you more on the right track to optimized JASS.

If you need more help, do ask.
02-22-2010, 10:05 PM#4
Kueken
Also, strings are quite slow in Jass, you might want to replace your paint string arguments with integers or maybe ARGB objects.
€ okay, since you call it only 1 time in the create method, it should be insignificant
02-23-2010, 02:31 PM#5
Iron_Doors
By looking at your code it seems to me that whenever your "move" method is called, you iterate over all units in a group "allPlayers", and for all living units in that group you call the "splat" method of a paintball from above it's declaration. Such calls are implemented in jasshelper by using TriggerEvaluate among other things, which is even slower than normally calling a function in jass. You also seem to do the same thing for presumably almost all destructables on the map.

However, out of all of those units, you act only on the ones in a 70.0 radius of any of the paintball units of the paintball struct in question. What you should do, is to enumerate only the units or destructables in that radius (or a bit larger) in the first place with GroupEnumUnitsInRange and whatever destructable counterpart comes closest to that. You should also try to move around your code to avoid unnecessary TriggerEvaluates.

Also you might want to store values such as dist * Cos(this.angle[i] * bj_DEGTORAD) in fields instead of computing them at every occasion anew.

Finally, you should probably have a single struct for every paintball unit instead of multiple ones, in order to avoid all of those IsUnitInGroup checks.


@Kueken
He has only a small set of different color strings so leaks shouldn't be a problem if that was what you meant with "strings are quite slow in Jass".
03-04-2010, 03:01 AM#6
Tastingo
Ok sorry been busy lately. I fixed it up a bit, and so far it is a lot better. I will rep you all since you all helped in ways. I have not made each paintball an instance yet, want to get everything running smoothly first before I make drastic changes like that. How exactly would I be using a timer instead of using the event, could someone care to explain? Like would I create one and have it run periodically at the beginning of the game?