• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[TRIGGER] Unit Group problem

Status
Not open for further replies.
Level 18
Joined
Oct 18, 2007
Messages
930
Ok, how do i make a unit group that picks only, lets say the 5 closest targets?

Code:
[COLOR="Orange"]Edit:[/COLOR] [COLOR="Blue"]Fixed the problem, check out the map. [/COLOR]
[url=http://www.hiveworkshop.com/resources_new/spells/1562]Warcraft III Spells - Detect the closest units[/url]
 
Last edited:
Level 3
Joined
Jun 25, 2008
Messages
65
Maybe like this:
Set X = 0
Pick Every unit in 500 range of XXX
Set X = X + 1
if X <= 5
Add (Picked unit) to YourGroup // Variable of Type: Unit Group

Then you have your 5 units in "YourGroup"
As I think the Command "Pick Every Unit in X range" starts with the closest ones. But im not sure
And im not sure if this will work..^^
 
Level 9
Joined
Jun 26, 2007
Messages
659
the trigger probably pick and check the units in their order in the "all-units" array of the game
that was obvious to me that a "pick" feature doesn't sort its elements : why the game would perform a process wich cost cpu time if this process is almost never usefull ?

you've got to sort the group by yourself, that's the only way i think because blizzard hadn't made a "sort units by distance to a point" feature
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
Making loops in GUI:

We'll need: a global integer variable. I'll call it
Integer_C

Making the loop:
  • For each Integer_C from 1 to 3 do (multiple actions)
    • Loop - actions
      • If (all conditions are true) then do (then actions) else do (else actions)
        • If - Conditions
          • (Your conditions)
        • Then - Actions
          • Set Integer_C = 4
        • Else - Actions
          • (Your actions)
          • Set Integer_C = 1
Using this for loop, you create an infinite loop. The loop is only exited when the "If - conditions" are reached. In your example, you do:

  • For each Integer_C from 1 to 3 do (multiple actions)
    • Loop - actions
      • If (all conditions are true) then do (then actions) else do (else actions)
        • If - Conditions
          • (Number of units in closestunits) Less than (<) 5
        • Then - Actions
          • Set Integer_C = 4
        • Else - Actions
          • Find the closest unit and add it to the unit group ClosestUnits
          • Set Integer_C = 1
  • Unit Group - pick every unit in ClosestUnits and do actions
    • Do stuff with the 5 units
 
Level 9
Joined
Jun 26, 2007
Messages
659
but "Find the closest unit and add it to the unit group ClosestUnits" have two problems :
1) it's in fact "Find the closest unit wich is not already in the group"
2) it will require a second loop in the loop :/

in JASS, you can use "exitwhen" as many time as you wish and anywhere in the loop, that's usefull

anyway i'm still thinking that a loop's "pick in range" with an incremental range will take more time than any kind of sort algorythm, even than a simple bubble sort.
"pick in range" is a glutton action, each occurence of it create a new group, check all unit of the game, make two real substractions + two real square + one real addition + one real square root + a real comparison for each unit.
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
1) The answer to this was already given AFAIK, and I wrote what's supposed to be done in 1 line rather than a complete script.
2) And? You'll need a 2nd loop ANYWAY.

If you look at the GUI trigger, you could in fact also exit at any point in the loop. I know jass well enough to know it's better for loops, but I'm pointing out to all GUI users out there that you can have loops in GUI.

It is true that a "pick in range" with increasing ranges will probably take more time than a simple sort. Nevertheless, I'm pretty sure no square root is calculated, it's one of the basic principles of efficient programming to avoid square roots if possible.

If sqrt(x*x + y*y) <= your_range, you might as well calculate:
x*x + y*y <= your_range*your_range, which is an easier calculation.
On the other hand, "pick in range" is a native, which would probably mean it's been made as efficient as possible and will avoid using jass unit groups with unefficient FirstOfGroup and GroupRemoveUnit calls. Especially because it's probably constantly used for Acquisition ranges as well. For that reason, it might be more efficient to use "pick in range" actions.
 
