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

Rapid fire events - How bad are they?

Status
Not open for further replies.
Level 8
Joined
May 21, 2019
Messages
435
I often encounter a scenario in which the event I am using is so incredibly broad, that I can't help but subconciously worry about them piling up.

This is particularly the case for generic events, such as "A unit is attacked". Lets say that you have loads of triggers with this event, and that you have loads of units fighting all over the place with really fast attack speed. It'd trigger each of these triggers up to several hundred times per second.
In this case, I am thinking entirely on the amount that it taxes the game to see a generic event, and then return a false on the condition check. How high would you have to pile these events before they start to become a serious issue, even when not resulting in any actions? Is it worth worrying about at all?
 
Last edited:
Level 45
Joined
Feb 27, 2007
Messages
5,578
Your description of your maze solution does not make it clear what you've done to 'save time'. If you can post a trigger someone can comment on it. I've always heard that triggerconditions are specifically extremely efficient so there can be many evaluations without issue. In general if you think this might be a problem for you you can do something like this for each different event:

  • Events
    • Unit - A unit is attacked
  • Conditions
  • Actions
    • Trigger - Run Trigger1 <gen> checking conditions
    • Trigger - Run Trigger2 <gen> checking conditions
    • Trigger - Run Trigger3 <gen> checking conditions
    • -------- None of these can have waits in them --------
    • -------- event responses probably still work in them --------
Or like this:

  • Events
    • Unit - A unit is attacked
  • Conditions
  • Actions
    • Set AttackedUnit = (Triggering Unit)
    • Set AttackedReal = 1.00
    • -------- all triggers using this variable event fire now and probably have to use variables like the one I wrote above, since (most) event responses won't work --------
    • Set AttackedReal = 0.00
  • Events
    • Game - AttackedReal becomes equal to 1.00
  • Conditions
  • Actions
    • Unit - Explode AttackedUnit
The best solution, of course, is to be smart about your triggers in the first place. Don't cut corners and save time if it makes the code complex or huge parts overlap or... etc.
 

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,011
  • Events
    • Unit - A unit is attacked
  • Conditions
  • Actions
    • Set AttackedUnit = (Triggering Unit)
    • Set AttackedReal = 1.00
    • -------- all triggers using this variable event fire now and probably have to use variables like the one I wrote above, since (most) event responses won't work --------
    • Set AttackedReal = 0.00
  • Events
    • Game - AttackedReal becomes equal to 1.00
  • Conditions
  • Actions
    • Unit - Explode AttackedUnit
The best solution, of course, is to be smart about your triggers in the first place. Don't cut corners and save time if it makes the code complex or huge parts overlap or... etc.
Wait...what's the purpose of doing double events? Sounds slower than doing everything in one trigger.

But yes Cespie, your scenario is too vague, you could have 600 units attacking each other at fast speed and with that event and have full performance, still, depends on the length of your code.
 
Level 8
Joined
May 21, 2019
Messages
435
But yes Cespie, your scenario is too vague, you could have 600 units attacking each other at fast speed and with that event and have full performance, still, depends on the length of your code.
The length is specified:
Event + condition returning false.

So in other words, the event is triggered, and then the actions are never run, because the condition returned false.
So in other words:
How taxing is it to detect an event and then check a condition? You say that 600 units triggering this really really fast, wouldn't even be an issue, do you know this for sure, or are you guessing?
 
Level 8
Joined
Oct 4, 2016
Messages
210
i had that doubt in the past, but after developing an AI with all standard events (spell effect, attack) and many evaluations for each AI player in a periodic trigger (groups, unit status, alliance checks, filters). All of that running together with other periodic instructions (hero spells, items, multiboard, etc.)
it seems that it requires a tons of heavy instructions in a very low time to notice a bad performance.
As they said above, depends on what are you doing in every event fired and how you are going to do it.

Regards
 
Level 8
Joined
May 21, 2019
Messages
435
Your description of your maze solution does not make it clear what you've done to 'save time'.
Yeah, not sure how you're even able to comment on that, seeing how I edited it out before the post even got moderated. I realized that it was a bit tricky to explain exactly what I did, but it basically ended up saving a shitton of coding time.
I ended up deleting it because I was on my work PC, and I couldn't for the life of me remember how I actually coded it, so I gave up trying to explain the scenario.

