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

Get Closest Unit 3D v1.0.0

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: deepstrasz
A system that will find the current closest unit in a specific event in 3D way (flying height factor is calculated). The map also contains a very complete tutorials about the system and the process.

This system also helps you to understand how to use this system to make other abilities and systems, the current version only contains 1 example (simple movement/order group system). I will add more in the further version.

The map also contains my first 2 handmade functions (phytagoras & convert "smart" order) and I feel proud of it B|

Anyway, enjoy :)


  • Example 1
    • Events
      • Player - Player 1 (Red) Selects a unit
    • Conditions
    • Actions
      • -------- store clicked unit into a variable --------
      • Set EX_TriggU = (Triggering unit)
      • -------- set the clicked unit current position --------
      • Set EX_Point[1] = (Position of EX_TriggU)
      • -------- group of unit that will be processed --------
      • Set EX_Group = (Units in (Entire map))
      • -------- let's guess the initial closest unit for the unit group above --------
      • Set EX_Result = (Random unit from EX_Group)
      • -------- let's process them all --------
      • Unit Group - Pick every unit in EX_Group and do (Actions)
        • Loop - Actions
          • -------- store unit that will be processed into a variable --------
          • Set EX_PickU = (Picked unit)
          • -------- if you delete this condition the result will be the clicked unit it self --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • EX_PickU Not equal to EX_TriggU
            • Then - Actions
              • -------- set the current position of processed unit --------
              • Set EX_Point[2] = (Position of EX_PickU)
              • -------- set the current position of current closest unit --------
              • Set EX_Point[3] = (Position of EX_Result)
              • -------- use phytagoras to calculate 3D distance from position of clicked unit to position of processed unit --------
              • Custom script: set udg_EX_Range[1] = Phytagoras(DistanceBetweenPoints(udg_EX_Point[1], udg_EX_Point[2]), GetUnitFlyHeight(udg_EX_PickU), GetUnitFlyHeight(udg_EX_TriggU))
              • -------- use phytagoras to calculate 3D distance from position of clicked unit to position of current closest unit --------
              • Custom script: set udg_EX_Range[2] = Phytagoras(DistanceBetweenPoints(udg_EX_Point[1], udg_EX_Point[3]), GetUnitFlyHeight(udg_EX_Result), GetUnitFlyHeight(udg_EX_TriggU))
              • -------- if range from clicked unit location to processed unit location is closer than range from clicked unit location to current closest unit location then we find a closer unit --------
              • -------- then set it into "Result" variable --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • EX_Range[1] Less than EX_Range[2]
                • Then - Actions
                  • Set EX_Result = EX_PickU
                • Else - Actions
              • -------- remove leaks --------
              • Custom script: call RemoveLocation(udg_EX_Point[2])
              • Custom script: call RemoveLocation(udg_EX_Point[3])
            • Else - Actions
      • -------- Additional information: All actions below triggered after pick unit action has been finished. I hope you understand more about unit pick and loop integer --------
      • -------- remove leaks --------
      • Custom script: call DestroyGroup(udg_EX_Group)
      • Custom script: call RemoveLocation(udg_EX_Point[1])
      • -------- create sfx for the closest unit --------
      • Special Effect - Create a special effect attached to the origin of EX_Result using Abilities\Spells\Orc\WarStomp\WarStompCaster.mdl
      • Special Effect - Destroy (Last created special effect)
  • Example 2
    • Events
    • Conditions
    • Actions
      • -------- first define the current location of main unit --------
      • Set EX_Point[1] = (Position of EX_TriggU)
      • -------- define all units that will be processed. InOrderGroup is all units that have counted as one of the closest unit. so they will not be re-counted --------
      • Set EX_Group = (Units in (Entire map) matching (((Matching unit) is in EX_InOrderGroup) Equal to False))
      • -------- initial expectation --------
      • Set EX_Result = (Random unit from EX_Group)
      • -------- current closest #rank. #1 is the closest of the closest :p --------
      • Set EX_InOrderInteger = (EX_InOrderInteger + 1)
      • -------- let's process them --------
      • Unit Group - Pick every unit in EX_Group and do (Actions)
        • Loop - Actions
          • -------- store unit that will be processed into a variable --------
          • Set EX_PickU = (Picked unit)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • EX_PickU Not equal to EX_TriggU
            • Then - Actions
              • -------- set current location of processed unit --------
              • Set EX_Point[2] = (Position of EX_PickU)
              • -------- set current location of current expected closest unit --------
              • Set EX_Point[3] = (Position of EX_Result)
              • -------- use phytagoras to calculate 3D distance from position of clicked unit to position of processed unit --------
              • Custom script: set udg_EX_Range[1] = Phytagoras(DistanceBetweenPoints(udg_EX_Point[1], udg_EX_Point[2]), GetUnitFlyHeight(udg_EX_PickU), GetUnitFlyHeight(udg_EX_TriggU))
              • -------- use phytagoras to calculate 3D distance from position of clicked unit to position of current closest unit --------
              • Custom script: set udg_EX_Range[2] = Phytagoras(DistanceBetweenPoints(udg_EX_Point[1], udg_EX_Point[3]), GetUnitFlyHeight(udg_EX_Result), GetUnitFlyHeight(udg_EX_TriggU))
              • -------- if range from clicked unit location to processed unit location is closer than range from clicked unit location to current closest unit location then we find a closer unit --------
              • -------- then set it into "Result" variable --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • EX_Range[1] Less than EX_Range[2]
                • Then - Actions
                  • Set EX_Result = EX_PickU
                • Else - Actions
              • -------- remove leaks --------
              • Custom script: call RemoveLocation(udg_EX_Point[2])
              • Custom script: call RemoveLocation(udg_EX_Point[3])
            • Else - Actions
      • -------- remove leaks --------
      • Custom script: call DestroyGroup(udg_EX_Group)
      • Custom script: call RemoveLocation(udg_EX_Point[1])
      • -------- then after we get the result, we will write them in an order --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • EX_InOrderInteger Less than or equal to EX_InOrderTotal
        • Then - Actions
          • -------- add the result unit into a unit group so they will not be recounted --------
          • Unit Group - Add EX_Result to EX_InOrderGroup
          • -------- you may read this variable as "Unit in [#rank] = result"\ --------
          • Set EX_InOrderUnit[EX_InOrderInteger] = EX_Result
          • -------- if we haven't get enough unit yet then repeat the process above --------
          • Trigger - Run (This trigger) (checking conditions)
        • Else - Actions
          • -------- after we get enough unit, clear the "counted" unit group --------
          • Unit Group - Remove all units from EX_InOrderGroup
          • -------- create floating text that show the unit's rank --------
          • For each (Integer A) from 1 to EX_InOrderTotal, do (Actions)
            • Loop - Actions
              • Floating Text - Create floating text that reads (String((Integer A))) above EX_InOrderUnit[(Integer A)] with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
              • Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
              • Floating Text - Change (Last created floating text): Disable permanence
              • Floating Text - Change the lifespan of (Last created floating text) to 5.00 seconds
              • Floating Text - Change the fading age of (Last created floating text) to 4.00 seconds

  • Select event
    • Events
      • Player - Player 1 (Red) Selects a unit
    • Conditions
    • Actions
      • For each (Integer A) from 1 to Order_MaxUnit, do (Actions)
        • Loop - Actions
          • Unit - Remove Selection Circle from Order_Unit[(Integer A)]
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Triggering unit) Equal to Order_Unit[1]
          • Order_Integer Equal to 0
        • Then - Actions
          • Set Order_Integer = 1
          • Set Order_MaxUnit = 20
          • Trigger - Run Create Group <gen> (checking conditions)
        • Else - Actions
          • Set Order_Integer = 0
          • Set Order_Unit[1] = (Triggering unit)
          • For each (Integer A) from 2 to Order_MaxUnit, do (Actions)
            • Loop - Actions
              • Set Order_Unit[(Integer A)] = No unit
          • Unit - Add Selection Circle to Order_Unit[1]
  • Create Group
    • Events
    • Conditions
    • Actions
      • Set EX_Point[1] = (Position of Order_Unit[1])
      • Set EX_Group = (Units in (Entire map) matching (((Matching unit) is in Order_Group) Equal to False))
      • Set EX_Result = (Random unit from EX_Group)
      • Set Order_Integer = (Order_Integer + 1)
      • Unit Group - Pick every unit in EX_Group and do (Actions)
        • Loop - Actions
          • Set EX_PickU = (Picked unit)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • EX_PickU Not equal to EX_TriggU
            • Then - Actions
              • Set EX_Point[2] = (Position of EX_PickU)
              • Set EX_Point[3] = (Position of EX_Result)
              • Custom script: set udg_EX_Range[1] = Phytagoras(DistanceBetweenPoints(udg_EX_Point[1], udg_EX_Point[2]), GetUnitFlyHeight(udg_EX_PickU), GetUnitFlyHeight(udg_EX_TriggU))
              • Custom script: set udg_EX_Range[2] = Phytagoras(DistanceBetweenPoints(udg_EX_Point[1], udg_EX_Point[3]), GetUnitFlyHeight(udg_EX_Result), GetUnitFlyHeight(udg_EX_TriggU))
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • EX_Range[1] Less than EX_Range[2]
                • Then - Actions
                  • Set EX_Result = EX_PickU
                • Else - Actions
              • Custom script: call RemoveLocation(udg_EX_Point[2])
              • Custom script: call RemoveLocation(udg_EX_Point[3])
            • Else - Actions
      • Custom script: call DestroyGroup(udg_EX_Group)
      • Custom script: call RemoveLocation(udg_EX_Point[1])
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Order_Integer Less than or equal to (Order_MaxUnit + 1)
        • Then - Actions
          • Unit Group - Add EX_Result to Order_Group
          • Set Order_Unit[Order_Integer] = EX_Result
          • Trigger - Run (This trigger) (checking conditions)
        • Else - Actions
          • Unit Group - Remove all units from Order_Group
          • For each (Integer A) from 2 to Order_Integer, do (Actions)
            • Loop - Actions
              • Unit - Add Selection Circle to Order_Unit[(Integer A)]
  • Order Event
    • Events
      • Unit - A unit Is issued an order targeting an object
      • Unit - A unit Is issued an order targeting a point
      • Unit - A unit Is issued an order with no target
    • Conditions
      • (Triggering unit) Equal to Order_Unit[1]
      • (Give Order <gen> is on) Equal to False
    • Actions
      • Set OrderString = (String((Issued order)))
      • Set OrderTarget = (Target unit of issued order)
      • Set OrderPlayer = (Owner of (Triggering unit))
      • Custom script: call RemoveLocation(udg_OrderPoint)
      • Set OrderPoint = (Target point of issued order)
      • Set OrderInteger = 0
      • Custom script: set udg_OrderString = Smart(udg_OrderString, udg_OrderTarget, udg_OrderPlayer)
      • Trigger - Turn on Give Order <gen>
  • Give Order
    • Events
      • Time - Every 0.01 seconds of game time
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • OrderInteger Less than or equal to Order_MaxUnit
        • Then - Actions
          • Set OrderInteger = (OrderInteger + 1)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • OrderString Equal to attack
            • Then - Actions
              • Unit - Order Order_Unit[OrderInteger] to Attack-Move To OrderPoint
              • Unit - Order Order_Unit[OrderInteger] to Attack OrderTarget
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • OrderString Equal to follow
            • Then - Actions
              • Unit - Order Order_Unit[OrderInteger] to Follow OrderTarget
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • OrderString Equal to move
            • Then - Actions
              • Unit - Order Order_Unit[OrderInteger] to Move To OrderPoint
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • OrderString Equal to patrol
            • Then - Actions
              • Unit - Order Order_Unit[OrderInteger] to Patrol To OrderPoint
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • OrderString Equal to stop
            • Then - Actions
              • Unit - Order Order_Unit[OrderInteger] to Stop
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • OrderString Equal to holdposition
            • Then - Actions
              • Unit - Order Order_Unit[OrderInteger] to Hold Position
            • Else - Actions
        • Else - Actions
          • Trigger - Turn off (This trigger)