Level 9
Joined
Jun 26, 2007
Messages
659
And? You'll need a 2nd loop ANYWAY.
Not sure, because we need only the 5 first elements (and we doesn't care about their order) and not the full sorted list, you don't need a sort but just a "little piece of sort"

If you look at the GUI trigger, you could in fact also exit at any point in the loop. I know jass well enough to know it's better for loops, but I'm pointing out to all GUI users out there that you can have loops in GUI.
You're talking to the guy who have done a full chess game without JASS -_-

it's one of the basic principles of efficient programming to avoid square roots if possible.
On the other hand, "pick in range" is a native, which would probably mean it's been made as efficient as possible
and one other principles of the efficient programming is to not assume what a function do without its code ;)

it might be more efficient to use "pick in range" actions.
Sorry, but i'm still thinking that a sort (wich could be partial) could be faster
for example, you could do it with a "5 instances only" bubble sort (because you don't care about the order of the remains of the list)

PS :
unefficient FirstOfGroup and GroupRemoveUnit calls
i souldn't assume but i'm pretty sure that FirstOfGroup just do a kind of "return group.headptr" and that calling GroupRemoveUnit on FirstOfGroup just do "if (group.headptr==input_unit) set group.headptr = group.headptr.next" and exit the function...
 
Level 11
Joined
Oct 20, 2007
Messages
342
Sry 1st, that i can't give u a beautiful trigger using {TRIGGER} code
my warcraft and editor was crash

a way... but maybe will cause lag... i can't try my self


"create variable "A" as integer"
"create variable "range_A" as real"
"create variable "Group" as unit group"


for loop integer A from 1 to 1
action:
set variable range_A = range_A + 20 (lower the number to make it more accurate)
set variable Group = every unit in range range_A matching condition "what u want"
If then else condition
condition - if number if unit in Group not equal or greater than 5 (prevent miss take)
true - set A to 0 (it will repeat if set to 0)
- clear all unit in Group
false - "what u want..."


for the memory leak... u should remove by urself.... i m not given...
it will make you blur...
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
You're talking to the guy who have done a full chess game without JASS -_-
I think I can say without doubt that I'm among the best GUI people around here.

and one other principles of the efficient programming is to not assume what a function do without its code ;)
you mean?

Sorry, but i'm still thinking that a sort (wich could be partial) could be faster
for example, you could do it with a "5 instances only" bubble sort (because you don't care about the order of the remains of the list)
Logically, I agree with you. But I'm not at all sure of that. You must remember that like every 0,01 seconds the AI loops through all units on the map and calculates the acquisition range for auto-attacks. That would also be some sort of "pick every unit in range and if unit is hostile, attack". I'd be surprised if they would make something as crucial as that not as efficient as possible.
A sort would still mean looping 5 times through all units. Picking every unit in range requires a 1 time loop through all units and checking the range between their positions, and loop again if required.

i souldn't assume but i'm pretty sure that FirstOfGroup just do a kind of "return group.headptr" and that calling GroupRemoveUnit on FirstOfGroup just do "if (group.headptr==input_unit) set group.headptr = group.headptr.next" and exit the function...
Sure it's something like that, but like I said, the AI would have to loop through all units every 0,01 seconds. It would be pretty stupid to use a group for that and 2 expensive function calls to get the first of group and remove it. It's 2 calls per unit on the map. I wouldn't be using a queue for something like that... Especially considering that acquisition range would work like this:

Pick every unit on the map and do:
Pick every unit but picked unit and check the range between the 2
if range < picked unit's acquisition range then
attack the unit

This would require 1 group to be created for each unit on the map... Sounds pretty heavy to me.

I'm pretty convinced pick every unit - native is faster than you'd expect. Well, the easiest way to find out is make a benchmark for the EnumUnitsInRange native and your own custom sort... Feel free to make it, I'm interested in it.
 
Level 9
Joined
Jun 26, 2007
Messages
659
you mean?
arf... that wasn't english ? :/
ok, i'll try to say it an other way...
you think that's "pick in range" do not use square root, but you're not sure
in coding, it's a bad thing to considerate as true thing you can't check
is it more understandable ?
(my apologies, i'm french, sorry, sorry, sorry, ...)

You must remember that like every 0,01 seconds the AI loops through all units on the map and calculates the acquisition range for auto-attacks.
maybe not, maybe they have found a way to optimize that by using a less obvious algorythm
for exemple, it's probably performed only for unit wich are of different teams
you've got 100 units and your opponent got 100 units too
a "pick in range" for each unit perform 40 000 checks
if you take in consideration that unit acquisition work only on enemy unit
it perform 10 000 checks
i think (but i'm still assuming, wich is bad) that the "acquisition range" do not use "pick in range" because it would do to many useless check

It would be pretty stupid to use a group for that and 2 expensive function calls to get the first of group and remove it
well, only map script are in jass, the game is probably in C++, wich mean that the game's dev have access to hundreds of options we don't have
and because of that, they probably have access to an array (and not a group) wich contain all units and on wich they just do "for(int i=0; i<nb_units; i++) all_units.anythingtheywant"

PS : if i make a sort, i won't make it in a group, i'll use an output array and use the group as a constant input ;)
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
you think that's "pick in range" do not use square root, but you're not sure
in coding, it's a bad thing to considerate as true thing you can't check
I can't know for sure indeed, but why would they use sqrt if they don't have to?

maybe not, maybe they have found a way to optimize that by using a less obvious algorythm
The algorithm would still involve a "pick every unit in range". All they could do is eliminate e.g. half the units on the map, but they would still have to pick every possible unit and calculate its distance to the "picked" unit... Which, when done with groups, would be inefficient.

well, only map script are in jass, the game is probably in C++, wich mean that the game's dev have access to hundreds of options we don't have
Ofcourse, which brings me to my point that the native "Pick every unit in range" is probably programmed efficiently in c, while a jass script that picks every unit in a group with FirstOfGroup calls and such would never be as efficiently.
Which is exactly why I'd like to see a benchmark between your jassed sort and the native "pick every unit" scripts.

You guys are really... Arrogant...
Why?

Also, you might want to remove the "banned" tag. It's not allowed to mimic the effects.
 
Level 9
Joined
Jun 26, 2007
Messages
659
I can't know for sure indeed, but why would they use sqrt if they don't have to?
because in pro world, you make sometime crappy code
in fact, optimizing code cost money, so optimization are done only when required
if something doesn't make your product lag, you don't spend time to optimize it

Which, when done with groups, would be inefficient
As i've already said : "group" is a JASS type, warcraft is not made in JASS
JASS is just a script language, warcraft is made in a compilable language

the native "Pick every unit in range" is probably programmed efficiently in c
not more and not less than FirstInGroup ;)
but with a "pick in range" called n times with k units, you can be sure that the game will perform n*k checks, by using FirstInGroup cleverly, you can do less than n*k checks

@Dinasti : i've done a partial sort function based on the bubble sort, but i hadn't tested it a lot,
it clean the input_group but it doesn't destroy it, it don't destroy the from_point too
JASS:
function GetClosestUnitToPointInGroup takes group input_group, location from_point, integer output_group_size returns group
  local integer input_group_size = CountUnitsInGroup(input_group)

  local unit array unit_array
  local real array range_array

  local group output_group

  local integer i
  local unit i_unit
  local real i_range
  local location i_loc

  local integer l

    set i = 0
    loop
      exitwhen (i >= input_group_size)
        set i_unit = FirstOfGroup(input_group)
        set unit_array[i] = i_unit
        set i_loc = GetUnitLoc(i_unit)
        set range_array[i] = DistanceBetweenPoints(from_point, i_loc)
        call RemoveLocation(i_loc)
        set i_loc = null
        call GroupRemoveUnit(input_group, i_unit)
      set i = i + 1
    endloop

    set l = 0
    loop
      exitwhen ((l == output_group_size) or (l >= input_group_size - 1))
        set i = 0
        loop
          exitwhen (i >= input_group_size - l - 1)
            if (range_array[i] < range_array[i+1]) then
              set i_unit = unit_array[i]
              set i_range = range_array[i]
              set unit_array[i] = unit_array[i+1]
              set range_array[i] = range_array[i+1]
              set unit_array[i+1] = i_unit
              set range_array[i+1] = i_range
            endif
          set i = i + 1
        endloop
      set l = l + 1
    endloop

    set output_group = CreateGroup()
    set i = 0
    loop
      exitwhen (i >= input_group_size)
        if (i >= input_group_size - output_group_size) then
          call GroupAddUnit(output_group, unit_array[i])
        endif
        set unit_array[i] = null
      set i = i + 1
    endloop

  return output_group
endfunction
 
Last edited:
Level 21
Joined
Aug 21, 2005
Messages
3,699
because in pro world, you make sometime crappy code
in fact, optimizing code cost money, so optimization are done only when required
if something doesn't make your product lag, you don't spend time to optimize it
There's a difference between "optimizations" and the very basics... It's common knowledge amongst programmers that to know exact distance you use sqrt, to know which one is the closest (or "relative" distance), you don't use sqrt.

As i've already said : "group" is a JASS type, warcraft is not made in JASS
JASS is just a script language, warcraft is made in a compilable language
Yes, which is yet again my point: why would they use groups for something that needs to be efficient? Which brings me to the point that "pick every unit" (made in c) can still be faster than a group sort (made in jass). Not because of the principles behind the concept, but because it's simply not written in jass.


not more and not less than FirstInGroup ;)
Which still requires at least 1 function call. Per unit, that is.

