Warcraft III resources & community, 2003–2006 · archived

JASS #298

not rated
Submitted by DaminonFunctions0 downloads
CV_Point, real CV_Radius, integer CV_N returns group
//******************************************************************************
//* GET THE N CLOSEST UNITS TO A LOCATION OF A GROUP
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* Made by Daminon
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* This function finds the N closest units of a group to a location and returns a group containing them.
//* IMPORTANT:
//* - You must remember the radius used for adding the units to CV_Group1 so it
//* can be used in CV_Radius. You must set CV_Radius to a greater vaule than
//* the pick-radius for CV_Group1.
//* - Be sure to not set a greater vaule to CV_N than the amount of units in
//* CV_Group1.
//* - Never use this function when there are 0 units in the given group.
//******************************************************************************

Source

function CF_GetNClosestUnits takes group CV_Group1, location 
    local unit CV_First  
    local unit CV_Closest 
    local integer CV_UnitAmount 
    local real CV_Distance 
    local real CV_TestDistance 
    local location CV_FirstLocation 
    local group CV_Group2 
    local group CV_Group3
    set CV_Distance = CV_Radius  
    set CV_Group2 = CreateGroup()
    set CV_Group3 = CreateGroup()
    call GroupAddGroup(CV_Group1, CV_Group2)  
    
    loop   
    exitwhen (CV_N == 0) 
        call GroupAddGroup(CV_Group2, CV_Group1)  
        set CV_UnitAmount = CountUnitsInGroup(CV_Group1) 
     
        loop 
        exitwhen (CV_UnitAmount == 0) 
            set CV_First = FirstOfGroup(CV_Group1) 
            set CV_FirstLocation = GetUnitLoc(CV_First) 
            set CV_TestDistance = DistanceBetweenPoints(CV_Point, CV_FirstLocation)  
            if (CV_TestDistance < CV_Distance) then   
                set CV_Closest = CV_First 
                set CV_Distance = CV_TestDistance 
            endif  
            set CV_UnitAmount = CV_UnitAmount - 1 
            call GroupRemoveUnit(CV_Group1, CV_First) 
        endloop
        
        call GroupAddUnit(CV_Group3, CV_Closest)
        call GroupRemoveUnit(CV_Group2, CV_Closest) 
        set CV_Distance = CV_Radius
        set CV_N = CV_N - 1  
    endloop 

    call RemoveLocation(CV_Point)
    call RemoveLocation(CV_FirstLocation)
    call DestroyGroup(CV_Group1)
    call DestroyGroup(CV_Group2)
    set CV_FirstLocation = null 
    set CV_Point = null
    set CV_Group1 = null
    set CV_Group2 = null
    set CV_First = null
    set CV_Closest = null
    
    return CV_Group3
endfunction 

Comments

3
this would be even better improvement. Vexorian himself proved that pow ( ... , 2 ) is slow so replace those lines with.



Pow((CV_X1 - CV_X2), 2) + Pow((CV_Y1 - CV_Y2), 2)


local real cv_x = CV_X1 - CV_X2
local real cv_y = CV_Y1 - CV_Y2
local real xx = cv_x * cv_x
local real yy = cv_y * cv_y
Replace DistanceBetweenPoints(CV_Point, CV_FirstLocation)

with (Pow(GetLocationX(CV_Point)-GetLocationX(CV_FirstLocation),2)+Pow(GetLocationY(CV_Point)-GetLocationY(CV_FirstLocation),2) )

also CV_Distance = CV_Radius*CV_Radius


Will be a huge performance improvement.

Also Try using coordinates instead of locations
Yes I know, I know! The description and function variables are in total chaos. I made some mistakes when I uploaded it. Below you see the script fixed. Use this instead of the actually uploaded script. Will probably modify it in the future with X and Y coordiantes instead of locations as Vexorian suggested.

Fixed:
- Functions' taken and given variables.
- Now just set CV_Radius to the original vaule of the pick-radius.


