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

[GUI] How to use Coordinates instead of Points(Locations) in GUI

Level 11
Joined
Jul 25, 2014
Messages
490
How to use coordinates instead of points in GUI

General description:
Most GUI Trigger users use points(locations) in their code to get a point in the map, mostly for moving, creating effects, checking valid points and so on. And they are probably frustrated with them because they're not efficient, full of lags and have to be cleaned by a custom script after usage.
JASS doesn't have such problems because they use coordinates. This tutorial helps you convert your coding full of points into full of coordinates.

NOTE! This tutorial is reliant on custom script usage. If you're just starting to learn triggers, it is recommended to wait until using them, unless they're necessary. There will be a lot of udg_ prefixes (declaration of a global GUI variable).

Why coordinates?
Because unlike points, they ARE efficient. They don't need to be cleaned up because they're Real type variable values. They are also more efficient in terms of speed and moving units with coordinates using SetUnitX/Y do not interrupt orders. It can be chosen to interrupt them using SetUnitPosition which also take coordinates as arguments.

Practical usage
-------------------------------------------------------------
For practical usage, one should have at least two Real type variables, let's call them x and y. Let's start off with something simple like creating a special effect at the target point of ability being cast, using coordinates and a simple Thunder Clap effect.
  • Special effect usage
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Doom
    • Actions
      • Custom script: set udg_x = GetSpellTargetX()
      • Custom script: set udg_y = GetSpellTargetY()
      • Custom script: call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl", udg_x, udg_y))
      • -------- Doesn't have to be cleaned! --------
-- Let's break it down!
  1. First line is to set the Target Point Of Ability Being Cast X coordinate. Notes - this doesn't actually use a point.
  2. Second line is for the same reason, just with an Y coordinate.
  3. This is where it gets a little tricky for an ordinary user. We instantly destroy the effect created because if we don't it will leak, obviously. Inside the second brackets, the first argument is for the file path. Note that it has to be done with double backwards slashes, that's just how the compiler gets that it's a filepath. The second/third arguments are for the wanted X and Y coordinates. We use the ones we set earlier.
Another special effect function call is AddSpecialEffectTarget("Filepath", YourWantedUnit, "AttachmentPoint"), it is for creating a special effect on target unit. Filepath is for the filepath, obviously, YourWantedUnit is on which unit you want to attach the special effect to (preferably use a variable with a udg_ prefix) and AttachmentPoint is on which part of the body you want the effect to cling on. Mostly used with "origin", "chest" and "overhead".
-------------------------------------------------------------
Moving on to a little more sophisticated function with coordinates we have distances. Distances are calculated with the vector formula from your daily math struggles. It has to have at least two sets of coordinates (for the first point and for the second point). Let's give it a try!
  • Distance usage
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Doom
    • Actions
      • Set Caster = (Triggering unit)
      • Set Target = (Target unit of ability being cast)
      • Custom script: set udg_x = GetUnitX(udg_Caster)
      • Custom script: set udg_x = GetUnitY(udg_Caster)
      • Custom script: set udg_x2 = GetUnitX(udg_Target)
      • Custom script: set udg_y2 = GetUnitY(udg_Target)
      • -------- The following two lines are optional, they exist just to ensure readability. --------
      • Set dx = (x2 - x)
      • Set dy = (y2 - y)
      • -------- Let's calculate! --------
      • Set Distance = (Square root(((dx x dx) + (dy x dy))))
-- Breakdown!
  1. The first 6 lines are already known to us so I'll just skip them.
  2. dx and dy are both real values which calculate the distance on their respective axis. They can be skipped and directly be used in the distance formula, but it helps to read and edit if they are done seperately. Doesn't matter from which coordinate you subtract the other, in the end it is squared and the minus sign is gone (if it was there in the first place).
  3. The Distance is calculated by using a simple formula (Pythagoras Theorem) by using the two axis distances we set before.