Keywords:
get, closest, 3d, gui
Contents

Get Closest (Map)

Reviews
23:06, 26th Nov 2013 Maker: You should have a trigger that returns the closest unit and is stripped of anything extra
Status
Not open for further replies.

Moderator

M

Moderator

23:06, 26th Nov 2013
Maker: You should have a trigger that returns the closest unit
and is stripped of anything extra
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I won't, there is one submission rule that I can't fulfill. it's "make a nice thread". this still can be categorized as system I think.

the map is like hanky's dynamic indexing, it gives some example how to do a correct indexing. this system do the same, it gives some examples how to get closest unit(s)

I think I will just make a function that returns the closest unit so that user can easily calls it.. but I need to learn some first..
 
Last edited by a moderator:
Level 16
Joined
Jul 31, 2012
Messages
2,217
Oh :p
I'll help you if you want, i will look at the code and review it, the big review version xD (explanation: i sometimes review a big one if i have time and not lazy, the result is somehow the review of jakezinc, and sometimes i just don't feel like it and the result is, a small review :p)
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
For the group order part, I'm working on something.

Also, dunno about 3D, but in 2D I've made a quite simple function for getting closest unit.

JASS:
function ClosestUnitLoop takes nothing returns nothing
        local unit u = GetEnumUnit()
        local real x = GetUnitX(u) - ExtraX
        local real y = GetUnitY(u) - ExtraY
        local real r = SquareRoot(x * x + y * y)
        if r < ExtraReal then
            set ExtraReal = r
            set ExtraUnit = u
        endif
        set u = null
    endfunction