But since we are on the topic now... First off, look at the attached screenshot. That shows 1 of the 12 identical mazes and the 7 regions used as reference points for the pathing. I numbered them, for reference. Then, I put all 7x12 of these points into 7 arrays of 12 points each. 1 array for all 12 "point 1" locations, for example. I will spare you the wall of text on that one... but the event generator looks like this:

  • Events
  • Conditions
  • Actions
    • For each (Integer A) from 1 to 12, do (Actions)
      • Loop - Actions
        • -------- Add events to move triggers --------
        • Trigger - Add to CathLolicon toR1 <gen> the event (Unit - A unit enters Cspawn[(Integer A)])
        • Trigger - Add to CathLolicon toR2 <gen> the event (Unit - A unit enters Cr1[(Integer A)])
        • Trigger - Add to CathLolicon toR3 <gen> the event (Unit - A unit enters Cr2[(Integer A)])
        • Trigger - Add to CathLolicon toR4 <gen> the event (Unit - A unit enters Cr3[(Integer A)])
        • Trigger - Add to CathLolicon toR5 <gen> the event (Unit - A unit enters Cr4[(Integer A)])
        • Trigger - Add to CathLolicon toEnd <gen> the event (Unit - A unit enters Cr5[(Integer A)])
        • Trigger - Add to CathLolicon toHell <gen> the event (Unit - A unit enters Cend[(Integer A)])
As you can see, there's 7 movement triggers, 1 for each of the points. The loop above basically generates 12 events for each of them, so that it can run on all 12 mazes.

The actual triggers look like this:

  • CathLolicon toR3
    • Events
    • Conditions
      • (Owner of (Triggering unit)) Equal to Neutral Hostile
    • Actions
      • For each (Integer A) from 1 to 12, do (Actions)
        • Loop - Actions
          • If ((Custom value of (Triggering unit)) Equal to (Integer A)) then do (Unit - Order (Triggering unit) to Move To Cr3p[(Custom value of (Triggering unit))]) else do (Do nothing)
Originally, I thought that I could just ask the triggering unit to move to the next point by the index stored in their custom value (which, logically, should work), but the game for some reason lost track of which iteration of the trigger was being called, so I had to loop through all 12 scenarios to make sure that each iteration of the trigger only ran the 1 scenario that I needed.
Either way, I also realized that in this case, I am actually running through rapid action loops rather than condition checks and events, so I also deleted this because I realized it wasn't relevant to the main topic. But since we are here, can someone explain to me why this trigger doesn't work without the loop?

I've always heard that triggerconditions are specifically extremely efficient so there can be many evaluations without issue.
That's very comforting to hear!

The best solution, of course, is to be smart about your triggers in the first place. Don't cut corners and save time if it makes the code complex or huge parts overlap or... etc.
Being smart about your triggers can mean many things. Refactoring triggers saves a lot of time in both development and changes. If I hadn't used this approach, I would have had to code all 12 mazes instead of just 1.
 

Attachments

  • screenshot.png
    screenshot.png
    1.7 MB · Views: 62
Last edited:
Level 8
Joined
May 21, 2019
Messages
435
i had that doubt in the past, but after developing an AI with all standard events (spell effect, attack) and many evaluations for each AI player in a periodic trigger (groups, unit status, alliance checks, filters). All of that running together with other periodic instructions (hero spells, items, multiboard, etc.)
it seems that it requires a tons of heavy instructions in a very low time to notice a bad performance.
As they said above, depends on what are you doing in every event fired and how you are going to do it.

Regards

Thanks, that's a good case. :)
 

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,011
The length is specified:
Event + condition returning false.
This is no length...you're not calling any functions. Basically a useless "empty" trigger.

So in other words, the event is triggered, and then the actions are never run, because the condition returned false.
So in other words:
How taxing is it to detect an event and then check a condition? You say that 600 units triggering this really really fast, wouldn't even be an issue, do you know this for sure, or are you guessing?
It's called from experience, if you have 600 units and have the trigger do something with them with lots of crap code it will definetly lag, how much, I don't know, depends on your code. This isn't something you read in a book and its done, it's not a definetive answer. Go test and see it yourself.
My guess that your trigger doesn't work seems to be the custom value of the unit always being 0.
 
Level 8
Joined
May 21, 2019
Messages
435
This is no length...you're not calling any functions. Basically a useless "empty" trigger.
It's not necessarily empty, but whatever actions this theoretical trigger contains is irrelevant as the execution is terminated at the condition returning false. Therefore, whether it contains 0 or 100 actions, it doesn't really matter in this case.