That's it! Now you can use this distance to get the distance between two points without actually having to use points!
-------------------------------------------------------------
And now moving on to an example usage, for example lets take a spell. I'm going to do it with Dynamic Indexing since, in my opinion, it is the most common method for GUI users to make their spells or triggers MUI. If you don't know what it does, make sure to read up on PurgeandFire's tutorial about Dynamic Indexing.
A test spell that spams messages on the screen whenever the distance between the caster and the target is greater than 300 and pulls the target to the caster, not interrupting orders. If it is lower, it will say "Hello good sir" and end the spell. And it's done with calculating the distance with coordinates.
  • Practical Usage
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Doom
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • MaxIndex Equal to 0
        • Then - Actions
          • Trigger - Turn on Practical Usage loop <gen>
        • Else - Actions
      • -------- Setting up needed variables. --------
      • Set MaxIndex = (MaxIndex + 1)
      • Set Caster[MaxIndex] = (Triggering unit)
      • Set Target[MaxIndex] = (Target unit of ability being cast)
      • Custom script: set udg_x = GetUnitX(udg_Caster[udg_MaxIndex])
      • Custom script: set udg_x = GetUnitY(udg_Caster[udg_MaxIndex])
      • Custom script: set udg_x2 = GetUnitX(udg_Target[udg_MaxIndex])
      • Custom script: set udg_y2 = GetUnitY(udg_Target[udg_MaxIndex])
      • -------- Setting up the angle, (from target to caster) explained later. --------
      • Custom script: set udg_Angle[udg_MaxIndex] = Atan2(udg_x2 - udg_x, udg_y2 - udg_y)
      • -------- Calculating the distance. --------
      • Set dx = (x2 - x)
      • Set dy = (y2 - y)
      • Set Distance[MaxIndex] = (Square root(((dx x dx) + (dy x dy))))
  • Practical Usage loop
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • For each (Integer CurrentIndex) from 1 to MaxIndex, do (Actions)
        • Loop - Actions
          • -------- Setting up needed variables. --------
          • Custom script: set udg_x = GetUnitX(udg_Caster[udg_CurrentIndex])
          • Custom script: set udg_x = GetUnitY(udg_Caster[udg_CurrentIndex])
          • Custom script: set udg_x2 = GetUnitX(udg_Target[udg_CurrentIndex])
          • Custom script: set udg_y2 = GetUnitY(udg_Target[udg_CurrentIndex])
          • -------- Calculating the distance. --------
          • Set dx = (x2 - x)
          • Set dy = (y2 - y)
          • Set Distance[MaxIndex] = (Square root(((dx x dx) + (dy x dy))))
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Distance[MaxIndex] Greater than or equal to 300.00
            • Then - Actions
              • -------- Moving the unit using SetUnitX/SetUnitY. --------
              • Custom script: set udg_Angle[udg_CurrentIndex] = Atan2(udg_x2 - udg_x, udg_y2 - udg_y)
              • Custom script: call SetUnitX(udg_Target[udg_CurrentIndex], udg_x2 + 5.00 * Cos(udg_Angle[udg_CurrentIndex]))
              • Custom script: call SetUnitY(udg_Target[udg_CurrentIndex], udg_y2 + 5.00 * Sin(udg_Angle[udg_CurrentIndex]))
              • Game - Display to (All players) the text: Come back here!
            • Else - Actions
              • Game - Display to (All players) the text: Hello good sir!
              • -------- Deindexing. --------
              • Set Caster[CurrentIndex] = Caster[MaxIndex]
              • Set Target[CurrentIndex] = Target[MaxIndex]
              • Set Distance[CurrentIndex] = Distance[MaxIndex]
              • Set CurrentIndex = (CurrentIndex - 1)
              • Set MaxIndex = (MaxIndex - 1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • MaxIndex Equal to 0
                • Then - Actions
                  • Trigger - Turn off Practical Usage loop <gen>
                • Else - Actions
      • -------- Done! --------
-- There's nothing to breakdown anymore, it is that simple! Now you can use coordinates instead of points in your triggers! For the SetUnitX/SetUnitY/Atan2 angle movement make sure to visit the About Movement tutorial by D4RK_G4ND4LF, it is extremely descriptive.

Further notes:
This can be done with local variables instead of using global ones when declaring coordinates, but this would push the user forward to using JASS over GUI. It can be a nice waypoint when it is done with globals -- it is not that scary.

Drop feedback!
@Moderators: If there were major points/minor typos that I missed out, let me know.

@Users: If the information was still not quite clear to you, let me know, I will update the tutorial to be as descriptive as possible.
 
Last edited:

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,183
More blank rows and structure in general this looks really messy.

Also I don't see the point of using coordinates in GUI.
Sure it saves lines of code but it just makes it more complicated than it should.
Since no GUI action supports coordinates you have to convert it to jass each time you use a location, it's simply inconvenient for most GUI users.

If they want to learn this they should just move to jass entirely and learn it that way.
 
And they are probably frustrated with them because they're not efficient, full of lags and have to be cleaned by a custom script after usage.
About points - pretty harsh statement, and just a bit exaggerating. Points are not sooo bad.
Using groups is muuuuch consuming, and still can be used without problems.

hey are also more efficient in terms of speed and moving units with coordinates do not interrupt orders
That's not quite true, as SetUnitPosition does also take real x/y as arguments.
The difference to JASS is, we have access to SetUnitX/Y, which won't interrupt orders.

Learning about others GetX/Y natives would deinifitly be a good idea, so they learn them to know For example like Chasoy mentioned GetLocationX

We maybe don't share the very same view about the matter, but for me the whole "using reals in GUI" is not about efficiency. Not at all.
If they want efficiency, then they are just wrong with using GUI itself, and they won't be able to change it with small changes like using reals over locations.
For me it's only about open the borders to new natives and possibilities they can use.
Stuff that allow them the cool things like using SetUnitX.
So it's imo also not bad when a GUI user uses locations in combinations with reals. It mostly will be just simpler.

I'm for making JASS more tempting for the GUI users, so we need to show all possibilities to let them know how much more awesome it is. :D
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
When to use coordinates?
Periodic actions that have small timeouts.


And, as IcemanBo was saying, coordinates themselves aren't the thing responsible for the order interruption when a unit is moved, it's the function that the GUI calls, which is the native SetUnitPosition.
 
Level 11
Joined
Jul 25, 2014
Messages
490
@IcemanBo
I see using custom scripts in GUI as a waypoint to JASS that the user uses. Most people start using custom scripts in GUI before actually trying JASS, and this tutorial covers one of the most important aspects when using JASS. Coordinates are not common in GUI, while in JASS, they are.
About the harsh statement about points, an average user will experience lags in their map and they have to scan through their code to see where they failed, and it's either unit groups or points. I could potentially cover unit groups as well, but I will see how it goes as-is.

@chobibo and IcemanBo
I'll make sure to include that information about SetUnitPosition, thanks.

EDIT: Slight update.
 
About the harsh statement about points, an average user will experience lags in their map and they have to scan through their code to see where they failed, and it's either unit groups or points. I could potentially cover unit groups as well, but I will see how it goes as-is.
That's just not true.
Locations don't cause lags. The whole "locations are bad and unefficient" presumption is only misleading and not really on the topic.
Points per se are not unefficient.
Working with reals is slightly faster, yeah, but you will never account any problems when using points over reals.
And no, mentioning that groups are also heavy data types would not be beneficial. It has nothing to do with it.

Of course you are right that it's the way to JASS, I never said the opposite.
But it should really be the topic to point out the new possibilities instead of comparing microseconds for efficiency.
 
Good idea. A few notes:
  • Points are not really "full of lags". They work just fine and are very fast--as long as you remove them when you're done!
  • That being said, coordinates aren't really used for efficiency. In GUI, they are used if you want to use SetUnitX and SetUnitY. In JASS, most natives use coordinates so it is just convenient to do so. You could just as easily use points, though.
  • SetUnitX/Y does not interrupt orders, but it also does not check pathing. If you use SetUnitX/Y willy-nilly, you might end up sending units into trees, cliffs, water, or even off the map. You can use a system like this one to check if a point is pathable before moving a unit there.
  • As KILLCIDE mentioned, it'd be nice if you'd go over calculating polar projection, angle between points, and distance between points. Math is scary.

    One easy way to demonstrate it is to show the equivalences. For example, this:
    • (Position of (Casting unit)) offset by \500.00 towards (30.00 degrees)
    Is equivalent to:
    JASS:
    set x = GetUnitX(GetTriggerUnit()) + 500.00 * CosBJ(30.00)
    set y = GetUnitY(GetTriggerUnit()) + 500.00 * SinBJ(30.00)
    You don't necessarily need to explain the math, but you should give the user enough information so he knows where to fill in the holes when he tries it out himself.

I think you should edit your examples to only use coordinates when absolutely necessary. If you use coordinates too much, you basically end up with 50% JASS since none of the GUI functions can even use coordinates. If I were you, I would only show:
(1) How to get the x/y coordinates from a point
(2) How to use SetUnitX and SetUnitY
(3) How to convert x/y coordinates into a point

GUI already has methods to calculate distance between points/angle between points, etc. It is fine to use them! :)

