HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Picking Closest Unit to another Unit >>Very Hard<<

02-23-2004, 11:21 PM#1
Shimrra
How do you choose the unit closest to another?
02-24-2004, 01:10 AM#2
linkmaster23
Thanks for posting this for me... I didn't think it was THAT hard...
02-24-2004, 01:25 AM#3
Hunter0000
Quote:
Originally posted by linkmaster23
Thanks for posting this for me... I didn't think it was THAT hard...


Whats with you latly linkmaster!? ur spamming and crap all the time for the past few days... lol

as for the question, I would run a loop that keeps searching in differnt ranges until it finds a unit.
02-24-2004, 01:32 AM#4
Grater
With difficulty. There is a function in the JASS vault, but it leaks memory... I think I'll write a new function to detect the closest unit.
02-24-2004, 01:34 AM#5
Hunter0000
is my method really leaky or sumthing? I would expect it to lag though..., well it depends on the range intervals.
02-24-2004, 01:44 AM#6
linkmaster23
In GUI please... lol.
02-24-2004, 01:45 AM#7
Shimrra
If the only way is in Jass, then it might be wise to accept it...
02-24-2004, 01:58 AM#8
linkmaster23
I will go as far as Custom Text Action...
02-24-2004, 02:18 AM#9
Grater
Okay, this sort of problem just cries out for JASS, so I wrote a function. I'll explain exactly how to use it too.

Code:
function FindClosestUnit takes group g, real x, real y, boolean destroyGroup returns unit
    local real dx
    local real dy
    local group tempGroup
    local real maxDist=999999.0
    local real dist
    local unit u = null
    local unit closest = null

    if (destroyGroup == false) then
        set tempGroup = CreateGroup()
        call GroupAddGroup(g, tempGroup)
    else
        set tempGroup = g 
    endif
    
    loop
        set u = FirstOfGroup(tempGroup)
        call GroupRemoveUnit(tempGroup, u)
        exitwhen (u == null)
        set dx = GetUnitX(u) - x
        set dy = GetUnitY(u) - y
        set dist = SquareRoot(dx*dx+dy*dy)
        if (dist < maxDist) then
            set closest = u
            set maxDist = dist
        endif
    endloop
    call DestroyGroup(tempGroup)
    return closest
endfunction

function FindClosestUnitLoc takes group g, location loc, boolean destroyGroup returns unit
    return FindClosestUnit(g,GetLocationX(loc),GetLocationY(loc),destroyGroup)
endfunction

Copy-paste the above code into the map header, that is, click on the map name at the top of the list of triggers, you'll get a nice empty box titled "Custom Script Code", paste it there.

Now for the trigger itself you should create some global variables, I'm going to assume:
TempGroup (Unit Group)
TempLoc (Point)
TempUnit (Unit)


Now to find the unit in TempGroup closest to TempLoc and put it in TempUnit simply use the following Custom Script action:
Code:
Custom script:   set udg_TempUnit = FindClosestUnitLoc(udg_TempGroup,udg_TempLoc,false)

For example, if you wanted to do a corpse explosion ability:
Code:
Actions
    Set TempLoc = (Target point of ability being cast)
    Set TempGroup = (Units within 600.00 of TempLoc matching (((Matching unit) is alive) Equal to False))
    Custom script:   set udg_TempUnit = FindClosestUnitLoc(udg_TempGroup,udg_TempLoc,true)
    Unit - Replace TempUnit with a Goblin Sapper using The new unit's default life and mana
    Unit - Order (Last replaced unit) to Neutral - Kaboom! (Position of (Last replaced unit))

Edit: For interested users the "true/false" part means whether you want the unit group to be destroyed true = do destroy, false = don't destroy I want to use it again!.
If in doubt, use "false"

Edit2: Fixed the code to make the unit group destroying behaivour work as stated.
02-24-2004, 02:40 AM#10
Shimrra
Could set it so it picks the 5 closest units within 1500 of the main point and then runs another trigger? The other trigger is so the rest of the spell can be done in GUI. Thanks for your help.
02-24-2004, 03:31 AM#11
Grater
First I want to mention I made a couple of bug fixes to the code I posted above.

