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

[Solved] Trigger Efficiency

Status
Not open for further replies.
Hello : )

I am teleporting a unit (UNIT) at the center of gravity of a unit group (between 10 and 20 units per group), but my trigger is not very efficient and brings lags.
My guess is that even with JASS loops, that many calculus every 0.03s seems too much for warcraft to handle and it wouldn't help much... but maybe am I wrong :)

Note : there can be 60+ UNITs that must find the center of gravity of their 20 units....

full



Well sorry, there is a bit of french in there ; )


If you find any other kind of way I could manage the UNIT around, that would be awesome ; )


Regiments in picture : (the soldiers are not selectable Chaos/Locust units)

full



Thanks for checking the forums !
 
Last edited:
Do you use JassHelper (which alloes vjass and is included in JNGP) ?
Jass could do some faster indeed, in case as you say, there can be 60+ units in a group.
Because for each unit inside group the "PickEveryUnitAndDoActions" function makes a new explicit function call.
In JASS you can do some nice code to avoid this extra function call, when you convert the enumeration into a custom loop. So 60 extra function calls*AmountOfGroups vs. 0 extra function calls is definitly more heavy.

I'm not sure it makes much sense to calculate the middle angle, and maybe just going with the angle of one unit is ok.
I mean, if all angles are uniform, result will be the same. If all angles are different, in a fight maybe, then the result may look very random anyways, so it doesnt really matter.

^That would be the biggest changes I could see. There are some micro optimization

s, but which should not really matter in result here:

You don't need to convert integer to real, if you directly used a "real" counter.

A custom integer variable could be used instead of "Ineteger A" loop, as getting "Integer A" in loop will always result in an extra function call.

If jass is used, no "Point" variable is needed, and we could directly work with given x/y coordinates and SetUnitX/Y natives, which are also slightly faster.

==

What could be very important, too, but isn't clear out of context, is that since also the groups are so large, it's very important that they are recycled properly.
So no extra operations are done for a invalid or dead regiment no more.

==

The calculation itself for getting middle values seems fine.

If you find any other kind of way I could manage the UNIT around, that would be awesome ; )
Example : (the soldiers are not selectable Chaos/Locust units)
What do you mean?

The picture looks good.:)
 
Do you use JassHelper (which alloes vjass and is included in JNGP) ?
Jass could do some faster indeed, in case as you say, there can be 60+ units in a group.
Because for each unit inside group the "PickEveryUnitAndDoActions" function makes a new explicit function call.
In JASS you can do some nice code to avoid this extra function call, when you convert the enumeration into a custom loop. So 60 extra function calls*AmountOfGroups vs. 0 extra function calls is definitly more heavy.
Yes of course I use JNGP : )
And ow, that's why I couldn't find a clear equivalent as a function of PickEveryUnitInUnitGroup in JASS (and a GetPickedUnit() )

Well, I guess I could try in JASS then, it shouldn't be complicated, but I still think that calculating the center of gravity of 60+ groups of 20 units every 0.03s would make it lag ^^
It would make 20*60/0.03 * 2(X and Y) = 80 000 additions per second

I'm not sure, but I guess it would be a little too much ? Is there any way to evaluate the "performance" of such calculation ?


I'm not sure it makes much sense to calculate the middle angle, and maybe just going with the angle of one unit is ok.
I mean, if all angles are uniform, result will be the same. If all angles are different, in a fight maybe, then the result may look very random anyways, so it doesnt really matter.


That's very true ! I actually thought of doing the same with the position (of the most central unit in a regiment which can be found thanks to how the regiments are set up) so it would pick one location directly (and it doesn't really matter if the flag is not exactly in the center...)


You don't need to convert integer to real, if you directly used a "real" counter.

A custom integer variable could be used instead of "Ineteger A" loop, as getting "Integer A" in loop will always result in an extra function call.

Ow that's very true ^^ I'm lazy sometimes. And I didn't know about the difference between IntegerA and with a standard variable "for loop", thanks !

If jass is used, no "Point" variable is needed, and we could directly work with given x/y coordinates and
SetUnitX/Y
natives, which are also slightly faster.
Good idea ! I wonder why they didn't make it in GUI ... seemed a pretty obvious one to "translate"

What could be very important, too, but isn't clear out of context, is that since also the groups are so large, it's very important that they are recycled properly.
So no extra operations are done for a invalid or dead regiment no more.

Oh yes, no worries for that ;) but it was worth mentioning.

What do you mean?

The picture looks good.:)
Well, I meant that if anyone had a different idea to manage the placement of the flag (with movement order or anything really, it would have been intersting :)
And then Example is to explain the picture and how it works unit-wise

Thhaaaaaankks a lot for the very complete and quick answer !
 
It looks like you're making some sort of black hole spell. It would be more efficient to write it in jass directly indeed, but I have a suggestion:

You could try using structs here:

If you are not quite acquainted with the concept of structs, you can use a global integer for the index and arrays for its' elements.
You pause the timer when the number of instances is greater than 0 and upon expiration, you will have to loop over all individual instances
and check if they are active, then do the corresponding actions.

So the baseline of the code would look like this:

JASS:
globals
    private integer arr_Index = 0
    private timer arr_Index_globalTimer = CreateTimer()
    private group array arr_Index_Group
    private boolean array arr_Index_Check
endglobals

//    Iteration part...
private function onExpire takes nothing returns nothing
    local integer i = 1
    loop
        //  do your actions here
        /* if done, then
            set element[index_thing] = element[arr_Index]
            set i = i - 1
            set arr_Index = arr_Index - 1
        */
        exitwhen i >= arr_Index
        set i = i + 1
    endloop
    if arr_Index == 0 then
        call PauseTimer(arr_Index_globalTimer)
    endif
endfunction

private function onRegister takes nothing returns nothing
    /*
    set someData[arr_Index] = dataField
    */
    if arr_Index == 0 then
        call TimerStart(arr_Index_globalTimer, 0.03, true, function onExpire)
    endif
    set arr_Index = arr_Index + 1
endfunction

About how the naming goes, it is up to you.
Structs are actually arrays.

For more information about indexing, see:
Dynamic Indexing
 
Last edited:
The group loop method used is from here vJass Optimization: Using a First of Group Loop for Enumeration, last chapter.

Try this code:
JASS:
globals
    group Group = CreateGroup()
    group Temp
endglobals

function Trig_Test_Actions takes nothing returns nothing
    local integer i
    local unit u
    local real x
    local real y
    local integer counter
    local integer hashKey
    local group group_regriment
  
    set i = 1
    loop
        exitwhen i > udg_NumberOfKeysRegriments
      
        set hashKey = Key[i]
        set counter = 0
        set x = 0
        set y = 0
        set group_regriment = LoadGroupHandle(udg_REGRIMENTS, 1, hashKey)
      
        call SetUnitFacing(LoadUnitHandle(udg_REGRIMENTS, 1, hashKey), GetUnitFacing(FirstOfGroup(group_regriment)))
        loop
            set u = FirstOfGroup(group_regriment
            exitwhen u == null
          
            set x = x + GetUnitX(u)
            set y = y + GetUnitY(u)
            set counter = counter + 1
          
            call GroupAddUnit(Group,u)
            call GroupRemoveUnit(group_regriment,u)
        endloop
      
        set Temp = group_regriment
        set group_regriment = Group
        set Group = Temp
      
        call SetUnitX(flag, x/counter)
        call SetUnitY(flag, y/counter)
      
        set i = i + 1
    endloop
    set group_regriment = null
endfunction

//===========================================================================
function InitTrig_Test takes nothing returns nothing
    set gg_trg_Test = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_Test, 0.03 )
    call TriggerAddAction( gg_trg_Test, function Trig_Test_Actions ) 
endfunction

Replaye the "Test" name in functions and trigger with your trigger sheet name.

edit:

And ow, that's why I couldn't find a clear equivalent as a function of PickEveryUnitInUnitGroup in JASS (and a GetPickedUnit() )
There exists it in JASS, too.
ForLoop() loops through a unit group.
GetEnumUnit() == (PickedUnit)
 
It looks like you're making some sort of black hole spell. It would be more efficient to write it in jass directly indeed, but I have a suggestion:

You could try using structs here:

If you are not quite acquainted with the concept of structs, you can use a global integer for the index and arrays for its' elements.
You pause the timer when the number of instances is greater than 0 and upon expiration, you will have to loop over all individual instances
and check if they are active, then do the corresponding actions.

So the baseline of the code would look like this:

JASS:
globals
    private integer arr_Index = 0
    private timer arr_Index_globalTimer = CreateTimer()
    private group array arr_Index_Group
    private boolean array arr_Index_Check
endglobals