Just be sure to clear leaks and you're golden. Any other "optimizations" will only save microseconds, but they'll probably take 3x longer to write and 4x longer to debug if you aren't already familiar with JASS.
 
Level 11
Joined
Jul 25, 2014
Messages
490
Hmm, I'll get the information that points are generally inefficient and full of lags out of this tutorial, however it seems like I didn't get one point straight - the memory leak of points can somtimes be a pain in the ass. This turned out to be a tutorial I didn't mean to do in the first place.
Imo, the conversion from points to coordinates and vice-versa is pointless, since there is nothing you can't do with coordinates that you can with points. Maybe I should list all the natives involving x/y coordinates and how to use them or just link JassCraft which has the native list?

As for the other notes, I'll get my stuff cleaned up and I'll add some more explanations to this once I force myself to actually be productive -.-
 
Why remove points at all when you can just use "call MoveLocation(location, x, y)"? May I suggest adding that into this tutorial since it can be turned into a easy GUI-like function if there is no issue with it? Kind of wondered about it for years now about people creating and destroying locations constantly instead of just moving already created ones, wouldn't it cost less?
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
Why remove points at all when you can just use "call MoveLocation(location, x, y)"?
Recycling the location instead of recreating and destroying it is always better. However, MoveLocation() takes X and Y as a parameter, so why even bother to continue using a location if you are going to be using coordinates anyway? As IcemanBo said, MoveLocation() is usually only used for GetLocationZ().
 

Submission:
How to use coordinates instead of points in GUI

Date:
21 November 2016

Status:
Graveyard
Note:

Tutorial name is not perfectly fitting. It's more about Custom Scripts than GUI, which might be misleading.

There are several important points mentioned in eariler posts. For example the whole thing about "efficiency".
We should outline the new possibilities instead of using mainly our "this is more efficient" card, which is not so a imprtant goal here.

I see it senseful when it becomes something like a general introducing tutorial for using reals over locations.

Purge made some good points, too, for example with comparing the default GUI way, and then the tequniques with reals.

The last example, making a MUI spell seems a bit too much. It might stay probably as a final example, we should focus on simple and straight forward examples.

As no changes were made for quite some time now, it's sent to Graveyard for now.
 
Top