- Joined
- Sep 26, 2009
- Messages
- 9,534
[GUI]*Advanced Triggering Tips
Why I am writing this
Hopeful goals
Leak removal tips
Other tips
Decrease your menu-scrolling time
Most Common Leaks - Thanks to Hashjie for this tip
Why are memory leaks important to watch out for?
Other tips
Efficiency
Conclusion
Advanced Triggering Tips
Why I am writing this
I see lots of GUI scripts which make the same mistakes again and again, whether it be by leaks or by adding work to the list that doesn't need to be added.
Hopeful goals
I hope GUI users will learn to pay better attention to what they are working with and save a bit of time.
Leak removal tips
I often see people come here asking for "does this thing leak?" without realizing that leak-spotting is not a magical ability but a simple science.
- Example
Yep, real magic. You can see the operations at the scripting level and realize that bj_wantDestroyGroup, if true, will simply tell the game to destroy the group, not "somehow destroy the group in some mysterious way".
- So what's a ForGroupBJ, and does it taste good on salad?
ForGroupBJ is, in Trigger terminology, "Unit Group - Pick all units in (Unit group) and do (Actions)". So, right before entering any "Pick all units", just set the bj_wantDestroyGroup variable and you're set and ready.
Isn't that a lot easier than setting the group to a temporary variable, doing the "pick all units", then adding a custom script variable to destroy the group? I certainly like to think so.
Just make sure to watch out for any "pick all units" in your map, because there is a custom script eager to help you destroy those memory leaks. Here are the other things you can use it for:
- Example
-
Custom script: set bj_wantDestroyGroup = true
JASS:
//===========================================================================
function ForGroupBJ takes group whichGroup, code callback returns nothing
// If the user wants the group destroyed, remember that fact and clear
// the flag, in case it is used again in the callback.
local boolean wantDestroy = bj_wantDestroyGroup
set bj_wantDestroyGroup = false
call ForGroup(whichGroup, callback)
// If the user wants the group destroyed, do so now.
if (wantDestroy) then
call DestroyGroup(whichGroup)
endif
endfunction
Yep, real magic. You can see the operations at the scripting level and realize that bj_wantDestroyGroup, if true, will simply tell the game to destroy the group, not "somehow destroy the group in some mysterious way".
- So what's a ForGroupBJ, and does it taste good on salad?
ForGroupBJ is, in Trigger terminology, "Unit Group - Pick all units in (Unit group) and do (Actions)". So, right before entering any "Pick all units", just set the bj_wantDestroyGroup variable and you're set and ready.
Isn't that a lot easier than setting the group to a temporary variable, doing the "pick all units", then adding a custom script variable to destroy the group? I certainly like to think so.
Just make sure to watch out for any "pick all units" in your map, because there is a custom script eager to help you destroy those memory leaks. Here are the other things you can use it for:
-
Uses
-
Events
-
Conditions
-
Actions
-
Set Count = (Number of units in (Unit group))
-
Set Unit = (Random unit from (Unit group))
-
Set Boolean = (All units in (Unit group) are dead) Equal to True
-
Set Boolean = ((Unit group) is empty) Equal to True
-
Unit Group - Add all units in (Unit group) to (Unit group)
-
-------- The first group will be destroyed in the above case --------
-
Unit Group - Remove all units in (Unit group) from (Unit group)
-
-------- The first group will be destroyed in the above case --------
-
-
Other tips
I don't know about the other "Leak Removal Experts", but here's how I decode GUI into a readable format for detecting leaks and other things:
What's that, you ask? Those are "magic" indicators "magically" (or, to some, "annoyingly") placed throughout your GUI script.
No, really, they almost always indicate what is in JASS a "function call". If you notice, every instance of "Integer A" from a "For each (Integer A)" loop is surrounded by (parenthesis).
This is because it is a call to a function "GetForLoopIndexA()" instead of "bj_forLoopAIndex", which is what you're really working with and what the function returns.
Because of this, looping with Integer A has slower performance than looping your own custom integer. For people who value execution speed of code and other programming gibberish, (this) should be a sign.
Examples
"(" and ")"
What's that, you ask? Those are "magic" indicators "magically" (or, to some, "annoyingly") placed throughout your GUI script.
No, really, they almost always indicate what is in JASS a "function call". If you notice, every instance of "Integer A" from a "For each (Integer A)" loop is surrounded by (parenthesis).
This is because it is a call to a function "GetForLoopIndexA()" instead of "bj_forLoopAIndex", which is what you're really working with and what the function returns.
Because of this, looping with Integer A has slower performance than looping your own custom integer. For people who value execution speed of code and other programming gibberish, (this) should be a sign.
Examples
-
(Position of (Triggering unit))
-
(Player((Integer A)))
-
(Owner of (Triggering unit))
Decrease your menu-scrolling time
Not only is (Triggering unit) <<faster>> than (Casting unit) or (Dying unit) in terms of execution time, it's actually faster to click because it is always the first option on the menu tree. Just use (Triggering unit) and, of course, set it to a variable if you use it more than 2-3 times.
Now that that's been mentioned, how about (Owner of (Triggering unit))? Ever notice that (Triggering player) is the first item on the menu-tree? That's because it's able to be used in more cases than you could imagine.
Every event which is selectable as "Any unit ---" is, in JASS, a "playerunitevent". This means that it has two triggering components, a unit, and a player.
Long story short, every "Any unit event" can use (Triggering player) instead of (Owner of (Triggering unit)) and get the same results.
This will cut down on your menu-scrolling time as well as game execution time, so it's faster in every sense of the word.
You can thank PurgeAndFire for showing me the (Triggering player) trick.
Now that that's been mentioned, how about (Owner of (Triggering unit))? Ever notice that (Triggering player) is the first item on the menu-tree? That's because it's able to be used in more cases than you could imagine.
Every event which is selectable as "Any unit ---" is, in JASS, a "playerunitevent". This means that it has two triggering components, a unit, and a player.
Long story short, every "Any unit event" can use (Triggering player) instead of (Owner of (Triggering unit)) and get the same results.
This will cut down on your menu-scrolling time as well as game execution time, so it's faster in every sense of the word.
You can thank PurgeAndFire for showing me the (Triggering player) trick.
Most Common Leaks - Thanks to Hashjie for this tip
Points
Unit groups
Player groups
Special effects
Lightning effects
Dummy units
Floating Text
Notes
-
(Position of (Unit))
-
(Position of (Item))
-
(Center of (Region))
-
(Position of (Triggering unit) offset by 50.00 towards (Position of (Attacking unit)))
-
-------- --------
-
Set TempPoint = (Position of (Unit))
-
Custom script: call RemoveLocation(udg_TempPoint)
Unit groups
-
Unit Group - Pick all units in (Unit group) and do (Actions)
-
(Number of units in (Unit group))
-
-------- --------
-
Set TempGroup = (Unit group)
-
Custom script: call DestroyGroup(udg_TempGroup)
-
Custom script: set bj_wantDestroyGroup = true
Player groups
-
Game - Text Message to (Player Group)
-
-------- --------
-
Set TempForce = (Player Group)
-
Custom script: call DestroyForce(udg_TempForce)
Special effects
-
Special Effect - (Destroy (Last created Special Effect))
Lightning effects
-
Set LightningVar = (Last created Lightning Effect)
-
Custom script: call DestroyLightning(udg_LightningVar)
Dummy units
-
Unit - Make (Last created unit) explode on death
-
Unit - Add 1.00 Second Generic Expiration Timer to (Last created unit)
Floating Text
-
Floating Text - Change the lifespan of (Last created floating text) to 5.00 seconds
-
-------- Or --------
-
Floating Text - Destroy (Last created floating text)
Notes
Plenty of other things leak, most of which are a waste of time when working with GUI, but just keep in mind that pre-placed things in GUI usually should just be left alone (regions, sounds, triggers) as they are non-repeating leaks (aren't created+leaked over and over).
Why are memory leaks important to watch out for?
If you haven't experienced first-hand, I suppose an explaination is in order.
You know that part of a computer called RAM? You need at least 128 of it to play WarCraft III, a recommended 512 IIRC. That's because WarCraft III idles at 100-ish MB of RAM. If you look at how much RAM WarCraft III is running, you'll notice it's higher when there are more units on the map than if there were few or none (on older computers it's often associated with slow performance as well). This is the RAM each object in the game is taking up - units, trees, doodads, items and those invisible things called locations, unit groups, player groups and more... so removing those things instead of leaving them in existence means that WarCraft III won't take up so much RAM.
You know that part of a computer called RAM? You need at least 128 of it to play WarCraft III, a recommended 512 IIRC. That's because WarCraft III idles at 100-ish MB of RAM. If you look at how much RAM WarCraft III is running, you'll notice it's higher when there are more units on the map than if there were few or none (on older computers it's often associated with slow performance as well). This is the RAM each object in the game is taking up - units, trees, doodads, items and those invisible things called locations, unit groups, player groups and more... so removing those things instead of leaving them in existence means that WarCraft III won't take up so much RAM.
Other tips
If you use "pick all units" or "pick all players", you cannot use waits inside of the loops they give. Yeah, most people don't realize this. Any why should they? It's a weird thing that it doesn't work. But here you go:
-
Error
-
Events
-
Conditions
-
Actions
-
Custom script: set bj_wantDestroyGroup = true
-
Unit Group - Pick all units in (Units in (Playable Map Area)) and do (Actions)
-
Loop - Actions
-
-------- Some stuff --------
-
Wait - 1.00 seconds
-
-------- More stuff --------
-
-
-
-
-
Error
-
Events
-
Conditions
-
Actions
-
Custom script: set bj_wantDestroyGroup = true
-
Unit Group - Pick all units in (Units in (Playable Map Area)) and do (Actions)
-
Loop - Actions
-
-------- Some stuff --------
-
Trigger - Run Error Fixed <Gen> (Ignoring conditions)
-
-
-
-
-
Error Fixed
-
Events
-
Conditions
-
Actions
-
Wait - 1.00 seconds
-
-------- More stuff --------
-
-
Efficiency
Efficiency is the one of the most important thing about coding. Most people don't talk about efficiency for GUI triggers because they give it up as a "lost cause". I firmly disagree - even though GUI scripts can't be as efficient as doing everything in custom script (JASS), there are steps that can be taken to make it better - any improvements are welcome.
One of the first things people start doing when figuring out how to make JASS execute faster is store things like (Triggering unit) and (Level of (Ability being cast) for (Triggering unit)) into local variables, and reference the variables instead of calling functions. GUI users often miss this. The problem is that GUI users don't usually look at "(" and ")" as function calls - but in almost all cases, that is indeed what they are.
As a GUI user, those parenthesis should become very important for you when reviewing your code. When you see a lot of repetition in parenthesis ((Integer A) is also a function call), you should be looking out for ways to avoid using them for the same thing more than once or twice. Setting them to a variable is the best way to achieve this.
One of the first things people start doing when figuring out how to make JASS execute faster is store things like (Triggering unit) and (Level of (Ability being cast) for (Triggering unit)) into local variables, and reference the variables instead of calling functions. GUI users often miss this. The problem is that GUI users don't usually look at "(" and ")" as function calls - but in almost all cases, that is indeed what they are.
As a GUI user, those parenthesis should become very important for you when reviewing your code. When you see a lot of repetition in parenthesis ((Integer A) is also a function call), you should be looking out for ways to avoid using them for the same thing more than once or twice. Setting them to a variable is the best way to achieve this.
-
Set Caster = (Triggering unit)
-
Set Index = (Player number of (Triggering player))
-
Set Level = (Level of Blizzard for Caster)
Conclusion
I don't even read the whole GUI trigger to spot for memory leaks, I just look for the (parenthesis) and find out everything I need to know. I see this:
(Ability being cast) is a function call, (Owner of (Casting unit)) is two function calls, (Position of (Casting unit)) is two function calls, and I think to myself, "how can we shorten the amount of parenthesis?"
The result:
-
Example
-
Events
-
Unit - A unit starts the effect of an ability
-
-
Conditions
-
(Ability being cast) Equal to Blizzard
-
-
Actions
-
Unit - Create 1 Footman for (Owner of (Casting unit)) at (Position of (Casting unit)) facing Default building facing degrees
-
-
(Ability being cast) is a function call, (Owner of (Casting unit)) is two function calls, (Position of (Casting unit)) is two function calls, and I think to myself, "how can we shorten the amount of parenthesis?"
The result:
-
Example
-
Events
-
Unit - A unit starts the effect of an ability
-
-
Conditions
-
(Ability being cast) Equal to Blizzard
-
-
Actions
-
Set TempLoc = (Position of (Triggering unit))
-
Unit - Create 1 Footman for (Triggering player) at TempLoc facing Default building facing degrees
-
Custom script: call RemoveLocation(udg_TempLoc)
-
-
Last edited by a moderator: