• 🏆 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!

Lightweight IsTerrainWalkable?

Status
Not open for further replies.

Deleted member 219079

D

Deleted member 219079

Currently I use this:
JASS:
/**
 * IsTerrainWalkable snippet for estimating the walkability status of a co-ordinate pair, credits 
 * to Anitarf and Vexorian.
 * 
 * API:
 *     boolean IsTerrainWalkable(real x, real y) - returns the walkability of (x,y)
 */ 
library IsTerrainWalkable initializer init
    globals
    
        // this value is how far from a point the item may end up for the point to be considered pathable
        private constant real MAX_RANGE=10.
        
        // the following two variables are set to the position of the item after each pathing check
        // that way, if a point isn't pathable, these will be the coordinates of the nearest point that is
        public real X=0.
        public real Y=0.
        private rect r
        private item check
        private item array hidden
        private integer hiddenMax=0
    endglobals

    private function init takes nothing returns nothing
        set check=CreateItem('ciri',0.,0.)
        call SetItemVisible(check,false)
        set r=Rect(0.0,0.0,128.0,128.0)
    endfunction

    private function hideBothersomeItem takes nothing returns nothing
        if IsItemVisible(GetEnumItem()) then
            set hidden[hiddenMax]=GetEnumItem()
            call SetItemVisible(hidden[hiddenMax],false)
            set hiddenMax=hiddenMax+1
        endif
    endfunction

    function IsTerrainWalkable takes real x, real y returns boolean
    
        // first, hide any items in the area so they don't get in the way of our item
        call MoveRectTo(r,x,y)
        call EnumItemsInRect(r,null,function hideBothersomeItem)
        
        // try to move the check item and get its coordinates
        // this unhides the item...
        call SetItemPosition(check,x,y)
        set X=GetItemX(check)
        set Y=GetItemY(check)
        
        //...so we must hide it again
        call SetItemVisible(check,false)
        
        // before returning, unhide any items that got hidden at the start
        loop
            exitwhen hiddenMax==0
            set hiddenMax=hiddenMax-1
            call SetItemVisible(hidden[hiddenMax],true)
        endloop
        
        // return pathability status
        return (x-X)*(x-X)+(y-Y)*(y-Y)<MAX_RANGE*MAX_RANGE
    endfunction
endlibrary
It lags the h*ll out of my system when looping it for 200 instances.
 

Deleted member 219079

D

Deleted member 219079

I stripped it down to this:
JASS:
library IsTerrainWalkable initializer init
    globals
        public real X=0.
        public real Y=0.
        private item check
    endglobals
    function IsTerrainWalkable takes real x, real y returns boolean
        call SetItemPosition(check,x,y)
        set X=GetItemX(check)
        set Y=GetItemY(check)
        call SetItemVisible(check,false)
        return (x-X)*(x-X)+(y-Y)*(y-Y)<100
    endfunction
    private function init takes nothing returns nothing
        set check = CreateItem('ciri',0.,0.)
        call SetItemVisible(check,false)
    endfunction
endlibrary
still lags, so many function calls..
 
Level 12
Joined
Mar 13, 2012
Messages
1,121
How about removing the model of the item so you also wont need the SetItemVisible(..) lines.

But my biggest concern is that you maybe use very inefficient coding if such a little function makes wc3 lag?
 

Deleted member 219079

D

Deleted member 219079

It does it 200*32 times a second, I can assure you isterrainwalkable is the inefficient part
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
If you are really so sure about the problem is because of this function, then only alternative is this

  • (Terrain pathing at POINT of type Walkability is off) Equal to False
Also if you are using this function 200 times in a loop, you can remove SetItemVisible line from function and hide item yourself at the end of loop.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,197
(Terrain pathing at POINT of type Walkability is off) Equal to False
Only returns based on wpm data so does not factor in units (buildings) and destructibles which both modify pathing such that an area can become un-walkable.
function IsTerrainWalkable takes real x, real y returns boolean
call SetItemPosition(check,x,y)
set X=GetItemX(check)
set Y=GetItemY(check)
call SetItemVisible(check,false)
return (x-X)*(x-X)+(y-Y)*(y-Y)<100
endfunction
Try checking manhatan distance and not Euclidian distance. This will save on several arithmetic operations.