it's not a definetive answer.
Actually, this could be quantified pretty easily be someone more tech savvy than me, and the overall load is pretty definitive, if you exclude boolean computations.

My guess that your trigger doesn't work seems to be the custom value of the unit always being 0.
I never said that my trigger doesn't work. The purpose of this thread was establishing whether or not running a ton of condition checks and event handlers causes a significant strain on the game or not. The consensus from the other answers appears to be that it is not significant.
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
Wait...what's the purpose of doing double events? Sounds slower than doing everything in one trigger.
Only one trigger is listening for the unit attacked event, the rest are all listening for the variable change event. It was my presumption that if anything (that is the caveat) the bottleneck would be in the event listening since the attacks are what is happening extremely frequently. There is no reason it would necessarily be 'better', I just thought to show all possibilities for the OP. I wouldn't do it that way.


Yeah, not sure how you're even able to comment on that, seeing how I edited it out before the post even got moderated.
It was in your post when I viewed the thread. I never refreshed the thread so it stayed even when you edited it. This specific scenario is where you need to be able to create triggers at run-time, which GUI just can't do (but JASS can). In the absence of that option the way you have described and shown it above is a reasonable solution in my opinion... though it is decidedly weird that the eventless triggers don't work without the integer loop. I can see no logical reason for that. You could do it entirely with two triggers if you wanted to, though, with better usage of the unit's CV forming a kind of list:

Imagine you put all the points into one array, with indices 1-7 being for maze 1, 8-14 for maze 2, 15-21 for maze 3, etc. When you spawn a unit you set its CV = (7*(maze # it's in - 1) + 2) and order it to move to Points[CV]; you need to order it to move when spawned because its CV won't be set and the move trigger wont order it properly but it will fire immediately upon the unit being spawned, interrupting the current trigger in the process. When a unit enters any of those regions, increase its CV by 1 and again order it to move to Point[CV]. You will need a separate trigger so that units are removed/killed instantly upon entering the end regions ("TO HELL" makes me think they do get removed); the unit is in maze # CV/7.

  • Events
    • Time - Elapsed game time is 0.10 seconds
  • Conditions
  • Actions
    • For each (Integer A) from 0 to 11 do (Actions) //note the array bounds here
      • Loop - Actions
        • Set S = (7 x (Integer A))
        • Set Points[S+1] = Center of Cspawn[S]
        • Set Points[S+2] = Center of Cr1[S]
        • Set Points[S+3] = Center of Cr2[S]
        • Set Points[S+4] = Center of Cr3[S]
        • Set Points[S+5] = Center of Cr4[S]
        • Set Points[S+6] = Center of Cr5[S]
        • Set Points[S+7] = Center of Cend[S]
        • -------- --------
        • Trigger - Add to MOVE TRIGGER the event (Unit - A unit enters Cspawn[S])
        • Trigger - Add to MOVE TRIGGER the event (Unit - A unit enters Cr1[S])
        • Trigger - Add to MOVE TRIGGER the event (Unit - A unit enters Cr2[S])
        • Trigger - Add to MOVE TRIGGER the event (Unit - A unit enters Cr3[S])
        • Trigger - Add to MOVE TRIGGER the event (Unit - A unit enters Cr4[S])
        • Trigger - Add to MOVE TRIGGER the event (Unit - A unit enters Cr5[S])
        • Trigger - Add to HELL TRIGGER the event (Unit - A unit enters Cend[S]) //these end regions get their own trigger
  • Events
    • -------- move trigger --------
  • Conditions
    • (Custom value of (Triggering Unit)) not equal to 0
  • Actions
    • Set U = (Triggering Unit)
    • Set CV = (Custom value of U) + 1
    • Unit - Set custom value of U to CV
    • Unit - Order U to Move to Points[CV]
  • Events
    • -------- hell trigger --------
  • Conditions
  • Actions
    • Set U = (Triggering Unit)
    • Set MazeNumber = (Custom value of U)/7 //integer division truncates after the first digit, but this should always be 7/7 or 14/7 or 21/7, etc. anyway.
    • Unit - Explode U
    • Set PlayerLives[MazeNumber] = PlayerLives[MazeNumber] - 1 //or whatever you do

How taxing is it to detect an event and then check a condition?
I've always heard that triggerconditions are specifically extremely efficient so there can be many evaluations without issue.
The execution is terminated at the condition returning false. Therefore, whether it contains 0 or 100 actions, it doesn't really matter in this case.
You are correct. However, what is being evaluated in the conditions is also relevant. It is unlikely to actually matter for performance, but having a massive/complex condition or a whole bunch of them in theory could. While you are also correct that the conditions all evaluate to booleans, many of the functions used in in the evaluation are not (for example something like "number of units in (units in region matching...)" both leaks a group and is way more than just boolean comparisons). Food for thought; I would say don't worry about it unless your conditions are very long or very numerous.
 
Last edited:
Level 8
Joined
May 21, 2019
Messages
435
though it is decidedly weird that the eventless triggers don't work without the integer loop. I can see no logical reason for that.
Yeah, me neither. It kind of seems like a syncronization error of sorts. The trigger isn't eventless though. The events of "A unit enters region" is added like in your example. I think it loses track of the triggering unit as 12 units trigger the trigger at the exact same time. Otherwise, it'd make perfect sense that a triggering unit entering any given region in cr3[] would go to cr4[custom value of triggering unit]. It may be a problem with how the custom value is assigned. It is assigned when the unit enters the spawn, and the first move command is delayed with a 0.2s wait to ensure that the value is actually set. It seemingly works fine, but it may be the reason why some units think that they are in a different maze when they spawn without the interger loop. I could try a different approach to assigning custom values and see if that works.

You could do it entirely with two triggers if you wanted to, though, with better usage of the unit's CV forming a kind of list
Pretty good suggestion. But I think you hit the nail on the head with the custom value being the part of the synchronization that fucks up. I will try messing with that first, and assign the value when the unit is being created instead of when it arrives. If that's the root of the issue, my move trigger can literally just be a single action (go to nextpoint[CV]). If that doesn't work, I might use your other example here.


You are correct. However, what is being evaluated in the conditions is also relevant.
I know :)
I said so myself in the next part:
the overall load is pretty definitive, if you exclude boolean computations.
Then again, it's totally fair to not know what this means or missing it.

Thanks for your input, it really gets the ball rolling! :)
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
Yes I know the events are added. Give me a little credit... I actually did read your code.