but with a "pick in range" called n times with k units, you can be sure that the game will perform n*k checks, by using FirstInGroup cleverly, you can do less than n*k checks
Now you're assuming stuff you can't see.




You're not nulling unit_array or i_loc.

And if you care so much about efficiency, don't use the CountUnitsInGroup bj.

JASS:
  local integer input_group_size = 0

  local unit array unit_array
  local real array range_array

  local group output_group

  local integer i
  local unit i_unit
  local real i_range
  local location i_loc

  local integer l

    loop
        set i_unit = FirstOfGroup(input_group)
        exitwhen (i_unit == null)
        set unit_array[input_group_size] = i_unit
        set i_loc = GetUnitLoc(i_unit)
        set range_array[input_group_size] = DistanceBetweenPoints(from_point, i_loc) // again, don't use a function, calculate it directly with x, y coordinates, preferably without squareroot.
        call RemoveLocation(i_loc)
        call GroupRemoveUnit(input_group, i_unit)
        set input_group_size = input_group_size + 1
    endloop

...
 
Level 9
Joined
Jun 26, 2007
Messages
659
There's a difference between "optimizations" and the very basics... It's common knowledge amongst programmers that to know exact distance you use sqrt, to know which one is the closest (or "relative" distance), you don't use sqrt.
when hundreds of persons work on the same code, their first aim is to make a code understandable, not an optimised one, I KNOW that you can avoid the sqrt if you just need to compare distances, but that's not enough to affirmate that it's done in "pick in range", you've got the source code of war3 ? no you don't

can still be faster than a group sort
but who the hell force you to sort a group ?
OF COURSE, i'll use an array and not a group, i say it since the beggening...

Which still requires at least 1 function call. Per unit, that is.
and why 1 fonction call per unit is heavier than 1 fonction call per range ?
and what allow you to affirm that "pick in range" is smaller than "first of group" ?

Now you're assuming stuff you can't see.
yes i am, but there is near 99% chance that i'm more qualified than you about that...

You're not nulling unit_array or i_loc.
useless

And if you care so much about efficiency, don't use the CountUnitsInGroup bj.
code have to be easy to read when possible, 1 CountUnitsInGroup is nearly nothing and it make the code more clear for the one who will try to understand it