return (x-X)*(x-X)+(y-Y)*(y-Y)<100
becomes
return x - X + y - Y < 10

The only disadvantage is this deforms the displacement check into a square with corners aligned along X and Y. However it saves on 4 arithmetic operations.

call SetItemVisible(check,false)
This only needs to be called once after ever atomic section of triggers and not every time surely? The item will only appear visible if you allow time to progress to the next frame. If you are running many such tests simultaneously (in a loop, in the same frame etc) then you can skip calling it until the end. The simplest approach for loops would be to add it as a special library call after the loop finishes as part of finalization (so you run X tests which do not hide the item and then hide the item at the end).
 

Deleted member 219079

D

Deleted member 219079

If you are really so sure about the problem is because of this function
Not anymore.. It's because of what happens after when terrain is unwalkable, where I check is it unit or destructable or else. I've minimized the performance drain, but it still drops fps by ~15
Also if you are using this function 200 times in a loop, you can remove SetItemVisible line from function and hide item yourself at the end of loop.
That's marvelous :)

Edit: @DSG ; Seems like a compromise, but thanks!
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
return (x-X)*(x-X)+(y-Y)*(y-Y)<100
becomes
return x - X + y - Y < 10

The only disadvantage is this deforms the displacement check into a square with corners aligned along X and Y.

Does not matter if it's a circle, square, triangle or anything in this case since you effectively only want to check if it moves at all and the threshold can be picked even smaller.

For manhattan you have to have the absolutes of the differences though.
 

Deleted member 219079

D

Deleted member 219079

JASS:
library jPhysAddon /*v1.0.0*/
    globals
        private unit FN_RETURNED
        private destructable FN_RETURNED_DES
        private real R
        private real X
        private real Y
        private group G = CreateGroup()
    endglobals
    
    function FN_Cond takes nothing returns boolean
        return GetUnitMoveSpeed(GetFilterUnit())==0 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD)
    endfunction
    
    function FindNearestUnit takes real x, real y, real range returns unit
        local real dx = 0
        local real dy = 0
        local real mindist = range
        local real dist = 0
        local unit FoG
        set FN_RETURNED = null
        call GroupEnumUnitsInRange( G, x, y, range, function FN_Cond )
        loop
            set FoG = FirstOfGroup( G )
            exitwhen ( FoG == null )
            set dx = GetUnitX(FoG) - x
            set dy = GetUnitY(FoG) - y
            set dist = SquareRoot(dx * dx + dy * dy)
            if dist<mindist then
                set mindist = dist
                set FN_RETURNED = FoG
            endif
            call GroupRemoveUnit( G, FoG )
        endloop
        return FN_RETURNED
    endfunction
    
    private function FND_Enum takes nothing returns nothing
        local destructable d = GetEnumDestructable()
        local real dist = SquareRoot(/*
        */(X-GetDestructableX(d))*(X-GetDestructableX(d))+/*
        */(Y-GetDestructableX(d))*(Y-GetDestructableX(d)))
        if dist < R or R == 0 then
            set R = dist
            set FN_RETURNED_DES = d
        endif
        set d = null
    endfunction
    
    function FindNearestDest takes real x, real y, real range returns destructable
        local rect r = Rect(0,0,range,range)
        call MoveRectTo(r, x, y)
        set FN_RETURNED_DES = null
        call EnumDestructablesInRect(r, null, function FND_Enum)
        call RemoveRect(r)
        set r = null
        return FN_RETURNED_DES
    endfunction
endlibrary
I guess that these functions are the real performance drain.

I would love if ya guys spotted any performance losses from it..
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
It's enough to pick the squared distance rather than distance for comparison -> abandon SquareRoot. For the first element picked, you should rather check if the current element is null than via range. Recycle the rect with SetRect. Big R is given no starting value. If you use R for the first condition, that won't be right. GroupEnumUnitsInRange actually takes a boolexpr instead of a function pointer. JassHelper automatically replaces it. But this means the line is

call GroupEnumUnitsInRange( G, x, y, range, Condition(function FN_Cond) )

Condition recycles the same boolexpr for the same function everytime, yet you still lose some time by calling it. Use a variable.

The coordinates passed to MoveRectTo are the new center coordinates, not the min values, also you should make it a tad bigger due to the discretization of rect borders.
 
Status
Not open for further replies.
Top