To get the 5 closest, basically just keep rerun the function 5 times, removing the closest unit from the main group each time. The trigger below will use my function to put the 5 closest units in the variable ClosestUnits
Code:
Actions
    Set TempLoc = (Your Point Here)
    Set TempGroup = (Units within 1500.00 of TempLoc Matching (Your condition here))
    Unit Group - Remove all units from ClosestUnits
    For each (Integer A) from 1 to 5, do (Actions)
        Loop - Actions
            Custom script:   set udg_TempUnit = FindClosestUnitLoc(udg_TempGroup,udg_TempLoc,false)
            Unit Group - Add TempUnit to ClosestUnits
            Unit Group - Remove TempUnit from TempGroup
    -------- Informative Debug Message --------
    Unit Group - Pick every unit in ClosestUnits and do (Actions)
        Loop - Actions
            Game - Display to (All players) the text: (String((Unit-type of (Picked unit))))
 
    -------- The 5 units are now in ClosestUnits --------
02-24-2004, 04:06 AM#12
Narwanza
Grater I understood your function up to this point.

Code:
        set dx = GetUnitX(u) - x
        set dy = GetUnitY(u) - y

What are the x and y? they aren't local reals declared by you, are they something that just exists in JASS?
02-24-2004, 04:18 AM#13
Grater
They are the parameters the function takes, you'll note there are actually two functions:
FindClosestUnit : that takes co-ordinates in the form of two reals
and
FindClosestUnitLoc which is a fluff function.

Some JASS users (including myself) prefer to not use location variables at all for many tasks, but it's inconvienent to use two reals in the GUI.
02-24-2004, 04:23 AM#14
Narwanza
I'm sorry for asking such a stupid question. I should have looked at parameters taken. I am going to go shoot myself now, j/k. I really do feel dumb, I know that stuff. Geez. *slaps himself*
02-24-2004, 11:34 AM#15
Alakafizz
It's not too hard to find just the closest unit in GUI. But if you ask me to find the 5 closets units one by one, i'd be pressed :)
Edit: Added support for finding the 5 closest units.


Here's to find just the closest:
Code:
Closest unit
    Events
    Conditions
    Actions
        Set [color=red]Caster[/color] = (Triggering unit)
        Set [color=red]ClosestUnit[/color] = (Random unit from (Units within 1500.00 of (Position of [color=red]Caster[/color]) matching ((Matching unit) Not equal to [color=red]Caster[/color])))
        Custom script:   set bj_wantDestroyGroup = true
        Unit Group - Pick every unit in (Units within 1500.00 of (Position of [color=red]Caster[/color]) matching ((Matching unit) Not equal to [color=red]Caster[/color])) and do (Actions)
            Loop - Actions
                If ((Distance between (Position of [color=red]Caster[/color]) and (Position of (Picked unit))) Less than (Distance between (Position of [color=red]Caster[/color]) and (Position of [color=red]ClosestUnit[/color]))) then do (Set [color=red]ClosestUnit[/color] = (Picked unit)) else do (Do nothing)
ClosestUnit contains the closest unit now.

5 closest units:
Code:
Closest 5 units
    Events
    Conditions
    Actions
        Unit Group - Remove all units from [color=red]AddingGroup[/color]
        Set [color=red]Caster[/color] = (Triggering unit)
        For each (Integer A) from 1 to 5, do (Actions)
            Loop - Actions
                Set [color=red]ClosestUnit[/color] = (Random unit from (Units within 1500.00 of (Position of [color=red]Caster[/color]) matching ((Matching unit) Not equal to [color=red]Caster[/color] and (((Matching unit) is in [color=red]AddingGroup[/color]) Equal to False))))
                Custom script:   set bj_wantDestroyGroup = true
                Unit Group - Pick every unit in (Units within 1500.00 of (Position of [color=red]Caster[/color]) matching ((Matching unit) Not equal to [color=red]Caster[/color] and (((Matching unit) is in [color=red]AddingGroup[/color]) Equal to False))) and do (Actions)
                    Loop - Actions
                        If ((Distance between (Position of [color=red]Caster[/color]) and (Position of (Picked unit))) Less than (Distance between (Position of [color=red]Caster[/color]) and (Position of [color=red]ClosestUnit[/color]))) then do (Set [color=red]ClosestUnit[/color] = (Picked unit)) else do (Do nothing)
                Unit Group - Add [color=red]ClosestUnit[/color] to [color=red]AddingGroup[/color]
AddingGroup contains the 5 closest units now.