EDIT
You guys got 12 posts in this single thread about one boring and confusing argument!
but you're still reading them ;p
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
when hundreds of persons work on the same code, their first aim is to make a code understandable, not an optimised one, I KNOW that you can avoid the sqrt if you just need to compare distances, but that's not enough to affirmate that it's done in "pick in range", you've got the source code of war3 ? no you don't
I don't think hundreds of people have developed jass. Even then, a quick calculation of x*x + y*y followed by a comment saying no sqrt is required won't take years of development, would you think? Besides, I'm pretty sure a lot work went into optimising warcraft, as with most videogames.


but who the hell force you to sort a group ?
OF COURSE, i'll use an array and not a group, i say it since the beggening...
Which still doesn't make my argument any less valid. In fact, you've said it yourself: jass can't be as efficient as c. I don't know which one would be faster, I don't even care, but you keep saying "nananana mine is faster nanana", while at the same time admitting the native itself can be faster because it wasn't written in jass. Make up your mind, ok?


and why 1 fonction call per unit is heavier than 1 fonction call per range ?
and what allow you to affirm that "pick in range" is smaller than "first of group" ?


yes i am, but there is near 99% chance that i'm more qualified than you about that...
right...

Not useless, always null variables and clean leaks. It has little to do with uselessness, more to do with discipline, something most of you seem to lack.

code have to be easy to read when possible, 1 CountUnitsInGroup is nearly nothing and it make the code more clear for the one who will try to understand it

Not at all. the fact the variablename is called input_group_size says enough. If you would still not know what that variable stands for, you can always add a comment that in the first loop you get the group size and put it into input_group_size. On top of that, your function is called GetClosestUnitToPointInGroup. Woever uses it should NOT care about the implementation, he should only know what input is required and what output is expected, and preferably in an efficient way.
 
Level 9
Joined
Jun 26, 2007
Messages
659
Besides, I'm pretty sure a lot work went into optimising warcraft, as with most videogames.
what's your personnal experience in videogame making to affirm that?
in most videogames, 90% of the execution time is taken by 10% of the code ;)

Which still doesn't make my argument any less valid. In fact, you've said it yourself: jass can't be as efficient as c. I don't know which one would be faster, I don't even care, but you keep saying "nananana mine is faster nanana", while at the same time admitting the native itself can be faster because it wasn't written in jass. Make up your mind, ok?
Ok, let's count :
with the "pick in range" method, you need to make 1 check per unit at least 64 times in the better case, and in the worst case... thousands of times... in c++
with the "array sort" i've done in JASS, i make 1 check per unit on the group only once, and then n checks (n is the number of wanted closest units) per unit, no matter their distance, in JASS...
are you sure yours is faster ?

Not useless, always null variables and clean leaks.
these variable are local,
no need to nullifie them at the beggining cause i know i will inialize them all
no need to nullifie them at the end, their memory place is released at the end of the function's execution
so, setting them to null would only make the cpu made totally useless instruction.

Not at all. the fact the variablename is called input_group_size says enough.
Maybe, that's a point of view, of course doing that, even once, is not free, but it's cheap, and i think it's cheap enough so i've made my choice.

Woever uses it should NOT care about the implementation
Maybe the author of this thread want to know what he put in is map, no?
Maybe he will try to understand how does it work, who knows?
 