//    Iteration part...
private function onExpire takes nothing returns nothing
    local integer i = 1
    loop
        //  do your actions here
        /* if done, then
            set element[index_thing] = element[arr_Index]
            set i = i - 1
            set arr_Index = arr_Index - 1
        */
        exitwhen i >= arr_Index
        set i = i + 1
    endloop
    if arr_Index == 0 then
        call PauseTimer(arr_Index_globalTimer)
    endif
endfunction

private function onRegister takes nothing returns nothing
    /*
    set someData[arr_Index] = dataField
    */
    if arr_Index == 0 then
        call TimerStart(arr_Index_globalTimer, 0.03, true, function onExpire)
    endif
    set arr_Index = arr_Index + 1
endfunction

About how the naming goes, it is up to you.
Structs are actually arrays.

For more information about indexing, see:
Dynamic Indexing

Well, I don't have any problem of indexing (and yes, indexing can be done in GUI). As you can see in the first picture, the loop only loop "NumberOfKeysRegiments" times, which is the number of instances to make. Each time a "Regiment" is created, or dies, the number of regiments is calculated again, and their Key (getUnitID( )) is stored in the array Key[ ].
I think it works perfectly (perhaps calculating everything again has less performance, but it's like storing 60 integer (max), and rarely.

If I didn't understand your post right, tell me ^^ (I didn't go too much in depth into it with the timer thingy)


The group loop method used is from here vJass Optimization: Using a First of Group Loop for Enumeration, last chapter.

Try this code:

Thanks you so much for taking the time to do that!

I just have a few questions :
JASS:
set group_regriment = LoadGroupHandle(udg_REGRIMENTS, 1, hashKey)

Are the two integers from hashtables inverted in JASS ? (in GUI, it would be LoadGroupHandle(udg_REGIMENTS,Hashkey, 6), with how I stored my stuff in REGIMENTS hashtable)


EDIT : ^^ Sorry, I'm stupid I understood now what is below :

((((

JASS:
            call GroupAddUnit(Group,u)
            call GroupRemoveUnit(group_regriment,u)
        endloop
 
        set Temp = group_regriment
        set group_regriment = Group
        set Group = Temp
I'm not quite sure I understand the end of the loop:
-So each time you get the X and Y of a unit, you add it to "Group" and remove it from "group_regiment" in order to be able to pick a different "first unit of group" and still store the all the units under "Group".
-Then, when it's done, you store again the units under "group_regiment" and then empty "Group"

What's the purpose of all this ? because at the next loop iteration, you'll set "group_regiment" again (set group_regiment = LoadGroupHandle(udg_REGIMENTS, HashKey, 6) )

I would simply use group_regiment and null it at the end as you did.

Or maybe I don't know much about unit groups as local variables and tweaking it would, in our case modify the group "LoadGroupHandle(udg_REGIMENTS, HashKey, 6)" ??

))))





EDIT 2:
Well, here is my code, but warcraft crashes, any idea what is wrong in there ?
JASS:
globals
    private Group = CreateGroup()
    private Temp
endglobals
function Trig_CenterOfGravity_Actions takes nothing returns nothing
    local integer i
    local unit u
    local real x
    local real y
    local integer counter
    local integer hashKey
    local group group_regiment
 
    set i = 1
    loop
        exitwhen i > udg_NumberOfKeysRegiments
   
        set hashKey = udg_Key[i]
        set counter = 0
        set x = 0
        set y = 0
        set group_regiment = LoadGroupHandle(udg_REGIMENTS, hashKey, 6)
   
        call SetUnitFacing(LoadUnitHandle(udg_REGIMENTS, hashKey, 1), GetUnitFacing(FirstOfGroup(group_regiment)))
        loop
            set u = FirstOfGroup(group_regiment)
            exitwhen u == null
       
            set x = x + GetUnitX(u)
            set y = y + GetUnitY(u)
            set counter = counter + 1
       
            call GroupAddUnit(Group,u)
            call GroupRemoveUnit(group_regiment,u)
        endloop
   
        set Temp = group_regiment
        set group_regiment = Group
        set Group = Temp
   
        call SetUnitX(LoadUnitHandle(udg_REGIMENTS, hashKey, 1), x/counter)
        call SetUnitY(LoadUnitHandle(udg_REGIMENTS, hashKey, 1), y/counter)
   
        set i = i + 1
    endloop
    set group_regiment = null

endfunction

//===========================================================================
function InitTrig_CenterOfGravity takes nothing returns nothing
    set gg_trg_CenterOfGravity = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_CenterOfGravity, 0.03 )
    call TriggerAddAction( gg_trg_CenterOfGravity, function Trig_CenterOfGravity_Actions )
endfunction

Here's my map just in case it comes from my JNGP
http://www.hiveworkshop.com/pastebin/76dbe57b3dfcdb0eb794e27ca1371a429948/
The trigger is under the trigger folder "Battle"
If you want to try the trigger on the map, pick Roma after the beggining. Then use the hero "diplomacy" and declare war on Carthage. Then attack a carthaginian city with your first hero. You can then move your "flags" on a battlefield which location is pinged on the minimap :)
 
Last edited:
Are the two integers from hashtables inverted in JASS ? (in GUI, it would be LoadGroupHandle(udg_REGIMENTS,Hashkey, 6), with how I stored my stuff in REGIMENTS hashtable)
Yes, you made ir right, and I took also the wrong number.^^
not quite sure I understand the end of the loop:
Ehm, have a look into the linked tutorial.
The default situation is that for looping through groups we use ForGroup() function (GUI also will use it), and it will call a "callback function" for each single unit, where you then will use (PickedUnit) or GetEnumUnit(). But in JASS we also can do a trick so we can directly loop through our group, without the need of an other callback function.
The method how to make this loop is explained in the tutorial.
The negative point is, that the method empties your group from all units, but we don't want that here. So we use an other help group to manage it properly, it's explained in the last chapter.

For the crash-- there is a small mistake in the "globals" block. I'm sure you will see. ;)
All seems to work for me now, but havent tested with big regiments.

The code seems fine now, but also as said, just ensure also to deindex the regiments when they are not valid/alive anymore, so the trigger doesnt loop through them, too.
And if there are 0 valid regiments at a moment, the periodic trigger might be even turned off, so it doesn't run for nothing.
 

Attachments

  • Rome II Total War3.1.w3x
    8.6 MB · Views: 56
For the crash-- there is a small mistake in the "globals" block. I'm sure you will see. ;)
All seems to work for me now, but havent tested with big regiments.
Ow, yes stupid mistake ^^
Actually, the crash seems to come from my editor. If I don't change anything, but save again the map you sent me, it crashes again... I am using NewGen WE (UMS 5.0)
I thought it contained vJASS, but perhaps not ^^

The code seems fine now, but also as said, just ensure also to deindex the regiments when they are not valid/alive anymore, so the trigger doesnt loop through them, too.
And if there are 0 valid regiments at a moment, the periodic trigger might be even turned off, so it doesn't run for nothing.
Yep !

Well, I'll try to fix my editor problem, and it should work just fine !


Thanks a lot for the dedicated help, sir!
 
Ehm, looks right. But there is a new "feature" that comes with using vjass. xD
Each time when there is editor content interaction, like only creating a new GUI trigger, or simply clicking on an action/condition what ever, or also playing a new unit on the terrain... each time before you can run the map you need to press "save" before, so the vjass compiled properly to normal jass. And after "save" you should be able to test the map.

If it doesn't help-- what error does occur?
 
Well, I was already saving before testing.
Have you tried again after?
Because what you explain is typical behavour for not properly compiled map.
Right before you test the map press save, and then test button. No actions in between. Try with attached
 

Attachments

  • Rome II Total War3.1 (1).w3x
    3 MB · Views: 106
Well, I guess Save As doesn't work ^^
I'm a dummy ... Thanks : )

Well, it "kinda" works ^^
I had to order the handles like this :
JASS:
        set flag = LoadUnitHandle(udg_REGIMENTS, 1, hashKey)
        set group_regiment = LoadGroupHandle(udg_REGIMENTS, 6, hashKey)

But the (big) problem is that the groups seem to swap around :
for each iteration of the trigger, it seems that the units of REGIMENTS(6 , Key[1]) is loaded in the next one (REGIMENTS(6, Key[2])) etc... and even REGIMENTS(6 , Key[NumberOfKeys (the last one)]) is loaded in the first one. So the flags swap around each regiments, and is teleported in the center of each ^^ It is funny, but not what is supposed to happen ^^

There seems to be a problem in the anti-leak/group recuperation system.
 

Attachments

  • Rome II Total War3.1.w3x
    8.6 MB · Views: 94
Last edited:
Yes, this might be true, I think I made a mistake. The "problem" with using this method is that it switches handles, and the origin group will be used as backupgroup for the next time iterating.
But as we only load groups from hashtables, the handle from hashtable still remains the same, and does not get updated.
To solve this, try to save the group again into hashable after these lines:
JASS:
set Temp = group_regiment
set group_regiment = Group
set Group = Temp
 