It is unlikely Triggering Unit is in any way being overwritten. It’s the most reliable event response and even works after a wait without being overwritten (some do not).

There should be no need to include a wait before ordering the move. As I said above a trigger doing something that fires another trigger actually instantly pauses the first and fully runs the secondary trigger (or until that trigger hits a wait) before returning to the original trigger and resuming its execution. This behavior, in fact, often results in errors where a trigger using an Integer A/B (both global variables) loop causes another trigger to fire that also uses an A/B loop which changes the loop bounds and current value, messing up the rest of the first trigger’s loop. Perhaps if you are creating units sequentially in a loop (with or without waits) this could be relevant and might explain the loop solution you used.
 
Level 8
Joined
May 21, 2019
Messages
435
Yes I know the events are added. Give me a little credit... I actually did read your code.
Yeah, I thought that you had just forgotten a minor detail, which would be fair, I mean, this conversation is taking place over several days.

It is unlikely Triggering Unit is in any way being overwritten. It’s the most reliable event response and even works after a wait without being overwritten (some do not).
Yeah, it doesn't really make sense, I will try to poke and prod it and update if I figure out what was up.

There should be no need to include a wait before ordering the move. As I said above a trigger doing something that fires another trigger actually instantly pauses the first and fully runs the secondary trigger (or until that trigger hits a wait) before returning to the original trigger and resuming its execution.
That's very useful information. Thanks.

This behavior, in fact, often results in errors where a trigger using an Integer A/B (both global variables) loop causes another trigger to fire that also uses an A/B loop which changes the loop bounds and current value, messing up the rest of the first trigger’s loop. Perhaps if you are creating units sequentially in a loop (with or without waits) this could be relevant and might explain the loop solution you used.
Yeah, I learned that Int A and B are global the hard way. The loop that spawns waves is sort of a fake loop that uses an IF statement to rerun itself until a variable increments to a certain point, so it should be resistant to variable conflicts. I kinda made it before realizing that the game has a built in loop function (lol), but I might wanna look into this one too. I plan on checking some of this later today.
 
Status
Not open for further replies.
Top