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

How to tell if a unit is behind another?

Status
Not open for further replies.
Level 11
Joined
Jan 23, 2015
Messages
788
How do you detect when a unit is behind another, or in front of it, doesn't matter.. I've tried estimating using the facing angle of the unit and the angle between both units, but failed, the fact that degrees turn to 0.0 when they pass 360.0 is really making it complicated, and I don't want to waste my brain on that.. Do you want to share some of your methods in detecting this? Does it have something with Offset Region?
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
To calculate the difference between two angles, you must find the smaller one out of these two:
1: Var1 - Var2
2: 360 - Var1 + Var2

As long as Var1 is the smaller angle and Var2 is the bigger angle.
This works for angles between 0 and 360 degrees.
Ifyou have angles between -180 and 180 then you have to normalize those angles first.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,285
You pay attention to trigonometry and apply it. I have given full detailed solutions how to do this multiple times in the past and so have dozens of other people.

Use the properties of the consine (or sine) function to do the detection. The consine function in WC3 is defined for all real values. Since cosine output is a repeating wave with a period of 360 degrees (2 pi) it does not matter if an angle is defined as -20, 340 or 700 degrees as it will all return the same value. It is also symmetric around 0 degrees (maximum) and 180 degrees (minimum) so you can use a inequality test to define an angular range which it is true for.

As such finding if unit A is behind unit B...
Get angle from B to A.
Subtract that angle by facing of B (so -360, 0 and 360, etc angle is now the facing of the unit).
Put the angle through Cosine.
Compare if cosine result is less than Cosine(boundary angle).
Boundary angle in this case would be 180 - {degrees from back of unit}. Could also be the negative of cosine {degrees from back of unit}. This is due to the symmetry properties of cosine.

If this is faster or not is questionable, especially in C/C++ or Java programs. However chances are it is faster in JASS purely because of how slow JASS is. It also works for any angle no matter how many revolutions around the unit it represents. The test is also a single case rather than multiple.
 
JASS:
function IsUnitBehindUnit takes unit u2, unit u1 returns boolean
    local real face = GetUnitFacing(u1)
    local real rangle = bj_RADTODEG*Atan2(GetUnitY(u2)-GetUnitY(u1),GetUnitX(u2)-GetUnitX(u1))
    return not (RAbsBJ(face-rangle) < 90 or RAbsBJ(face-rangle-360) < 90)
endfunction

function IsPointInCone takes real x1, real y1, real x2, real y2, real f, real a returns boolean
     return Cos(Atan2(y2-y1,x2-x1)-f) > Cos(a)
endfunction
//x1,y1: coordinates of the cone origin
//x2,y2: coordinates tested against the cone
//f: orientation of the cone in radians
//a: halved opening angle of the cone in radians (90° cone: bj_DEGTORAD*45)
 