Yes, that's what I've done ! And it works !

Thanks a lot for everything. Slight problem though, it doesn't face the unit nicely sometimes, I couldn't say why... (like +-180° from what it should be). Then, the regiment is given an order again, it goes back to truely facing the first unit of the group.

But well, everything is nice and clean :) Ty !


EDIT: Actually, I think it is facing 0° sometimes, which is pretty weird ...


Quick question about clearing a hashtable :
If I wrote my hashtable like this :
Capture.PNG

Meaning the key in the first stuff (which is child key ?) and then an integer (as parent key ?)
Does it clear it nicely if I do this : ?
Clear.PNG

(knowing that : integers, reals, units, unit groups and booleans are stored in there)

Or it wont work because I should have saved my values in a Hashtable with FIRST an integer, and THEN the key ? (in GUI, cause the order seems inverted in JASS)
 
Last edited:
Only the angle or also position is wrong? If only the angle, then I have no idea. Try to print game messages with the name of the unit, or so, and check if values are correct always.

Yes you are right, first child key and then parent key in GUI, and in the "jass version" (without BJ) it's other way around. You can check such things very easy if you convert GUI to jass:

Convert wanted function to jass and then click on "Function List":
one-png.260741


Search for your function and look what it does inside:
two-png.260742


the GUI functions often change order or so and then just call a normal jass native function internaly.
 

Attachments

  • one.PNG
    one.PNG
    19.4 KB · Views: 159
  • two.PNG
    two.PNG
    14 KB · Views: 127
Yes, only the angle :
It proceeds this way :
-The angle remains nice and the same as long as you don't give any order
-If you send an order to move from a position to the left of it, the regiment moves nicely, the flag follows remaining at the center of gravity, but "slowly" turns itself to an angle of 0° (on the right). Then remains at 0° when the regiment stops. If you give an order to move to the left (just a little bit more), the flag faces as the unit n°1 and remains still and nice.

I think it might be related to the order given (but it would be weird since all orders are stopped if a unit is teleported; maybe not when defining its X and Y though (and not a point); and why 0° ????)
This above doesn't always occur ... which is even weirder.
Perhaps I should check my "Move Regiment" trigger, but it doesn't change the unit order in the unit group whatsoever.

Maybe it faces 0° because it cannot get the unit facing ? But it's still weird ...

Well nvm, I'll find out some trick I'm sure ^^


But for my last question, can I use the Clear Hashtable ? Or should I remake my hashtable integer ordering to be able to use it ? And does it clear nicely the listed variable types ? (no leak)

Ow yeah I just saw BJs change ordering ...
 
Try to debug it, my guess is that an invalid unit is referenced. You could also print out the angle that is used.

can I use the Clear Hashtable ?
Yes.
Or should I remake my hashtable integer ordering to be able to use it ?
Hm, I'm not sure I understand.
And does it clear nicely the listed variable types ? (no leak)
It does clear the hashtable nicely, but it doesn't care for stored entries' content.
For example if you save a group in a hashtable and then flush the hashtable, then the entry will be free again, but the group per se will still exist.
 
Hm, I'm not sure I understand.
Let me explain my question:
Let's say a hashtable is an excel sheet (which it pretty much is)
When entering (in GUI), the coordinates, the cell in which I wanna store values, let's say the first coordinate is the Line, and the second one the Column.

As I used UnitIDs as a keys for my "flag units", the key would be the number of the line.

My question is : Does using ClearHashtable(Key) remove all data from line#key, or from column#key ??

(i just saw yesterday there was also a BJFlushHashtable, perhaps is it the thing to use to clear a "line" instead of a "column"?)



For example if you save a group in a hashtable and then flush the hashtable, then the entry will be free again, but the group per se will still exist.
Ouch okay, so I first destroy all the things that leak and then clear. Roger !
Should I also Null the Reals/Integers to save some octets or does Flushing do it for us?
 
Does using ClearHashtable(Key) remove all data from line#key, or from column#key ??
parent key, so in BJ (GUI) it's 2nd parameter, and in native function first key parameter. I could see that be converting to jass. ;)
first destroy all the things that leak and then clear
Yes, and if you don't need the groups or such anymore of course. But no, there exists something like "RemoveSavedInteger" and such, but if have many entries you just can use flush, and it will result in same.
 
Status
Not open for further replies.
Top