Hear ye, Hear ye.
I got bored of reading this entire post, so I skipped to the end, and couldnt tell if it had been solved yet, so I decided to answer it. Here's the trigger, and if you want to make it have a range limit, adjust the set TempGroup line.
  • Closest 5 Units
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Get Closest 5 Units
    • Actions
      • Set TempLocA = (Position of (Triggering unit))
      • Set TempGroup = (Units in (Playable map area) matching ((((Matching unit) is alive) Equal to True) and (((Matching unit) belongs to an enemy of (Owner of (Triggering unit))) Equal to True)))
      • For each (Integer TempIntA) from 1 to 5, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Number of units in TempGroup) Greater than 0
            • Then - Actions
              • Set TempRealB = 9999999.00
              • Unit Group - Pick every unit in TempGroup and do (Actions)
                • Loop - Actions
                  • Set TempLocB = (Position of (Picked unit))
                  • Set TempRealA = (Distance between TempLocA and TempLocB)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • TempRealA Less than TempRealB
                    • Then - Actions
                      • Custom script: set udg_ClosestUnits[udg_TempIntA] = null
                      • Set ClosestUnits[TempIntA] = (Picked unit)
                      • Set TempRealB = TempRealA
                    • Else - Actions
              • Unit Group - Remove ClosestUnits[TempIntA] from TempGroup
            • Else - Actions
      • For each (Integer TempIntA) from 1 to 5, do (Actions)
        • Loop - Actions
          • Game - Display to (All players) the text: (Name of ClosestUnits[TempIntA])
          • Custom script: set udg_ClosestUnits[udg_TempIntA] = null
      • Custom script: call DestroyGroup(udg_TempGroup)
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
what's your personnal experience in videogame making to affirm that?
in most videogames, 90% of the execution time is taken by 10% of the code ;)
And? You're avoiding the question again. If there's one type of programs that require optimisations (and are in fact usually heavily optimised), it's videogames.


Ok, let's count :
with the "pick in range" method, you need to make 1 check per unit at least 64 times in the better case, and in the worst case... thousands of times... in c++
with the "array sort" i've done in JASS, i make 1 check per unit on the group only once, and then n checks (n is the number of wanted closest units) per unit, no matter their distance, in JASS...
are you sure yours is faster ?
Sigh.
I've agreed with you that your method "logically" would be the faster. I've said that since post 1. I have however said that practically, it isn't necessary so, because jass is jass and not c. I'm not sure "mine" (whatever "mine" means here) is faster, but I don't think neither are you or should you. And I won't say yours practically is faster until I see a benchmark.

these variable are local,
no need to nullifie them at the beggining cause i know i will inialize them all
Ofcourse. I wasn't talking about nullifying them at the start.
no need to nullifie them at the end, their memory place is released at the end of the function's execution
so, setting them to null would only make the cpu made totally useless instruction.
In theory, yes, but the reference counter is broken (bugged) so you DO need to null them in jass. Sad, but true.

Maybe the author of this thread want to know what he put in is map, no?
Maybe he will try to understand how does it work, who knows?

Well, "how it works" has been explained a few times now, so I guess he just wants to use it and doesn't care about the implementation.

What does your exactly do?
graystuff basically picks every unit on the map that isn't in the temporary group and chooses the closest unit, adds it to a temporary group, and repeats this 5 times...
 
Level 9
Joined
Jun 26, 2007
Messages
659
If there's one type of programs that require optimisations, it's videogames.
not anywhere
i'll explain you : you're agree that some features cost more cpu time than the others
optimizing all features need coding time, coding time need workers, workers cost money to compagny
so coders have to optimize the more used features until the fps is correct
after that, anything more would be a waste of money for the compagny
things like a square root in a distance calculation is nothing comparate to the graphical display for example

anyway, you wan't benchmark, well do them, i'm personally sure that jass is no slow enough to take more time doing 6 instructions than c++ take to doing hundreds of... (else my maps would lag with the numbers of jass function i use in them ^^')

In theory, yes, but the reference counter is broken (bugged) so you DO need to null them in jass. Sad, but true.
I didn't know, thanks to notice me about that
need to redesign the last loop...
EDIT : done
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
not anywhere
i'll explain you : you're agree that some features cost more cpu time than the others
optimizing all features need coding time, coding time need workers, workers cost money to compagny
so coders have to optimize the more used features until the fps is correct
after that, anything more would be a waste of money for the compagny
things like a square root in a distance calculation is nothing comparate to the graphical display for example

Removing an unneeded square root is a very basic optimization which doesn't require days of rethinking as with optimizing shaders. Especially because this is so generally known that I'd be surprised if they hadn't done that. Another thing is that shader optimizations are only good for GPU stressing, while the CPU is perhaps even more important on that area.
 
Status
Not open for further replies.
Top