function GetClosestUnitOfGroup takes group g,real x,real y returns unit
        set ExtraReal = 100000
        set ExtraX = x
        set ExtraY = y
        call ForGroup(g, function ClosestUnitLoop)
        return ExtraUnit
endfunction
The function is a part of my ExtraFunctions library, which I haven't published though.
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
Xonok, you could ditch the Squareroot and you could keep everything in one function.
I don't understand the math behind this entirely, so the question is how to ditch squareroot.
Also, having a subfunction seemed quite necessary, as I would not be able to use ForGroup without it.
Although I'm now thinking it might actually be possible with a FirstOfGroup loop.
well, 3D or not, it's not important to make a movement group, and I think using 2D is the best..

When you are looking for the closest unit you are always dealing with a group. That's can't really be avoided imo, so not having one actually can hamper performance(perhaps you have tons of units that you don't want to count in?)
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
EDIT: Wow a lot of replies! This was for Xonok
JASS:
        local real z = GetUnitHeight(u)
        local real r = SquareRoot(x*x + y*y + z*z)
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
I don't understand the math behind this entirely, so the question is how to ditch squareroot.
SquareRoot(a) > SquareRoot(b) works the same as a > b since the values are always >= 0.

If SquareRoot(9) > SquareRoot(4) then... versus If 9 > 4... both the same.

Although I'm now thinking it might actually be possible with a FirstOfGroup loop.
Yeah, use First of group loop.
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
EDIT: Wow a lot of replies! This was for Xonok
JASS:
        local real z = GetUnitHeight(u)
        local real r = SquareRoot(x*x + y*y + z*z)

Hmm, seems so logical to me now. Yet, I wouldn't have figured this out on my own.

I'll add this as an option, possibly a separate function.

EDIT:
For the sake of simplicity I also added wrappers to take a unit instead of coordinates.
JASS:
    function ClosestUnitLoop takes nothing returns nothing
        local unit u = GetEnumUnit()
        local real x = GetUnitX(u) - ExtraX
        local real y = GetUnitY(u) - ExtraY
        local real r = SquareRoot(x * x + y * y)
        if r < ExtraReal then
            set ExtraReal = r
            set ExtraUnit = u
        endif
        set u = null
    endfunction
    
    function ClosestUnitLoopZ takes nothing returns nothing
        local unit u = GetEnumUnit()
        local real x = GetUnitX(u) - ExtraX
        local real y = GetUnitY(u) - ExtraY
        local real z
        local real r
        local location l = Location(x,y)
        set z = GetLocationZ(l)
        call RemoveLocation(l)
        set l = null
        set r = SquareRoot(x * x + y * y + z * z)
        if r < ExtraReal then
            set ExtraReal = r
            set ExtraUnit = u
        endif
        set u = null
    endfunction
    function GetClosestUnitOfGroup takes group g,real x,real y returns unit
        set ExtraReal = 100000
        set ExtraX = x
        set ExtraY = y
        call ForGroup(g, function ClosestUnitLoop)
        return ExtraUnit
    endfunction
    
    function GetClosestUnitOfGroupEx takes group g,unit u returns unit
        set ExtraReal = 100000
        set ExtraX = GetUnitX(u)
        set ExtraY = GetUnitY(u)
        call ForGroup(g, function ClosestUnitLoop)
        return ExtraUnit
    endfunction
    
    function GetClosestUnitOfGroupZ takes group g,real x,real y,real z returns unit
        set ExtraReal = 100000
        set ExtraX = x
        set ExtraY = y
        set ExtraZ = z
        call ForGroup(g, function ClosestUnitLoopZ)
        return ExtraUnit
    endfunction
    
    function GetClosestUnitOfGroupZEx takes group g,unit u returns unit
        local location l
        set ExtraReal = 100000
        set ExtraX = GetUnitX(u)
        set ExtraY = GetUnitY(u)
        set l = Location(ExtraX,ExtraY)
        set ExtraZ = GetLocationZ(l)
        call RemoveLocation(l)
        set l = null
        call ForGroup(g, function ClosestUnitLoopZ)
        return ExtraUnit
    endfunction
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
I actually used that when Flame Phoenix (the guy who wrote a tutorial here) asked some help to make his 3d Chain Missile System.

Use the firstOfGroup loop if you will discard the group containing the unit list, but if you don't want to discard the group, use ForGroup instead, or better, make 2 versions.

@ Thread Starter: Sorry for hijacking your thread dude hehehe, gonna look at the map too.
 
Level 8
Joined
Nov 20, 2011
Messages
202
So this resource is realy bad, first of all this is fake 3D or how you would explain this:

Fake3d.png


The trigger/function is horrible slow due to bad coding and writing in GUI.

Overall 1/5, because this is not real 3D and for 2D the native GroupEnumUnitsInGroup() is around 100 times faster. And even a real 3D function, which is much harder to write could be much more efficient.
 
Status
Not open for further replies.
Top