Level 11
Joined
Jan 23, 2015
Messages
788
I use another method, but a complicated one: I check if the facing angle of the 1st unit is nearly the same with the 2nd unit, I check if it's in a range of 60 degrees, so it works.. but, when the angle comes between 360 and 0, it gets complicated, cause I add 30 degrees on both sides (FacingAngle -30, and Facing Angle +30). So, first I need to check if I can add or subtract 30 without passing 360 before I set the angle, so, image a situation like:
Facing angle is = 25 degrees
Facing angle - 30 = -5 (there's space for only 25, the other 5 pass 0, so I need a different variable just for those degrees)
Facing angle + 30 = 55

So, if the Facing angle of Unit1, is somewhere nearby 25-0, 0-5 or 25-55 degrees of Facing angle of Unit2, then Unit1 is facing the back of Unit2 (that's actually what I need)..

I thought, there must be a simplier way, that's why I made this thread..
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
Use my method.
  • Untitled Trigger 001
    • Events
    • Conditions
    • Actions
      • Set TempUnit[0] = "A unit"
      • Set TempUnit[1] = "A unit"
      • Set TempLocation[0] = (Position of TempUnit[0])
      • Set TempLocation[1] = (Position of TempUnit[1])
      • Set TempReal[0] = (Facing of TempUnit[0])
      • Set TempReal[1] = (Angle from TempLocation[0] to TempLocation[1])
      • Custom script: call RemoveLocation(udg_TempLocation[0])
      • Custom script: call RemoveLocation(udg_TempLocation[1])
      • -------- - --------
      • Set TempReal[2] = (Max(TempReal[0], TempReal[1]))
      • Set TempReal[3] = (Min(TempReal[0], TempReal[1]))
      • Set TempReal[4] = (TempReal[2] - TempReal[3])
      • Set TempReal[5] = (360.00 - (TempReal[2] + TempReal[3]))
      • Set TempReal[6] = (Min(TempReal[4], TempReal[5]))
      • -------- - --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • TempReal[6] Greater than or equal to 90.00
        • Then - Actions
          • -------- TempUnit[1] is behind TempUnit[0] --------
        • Else - Actions
          • -------- TempUnit[1] is not behind TempUnit[0] --------
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,285
so, if I use cosine, and set an angle of 370 degrees, it automatically turns to 10 degrees right?
No but the output from cosine is the same at -350, 10, 370 etc. As you know from your high-school trigonometry class already.
cosine-graph.gif

The best part is that for all real inputs cosine is defined as that repeating output. As such angles that wrap around to >360 or <0 do not matter as you get the correct output all the time.

By applying an inequality (testing for a range) to the output of cosine you then test a range of input angles. The value used for the test is easily produced by a cosine of the maximum deviant angle you want to allow. The symmetry and repeating nature of cosine handles it all for you.

I thought, there must be a simplier way, that's why I made this thread..
There is. The one we are telling you about...

I created you a little demo map showing the approach I am referring to. In the demo map attacking a unit from directly behind +/- 30 degrees will deal 100 spell damage to the attacked unit. You will be able to try this for yourself by using the footman provided in the demo map which deal 0 damage to attack (and kill) the Blood Mages directly north of you.

This trigger is used for initialization.
  • Angle Test Example Init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set ANGBOU_ATE_30 = (Cos((180.00 - 30.00)))
And this does the actual testing for dealing damage.
  • Angle Test Example
    • Events
      • Unit - A unit Is attacked
    • Conditions
    • Actions
      • -------- setup register variables --------
      • Set RegUnitA = (Attacking unit)
      • Set RegUnitB = (Attacked unit)
      • Set RegLocA = (Position of RegUnitA)
      • Set RegLocB = (Position of RegUnitB)
      • -------- perform test --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Cos(((Angle from RegLocB to RegLocA) - (Facing of RegUnitB)))) Less than or equal to ANGBOU_ATE_30
        • Then - Actions
          • Unit - Cause RegUnitA to damage RegUnitB, dealing 100.00 damage of attack type Spells and damage type Normal
        • Else - Actions
      • -------- clean up --------
      • Custom script: call RemoveLocation(udg_RegLocA)
      • Custom script: call RemoveLocation(udg_RegLocB)
The two lines of custom script are for leak prevention and not required for the actual test (but are recommended because leaks can cause maps to perform badly).
 

Attachments

  • RobertMKD - ATE.w3x
    13.2 KB · Views: 44
Level 11
Joined
Jan 23, 2015
Messages
788
No but the output from cosine is the same at -350, 10, 370 etc. As you know from your high-school trigonometry class already.
cosine-graph.gif

The best part is that for all real inputs cosine is defined as that repeating output. As such angles that wrap around to >360 or <0 do not matter as you get the correct output all the time.

By applying an inequality (testing for a range) to the output of cosine you then test a range of input angles. The value used for the test is easily produced by a cosine of the maximum deviant angle you want to allow. The symmetry and repeating nature of cosine handles it all for you.


There is. The one we are telling you about...

I created you a little demo map showing the approach I am referring to. In the demo map attacking a unit from directly behind +/- 30 degrees will deal 100 spell damage to the attacked unit. You will be able to try this for yourself by using the footman provided in the demo map which deal 0 damage to attack (and kill) the Blood Mages directly north of you.

This trigger is used for initialization.
  • Angle Test Example Init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set ANGBOU_ATE_30 = (Cos((180.00 - 30.00)))
And this does the actual testing for dealing damage.
  • Angle Test Example
    • Events
      • Unit - A unit Is attacked
    • Conditions
    • Actions
      • -------- setup register variables --------
      • Set RegUnitA = (Attacking unit)
      • Set RegUnitB = (Attacked unit)
      • Set RegLocA = (Position of RegUnitA)
      • Set RegLocB = (Position of RegUnitB)
      • -------- perform test --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Cos(((Angle from RegLocB to RegLocA) - (Facing of RegUnitB)))) Less than or equal to ANGBOU_ATE_30
        • Then - Actions
          • Unit - Cause RegUnitA to damage RegUnitB, dealing 100.00 damage of attack type Spells and damage type Normal
        • Else - Actions
      • -------- clean up --------
      • Custom script: call RemoveLocation(udg_RegLocA)
      • Custom script: call RemoveLocation(udg_RegLocB)