function CF_GetNClosestUnits takes group CV_Group1, location CV_Point, real CV_Radius, integer CV_N returns group 
    local unit CV_First 
    local unit CV_Closest 
    local integer CV_UnitAmount 
    local real CV_Distance 
    local real CV_TestDistance 
    local location CV_FirstLocation 
    local group CV_Group2 
    local group CV_Group3 
    set CV_Distance = CV_Radius*CV_Radius 
    set CV_Group2 = CreateGroup()  
    set CV_Group3 = CreateGroup()  
    call GroupAddGroup(CV_Group1, CV_Group2)    
    
    loop  
    exitwhen (CV_N == 0)  
        call GroupAddGroup(CV_Group2, CV_Group1)  
        set CV_UnitAmount = CountUnitsInGroup(CV_Group1)    

        loop  
        exitwhen (CV_UnitAmount == 0)  
            set CV_First = FirstOfGroup(CV_Group1)  
            set CV_FirstLocation = GetUnitLoc(CV_First)  
            set CV_TestDistance = DistanceBetweenPoints(CV_Point, CV_FirstLocation)  
            if (CV_TestDistance < CV_Distance) then  
                set CV_Closest = CV_First 
                set CV_Distance = CV_TestDistance 
            endif  
            set CV_UnitAmount = CV_UnitAmount - 1 
            call GroupRemoveUnit(CV_Group1, CV_First)  
        endloop    
        
        call GroupAddUnit(CV_Group3, CV_Closest)  
        call GroupRemoveUnit(CV_Group2, CV_Closest)  
        set CV_Distance = CV_Radius*CV_Radius 
        set CV_N = CV_N - 1 
    endloop    
    
    call RemoveLocation(CV_Point)  
    call RemoveLocation(CV_FirstLocation)  
    call DestroyGroup(CV_Group1)  
    call DestroyGroup(CV_Group2)  
    set CV_FirstLocation = null  
    set CV_Point = null  
    set CV_Group1 = null  
    set CV_Group2 = null  
    set CV_First = null  
    set CV_Closest = null    
    
    return CV_Group3 
endfunction 



New version:
¯¯¯¯¯¯¯¯¯¯¯¯
Changes:
- Replaced all use of locations with X Y coordinates instead. Know that it takes X and Y coordinates now more instead of a location.
- Replaced the distance forumal with Vexorian's one to prevent use of square root.
- Removed the use of Pow.

Thanks to Vexorian and Nantuko_Husk.


function CF_GetNClosestUnits takes group Group1, real X1, real Y1, real Radius, integer N returns group 
    local unit First  
    local unit Closest 
    local integer UnitAmount 
    local real Distance 
    local real TestDistance 
    local real X2
    local real Y2
    local real X3
    local real Y3
    local group Group2 
    local group Group3
    set Distance = Radius * Radius  
    set Group2 = CreateGroup()
    set Group3 = CreateGroup()
    call GroupAddGroup(Group1, Group2)  
    
    loop   
    exitwhen (N == 0) 
        call GroupAddGroup(Group2, Group1)  
        set UnitAmount = CountUnitsInGroup(Group1) 
     
        loop 
        exitwhen (UnitAmount == 0) 
            set First = FirstOfGroup(Group1) 
            set X2 = GetUnitX(CV_First)
            set Y2 = GetUnitY(CV_First)
            set X3 = X1 - X2
            set Y3 = Y1 - Y2
            set TestDistance = X3 * X3 + Y3 * Y3  
            if (TestDistance < Distance) then   
                set Closest = First 
                set Distanace = TestDistance 
            endif  
            set UnitAmount = UnitAmount - 1 
            call GroupRemoveUnit(Group1, First) 
        endloop
        
        call GroupAddUnit(Group3, Closest)
        call GroupRemoveUnit(Group2, Closest) 
        set CV_Distance = Radius * Radius
        set N = N - 1  
    endloop 

    call DestroyGroup(Group1)
    call DestroyGroup(Group2)
    set Group1 = null
    set Group2 = null
    set First = null
    set Closest = null
    
    return Group3
endfunction