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

Temporary and specific variables

Status
Not open for further replies.
Level 6
Joined
May 15, 2009
Messages
191
Hello Hive, I have a general question concerning variables.

I believe I've heard that the most efficient way to keep your map running is to have as few variables as is necessary.
So for instance, if you have two different spells, let's assume simple single-target damage spells. Would you then have to create variables for each spell as such:

Spell1_casterUnit
Spell1_targetUnit

Spell2_casterUnit
Spell2_targetUnit

Or could you get away with simply making temporary variables like:

Tempu1
Tempu2

And then use those same variables for both triggers? What if the two abilities are supposedly cast at the same time? Would the map crash?
Or is there even any problem with creating all four variables rather than just two?
 
Realistically, having a lot of variables isn't going to slow down your map. It might slow down development time (because you have to scroll through a bunch of variables, lol), but if you use separate variables for each trigger, then you won't have random errors.

Shared variables are useful and convenient, but you have to be careful. Here is a simple example of a time where shared variables can cause problems:
  • Kill Unit
    • Events
      • Unit - A unit starts the effect of an ability
    • Conditions
    • Actions
      • Set TempPoint = (Position of (Triggering unit))
      • Unit - Kill (Triggering unit)
      • Special Effect - Add special effect using "SomeSFXModel.mdl" at TempPoint
      • Special Effect - Destroy (Last created effect)
      • Custom script: call RemoveLocation(udg_TempPoint)
  • A Unit is Killed
    • Events
      • Unit - A unit dies
    • Conditions
    • Actions
      • Set TempPoint = (Position of (Triggering unit))
      • Unit - Order Spirit Healer 0001 <gen> to move to TempPoint
      • Custom script: call RemoveLocation(udg_TempPoint)
Seems like pretty harmless triggers, right? If you try this, it'll probably create the special effect at the center of the map. This is a really hard problem to debug, especially as you have more and more triggers in your map.

As soon as the first trigger finishes the line "Unit - Kill (Triggering Unit)", the wc3 engine jumps over to the trigger "A Unit is Killed" to evaluate the trigger. The whole trigger completes, and ultimately TempPoint is reassigned and removed. As soon as the engine finishes the "A Unit is Killed" trigger, it jumps back to the original trigger and moves on to the special effect lines. Now that TempPoint has been removed, it'll just refer to a destroyed location (which will usually give the center of the map), so the special effect will be created in the wrong area.

How do you fix it? Well, if you have the insight of what is going on, you can just move the assignment of TempPoint to be after the "Unit - Kill unit" line. But that isn't always practical in all situations. If you use separate variables, you won't ever run into this issue. That is why we use separate variables for each trigger.

My example is a bit contrived, and I haven't actually tested it so my analysis might be untrue. But this definitely is a common issue with "For each integer..." loops, damage triggers, issuing orders, etc. That is why we made a rule that all the loops should have a dedicated looping variable, e.g.:
  • For each (SL_LoopingInteger) from 1 to 10 do (Actions)
(Integer A) and (Integer B) are also shared variables, and if you happen to run a line of code that jumps to another trigger and performs the loop, then you'll end up with weird issues.

There are actually many approaches to this issue. Technically, whenever you use a shared variable, you could just assign a local variable to it, use the shared variable, and then clean the leak and assign it back to the local variable. But that method involves custom script and actually makes things a lot more inconvenient. As such, separating your globals is a much better idea.

(This is actually a pretty common issue in real-world programming. That is why we tend to avoid global variables. However, GUI only has access to global variables, so you have to work with what you're given)
 
Level 19
Joined
Dec 12, 2010
Messages
2,069
temp variables should be assigned right before they gonna be used, not in start of actions query
i don't like to spam globals, prefering using temp hashtable for that
normally, if you have very few variables, on_damage events may alter some of them, and transfered to ForGroup temp variable with ability level won't stay long. So hashtable allows to create "global" dynamically.
im not sure about perfomance quality in this specific case, but overall it seems more handful to work with.
 
temp variables should be assigned right before they gonna be used, not in start of actions query

yeah, if you follow this you don't really need to worry, except for things you need to destroy/remove (e.g. locations). And if you know whatever actions you are implementing aren't go to fire any events, then you can use a shared variable safely.

But if you need to use it more than once and any of the functions will potentially fire an event, then it is probably wise to switch to a specific variable. Since it is difficult (and takes a long time) to determine which situations warrant a specific variable, it is just easier to use specific variables for everything. It is kinda the same situation with nulling variables. Technically you don't need to null all handles (only agents), but it is quicker (programming-wise) & easier to just null everything, with very little performance/clarity loss.
 
Last edited:
Level 33
Joined
Mar 27, 2008
Messages
8,035
Temp variables are variables that can be commonly used by more than 1 Spell at a time.
Specific variables are variables that are unique to that Spell only.

I don't have Trigger Editor with me, so I'm just gonna write this in text;

Temp Variable (Instant Use and Done)
You set a location for your dummy unit to appear and cast spell(s).
This method, you can create a variable name TempLoc and use in 2 different spells (because it's just a matter of dummy creation).

Specific Variable (Long-running Concurrent Process)
You set a Spell Duration for debuff.
This method, you cannot share between spells because some spells have 3 seconds duration, some have 8 seconds duration, therefore, the usage is different for each scenario.

Also, usually a Specific Variable is used when you create a trigger that consist of long-running concurrent process, like the example, Duration.

If it is instant, most of the time we use a Temporary Variable.
Even though sometimes we save the value of the instant to a new specific variable, the instantiation of the instant variable will be saved temporarily in a temp variable.

Also, about "what happens if they were cast at the same time?" issue, there is none.
Computer still process the instruction one by one.

Try picking units in a group and kill them.
If you put an interval in-between the kills, you will notice they would die one by one - this means the computer processes data one at a time, there can never be "same".
 
If it is instant, most of the time we use a Temporary Variable.
Even though sometimes we save the value of the instant to a new specific variable, the instantiation of the instant variable will be saved temporarily in a temp variable.

Also, about "what happens if they were cast at the same time?" issue, there is none.
Computer still process the instruction one by one.

Try picking units in a group and kill them.
If you put an interval in-between the kills, you will notice they would die one by one - this means the computer processes data one at a time, there can never be "same".

It is true that things aren't ran in parallel in Wc3. However, the main issue with shared variables is the order in which wc3 evaluates trigger events. I outlined it in my post. Even if you use shared variables only in instantaneous spells/situations, the value can still change under specific circumstances. If you use it as DracoL1ch mentioned, you probably won't encounter any issues. Technically you can still leak if you need to reference it at all after the instruction (e.g. RemoveLocation() or DestroyGroup()), but w/e.

If you know exactly what you're doing and know exactly what is going on in your map, you can use shared variables probably without any issue. But specific variables just avoid those issues altogether.
 
Status
Not open for further replies.
Top