• 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.

Pick Locusts in range

Status
Not open for further replies.
LITHUFFIN had found his resource was allowing his dummy units to be picked. I was highly skeptical of this, until I did a bunch of trial and error tests until I could find the cause of the problem:

His dummy unit could not be moved with SetUnitX and Y, but when I tried moving it like that it could not be picked by an InRange filter. SetUnitPosition moved the dummy but allowed it to be picked.

The discovery: In Object Editor, the unit had 0 default movement speed. Once I set that back to its default value, the unit could be moved with SetUnitX/Y AND no longer showed up in the group-filter checks!

So, to have a dummy unit which can be picked by GroupEnumUnitsInRange, its Object Editor movement speed must be set to 0.00 AND its movement must be done with SetUnitPosition.

Make sure any dummy unit is paused once created so that it doesn't spam "stop" order when you use SetUnitPosition.

TL;DR: Picking all dummy unit missiles in range is now possible!
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Using KillUnit makes a unit with locust appear in the GroupEnumUnitsInRange filter aswell. :p.

Good find, yet I'm not sure how we can use it. For example in Missile it would be great for
an onCollideWithMissile method, but on the other side dummies also appear in
the normal onCollide method and must be filtered out there.
 
If you use KillUnit, then won't it disappear since dummies are set to can't raise / does not decay?

Also, if Flux's proposal is correct, then maybe it can be temporarily enabled/disabled for missiles so you can set movement speed of all dummies to 0, check them for collision, set them back to > 0. This is O(n * 2) but it is much better than O(n ** n). Since the O(n * 2) is done only once per missile timeout (before and after movement and event calculations) it would be cheap.
 
SOLUTION FOUND! Removing the 'Amov' ability from the Locust unit will make it behave as if its default movement was 0. So the dummy unit can be picked in a unit group while having the same unit type as the ones who can't.

EDIT: Finally got to benchmarking SetUnitPosition against SetUnitX/Y. Unfortunately, even with paused units, SetUnitPosition is just about 6 times SLOWER than SetUnitX/Y. Removing the 'Amov' ability has no effect on the FPS. Turning off unit collision also has no effect since the dummy is already a locust.

It should be noted the best comparison is going to involve collision checks. GroupEnumUnitsInRange VS looping through every dummy unit. Also, SetUnitPosition will not need the "is unit in world bounds" check, so that's another thing which will be made more simple.
 
Last edited:
I gotta say 6 times slower is not that bad.

Considering all the overhead missile systems add (periodic timers, trigger evaluations, etc.), it shouldn't even matter that much in any real world application.
If I had to guess, I'd say in total it will only reduce the number of missiles you can have before the game performance takes a hit by the factor of 2, especially since GroupEnumUnitsInRange is super taxing anyway (but probably still better than any manual implementation of missile collision).

@BPower:
Btw, didn't you want to add the rectangle check (for fast moving missiles) to your missile system anyway?
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I want to express some doubts about an onMissile detection using this trick.

1.) How should I know that the unit enumerated is actually a Missile and not an ordinary dummy of another system?
I have to reference each unit ( handle id, index ) and check if it is really a Missile.

2.) Dummies start to appear in every unit enumeration. Worst case in a custom spell,
which runs independant from your missile library. We don't know what threads kick off
from onMissile, as you can start effects from inside the onMissile function during the loop.

3.) You have to add/readd amov frequently.

I can think of two possiblities for an onMissile evaluation.

A) Looping over a list of all missiles to check which is closest with the help of Pythagoras. God bless him.
Eventually there is a clever algorithm so missile B doesn't have to check missile A if A already check B.

B) Tracking a missiles tile position after moving it, using Tile Definition and store it in a table on this tile id.
Check surrounding tiles depending on a missiles position x/y and collision size.

I think solution A will turn out to be faster, because in most maps the total amount of
simultaneous flying missiles does not exceed 150.

You may think it's more instances, but it's not. Even when your spell fires ~75 missiles.
They are not fired at once and the first 20 are already recycled before the last 20 fire off.
 
Last edited:
I want to express some doubts about an onMissile detection using this trick.

1.) How should I know that the unit enumerated is actually a Missile and not an ordinary dummy of another system?
I have to reference each unit ( handle id, index ) and check if it is really a Missile.
If you don't want to use a simple lookup table, you can always just add a passive identifier ability to each missile created by the system.
But I recommend the lookup table, simply because it doesn't require object data.

2.) Dummies start to appear in every unit enumeration. Worst case in a custom spell,
which runs independant from your missile library. We don't know what threads kick off
from onMissile, as you can start effects from inside the onMissile function during the loop.
As I said, you probably want to have a lookup table to rule other dummies out.

3.) You have to add/readd amov frequently.
Not a big deal imho. Adding/removing abilities is fast.

I can think of two possiblities for an onMissile evaluation.

A) Looping over a list of all missiles to check which is in close with the help of Pythagoras. God bless him.
Eventually there is a clever algorithm so missile B doesn't have to check missile A if A already check B.
This is actually slower than enumerating units in range, as this is O(n²) complexity, whereas GroupEnumUnitsInRange uses a boolean tree internally for O(log n) [which results in O(log(n) * n) total as soon as we do that for all missiles].

You could optimize it further by having the missile stack ordered by movement direction and breaking each missile loop early if the missiles are pointing away from each other. Use insert-sort for that, as missiles are unlikely to do sudden direction changes. Now O(n²) would only be the worst-case scenario in which all missiles are pointing towards a common center.

B) Tracking a missiles tile position after moving it, using Tile Definition and store it in a table on this tile id.
Check surrounding tiles depending on a missiles position x/y and collision size.
This is basicly what GroupEnumUnitsInRange already does, except that the native will obviously enumerate all units instead of missiles only. I doubt you would be faster than that.

Btw, there is an option C) you haven't thought about:
Assuming all missiles are linear moving projectiles with constant speed, you can predict any collision with existing missiles via y = mx + n on the creation of the missile. So no periodic checks. I'm saying that because 90% of every use case of missile systems are probably linear moving missiles. And those that aren't can be treated seperately by using your O(n²) list approach.


I think solution A will turn out to be faster, because in most maps the total amount of
simultaneous flying missiles does not exceed 150.
If you really care for speed, you should implement both and toggle between them based on benchmarks.
But let's be honest here: it's not worth the trouble and you know that. Just use GroupEnumUnitsInRange and a lookup table and be done with it.
In the end, every extra JASS-level overhead probably costs you more performance than it saves over a native-level function call.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Thanks for the constructive feedback. Finally I will try to chose not the fastest, but
the solution which has the lowest potential to create bugs.
I also have to run my own test with SetUnitPosition and GroupEnumUnits to fully understand
how it working, how to disable/enable that feature.

At the moment I'm optimizing widget collisions ( items, destructables ),
once it is done I will make the rectangle collision, then comes onMissile.

Towards widget collision. Do you mind if I remove the member missile.unitGroup
and replace it with an hashtable child key ( missile ) / Table instance?

Units are the only widget type which have a group handle, the others must be
tracked in a Table. It would look more clean to also track units in that Table instance.

Equal to the unitGroup, you can either remove a single handle from the memory of hit widgets or flush the entire child key.
On destroy the child key ( missile ) is flushed and all saved handles reference are released.
 
Thanks for the constructive feedback. Finally I will try to chose not the fastest, but
the solution which has the lowest potential to create bugs.
I also have to run my own test with SetUnitPosition and GroupEnumUnits to fully understand
how it working, how to disable/enable that feature.

At the moment I'm optimizing widget collisions ( items, destructables ),
once it is done I will make the rectangle collision, then comes onMissile.

Towards widget collision. Do you mind if I remove the member missile.unitGroup
and replace it with an hashtable child key ( missile ) / Table instance?

Units are the only widget type which have a group handle, the others must be
tracked in a Table. It would look more clean to also track units in that Table instance.

Equal to the unitGroup, you can either remove a single handle from the memory of hit widgets or flush the entire child key.
On destroy the child key ( missile ) is flushed and all saved handles reference are released.
I never used the group that is member to missile, tbh. I didn't even know that was a thing until now... XD
 
After some testing, I can confirm you can't just add 'Amov' back to the unit. It doesn't restore the functionality. You can do it using a transform ability to shift it between unit types to restore the movement, if that's the goal.

What I recommend doing is adding a spell-immunity ability to it so most spells will not consider it. In addition, the filters could be edited to ensure they are not picking the dummy unit type. This would be easy to modify in Spell System, but harder for maps fooded with in-range checkers of other kinds.
 
Status
Not open for further replies.
Top