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

Collision in range

Status
Not open for further replies.
Level 12
Joined
May 22, 2015
Messages
1,051
In my map, there are bosses that are quite large. I have one main problem and a couple questions regarding unit collision size:

The main problem is that you have to put the spells deep inside the large bosses to hit them (at least for spells that are triggered). It's unintuitive because it is hard to tell if the spells will hit sometimes and it just looks weird when the spell animation touches the boss but doesn't affect them.

1) Is this the case for regular spells like flame strike? As in, does flame strike need to touch a unit's actual point or just a point that their collision detection covers? How about auras?

2) The values for unit collision are rounded to the nearest [something], right? (or maybe rounded down to the next value) Is it powers of 2 or something else? I think some numbers are: 8, 16, 32. I don't know much other than that.

3) Is there a good way to detect when units should get hit? I have 2 main ideas:

- Store all unit collision sizes with their unit types in a hashtable, then update my spells to check a larger radius (larger radius will be spell radius + max collision radius in my map). Then units will be tested to see if they are in range if they were their collision radius closer to the center.

- There is a limited number of big units in my map (bosses and fairly infrequent [maybe about 2 or 3 per minute] mini-boss units). I could store them in a unit group and then update my spells to check this unit group as well as their default radius. It would be less accurate with small units, but that isn't too important since the difference is very small. I could store the big units data in a hashtable or I could just use some hardcoded value since their sizes are kind of similar - it would probably look okay in-game to do a rough estimate.
 
Level 12
Joined
May 22, 2015
Messages
1,051
The key is to combine normal enumeration with an IsUnitInRange check. It automaticly accounts for the collision size of units.

I am using first of group loops with JASS with the function GroupEnumUnitsInRange

Are they coded differently? I'll probably test some things out and see what happens.

It seems like that function needs to compare to a unit. I figure I can just recycle a dummy for the whole game, but it seems unnecessary. I saw a function: IsUnitInRangeXY though. Will that work as well?
 
I am using first of group loops with JASS with the function GroupEnumUnitsInRange

Are they coded differently? I'll probably test some things out and see what happens.

It seems like that function needs to compare to a unit. I figure I can just recycle a dummy for the whole game, but it seems unnecessary. I saw a function: IsUnitInRangeXY though. Will that work as well?
Yes, this one works too.

The difference between GroupEnumUnitsInRange and IsUnitInRangeXY is, that the former will only enumerate units whose coordinate center is inside the radius. The IsUnitInRange natives will already return true if only a part of the unit is inside the radius.


So a simple solution is to use enumeration, add a global constant radius to it (basicly the maximum collision size of any unit in your map), then after enumerating remove those units that fail the IsUnitInRange check.


JASS:
scope EnumUnitsWithCollision

globals
    private constant real MAX_COLLISION = 128

    private real tempX = 0.
    private real tempY = 0.
    private real tempRange = 0.
endglobals

private function FilterInRange takes nothing returns boolean
    return IsUnitInRangeXY(GetFilterUnit(), tempX, tempY, tempRange)
endfunction

function GroupEnumUnitsWithCollision takes group g, real X, real Y, real radius returns group
    set tempX = X
    set tempY = Y
    set tempRange = radius
    call GroupEnumUnitsInRange(g, X, Y, radius+MAX_COLLISION, Filter(function FilterInRange))
    return g
endfunction

endscope
 
Level 12
Joined
May 22, 2015
Messages
1,051
How big is the collision size of the largest unit in your map?

I'll have to check but I think it is at least 64 or something like that. I don't know if I have bigger ones, but since the big ones are prime targets, it is frustrating to miss spells when you are trying to maximise the use of the AOE of them. I may not apply this logic to all abilities, but at least the player abilities that could affect the bosses should.

And thanks, Zwiebelchen! That is exactly what I needed for this. +rep
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
A slight modification on Zwiebelchen's script:
JASS:
function GroupAddUnitsInRangeWithCollision takes group whichGroup, real x, real y, real radius, real bounds returns group
    local unit FoG
    local group g = CreateGroup()
    call GroupEnumUnitsInRange(g, x, y, radius+bounds, null)
    
    loop
        set FoG = FirstOfGroup(g)
        exitwhen FoG == null
        call GroupRemoveUnit(g, FoG)
        
        if IsUnitInRangeXY(FoG, x, y, radius)
            call GroupAddUnit(whichGroup, FoG)
        endif
    endloop
    
    call DestroyGroup(g)
    set g = null
    return whichGroup
endfunction

(Be aware that this function adds the units to the group so any units that were already in it will not be removed first. With GroupEnumUnits, it does clear the group beforehand... kinda.)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Wietlol, I'd like to see an example of GroupEnum... not clearing the group first. It always removes old units when I've done it and when every other user I know has done it.

64.00 is the max normal unit collision, though if you want to include structures they go up to 197.00. The InRange check inside Spell System handles collision size in its InRange check, and it is quite accurate in hitting units compared to not using it.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
I dont use GroupEnum on "whichGroup" (the group that gets returned).
I add the units to "whichGroup". (I thought you would understand how that works.)

I have a question on IsUnitInRangeXY() though...
Does it include scaling factors?
Does it uses rounded collision or the actual OE value?

I can implement scaling factors and actual OE values if that is necessary.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
It uses the Object Editor raw collision size. There is no GetUnitScale check internally with IsUnitInRange, nor is there a native for it. I wouldn't bother unless the user is keeping track of bloated unit models themselves. But then there's the problem of shrunken unit scales which couldn't be adjusted with IsUnitInRange.

Edit: in regards to clearing groups, you wrote:

>> With GroupEnumUnits, it does clear the group beforehand... kinda.

That's what I was challenging you on. It does clear the group in every scenario; there is no "kinda".
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Yea but if someone comes and says that it doesnt really "clear" the group but rather overwrites some stuff or does something else to it, for that is was "kinda" because I dont know how it works, all I know is that units that already were there before the enum and not added by the enum will not be in the group then.

I can read the value from the OE, and I can sort of keep track of it.
IcemanBo wrote a GetUnitScale snippet which is pretty much usefull for GUI.
I on the other hand index all units using a Unit struct on which I have functions to scale and the values will be stored.
Then I can simply load variables from the struct to tell the scales of both X and Y. (Z is irrelevant to this situation.)
 
Level 12
Joined
May 22, 2015
Messages
1,051
I think what has been presented (the JASS code and the answers to my other questions) are all I need. I am not exactly sure about this scale thing being mentioned. Units don't change size in my map (at least not yet, but I was under the impression you can't change collision size, anyway), and if I add them, they are most likely only going to be used for animations - like the energy ball for Invoker's EMP in Dota.

I didn't know the group enum thing resets the group, but it doesn't really matter with first of group loops (they normally just remove all the units themselves). I'd have different problems if there was ever a case where the same group was being used in a nested loop - it would most likely cause bugs. It could be a shortcut in some really strange case, I guess.
 
Status
Not open for further replies.
Top