The two lines of custom script are for leak prevention and not required for the actual test (but are recommended because leaks can cause maps to perform badly).

Okay, isn't it necessary to have another variable like ANGBOU_ATE_30 but (180 + 30)? cause now it lets units damage the Mage only from one side of his back right?
 
Nice, more chinese
Seriously, did you even bother trying it even once before throwing your hands in the air, screaming "OMG IT'S JASS; RUN!!"?

These are simple functions you can just throw into your map header and call from within GUI via a simple custom script line. If you don't know how to do it, why not learn how to use custom script to call a Jass function instead of keeping your blinders closed? Heck, you could have just asked and we would have been happy to give you an example.

... seriously, what is wrong with you guys unwilling to even try one thing outside of your comfort zone? Are you afraid you might actually learn something in the process?
 
Level 11
Joined
Jan 23, 2015
Messages
788
No, It's just that I said that I use GUI only, and you gave me a JASS example, first I don't understand what are you showing me and is that the thing I want, second, again, I don't understand that JASS trigger, how do I adapt that trigger in my map since I don't understand it?
GUI is enough for me, I still use JASS scripts to clear leaks, move units, get locations, but I don't need to learn the whole language, GUI is a lot more clear to me, and I can find an action I want a lot more easier than a function in JASS, I really do not see a reason why I should bother losing time and learning JASS..
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,285
there's many programming languages out there why do I have to learn JASS?
Once you know one programming language, you can pretty much learn them all without much effort. For example if I was to be shown JASS now without any prior knowledge about it I could probably pick it up in an hour or two just from seeing the main script files.
 
No, It's just that I said that I use GUI only, and you gave me a JASS example, first I don't understand what are you showing me and is that the thing I want, second, again, I don't understand that JASS trigger, how do I adapt that trigger in my map since I don't understand it?
There's no need to understand it. It's two simple functions that have input parameters and give you an output boolean that tells you if the unit is behind or not. You don't have to understand how a calculator works internally in order to calculate 2+2. This is similar.

GUI is enough for me, I still use JASS scripts to clear leaks, move units, get locations, but I don't need to learn the whole language, GUI is a lot more clear to me, and I can find an action I want a lot more easier than a function in JASS, I really do not see a reason why I should bother losing time and learning JASS..
What, so you even know how to use Custom Script? Then what exactly is the problem? Just call the function and be done with it. It works exactly the same as removing a location via Custom script.

Also, the reason why you should "bother wasting time learning Jass" is that in the end, it will save you more time than it takes to learn the language. It makes you understand more about the game and programming in general, which will not only help you creating higher quality maps, but also teaches you a valuable skill for your life.

To all the people that say learning Jass is a waste of time, consider this:

I can write the same spell you make in GUI roughly 20 times faster in Jass. Why? Because GUI is inconvenient as hell with all the clicks and dropdowns.
I'd go as far to say that even for very simple maps like tower defenses, it's already worth it learning the basics of Jass in terms of time efficiency.
 
Status
Not open for further replies.
Top