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!
I have been working on a collision detection system for a knock-back system I'm working on. The trouble I am having is with the triggers that map the terrain walkability.
Originally, I had two loops, one nested inside the other - one representing the Y position of the map nested inside one representing the X position of the map. I quickly learned this wouldn't work, I assume because of the OP limit since the whole thing increases squarely... that problem seems obvious.
I decided to turn the X loop into a timed loop and the Y loop remained a normal integer loop that loops each time the periodic loop moves. To accommodate large maps, I added a the the 'MapSizer' and 'MapSection' variables to limit the size of the Y loop and increase the time it takes to load.
The choice of scanning the map on a 16x16 grid is a lot of the problem I understand and I've been aware that I am pushing it with this loop all along, but I'm wondering if there is something I am overlooking that would make this a lot more realistic.
The issue as it stands is the time it takes to execute this loop; all in all, it's over 5 minutes for this loop to run on a 256x256 size map and there is a second loop that follows the same timing to refine the hashtable seen in this loop, so whatever time I achieve is doubled.
On a very small map it is reasonable but otherwise it's hard to imagine somebody waiting so long to play a game..
Any advise would be much appreciated because the remainder of the system works great, but the wait is way too long.
I should also say, I was hoping to build this entirely in GUI...
Thanks in advance for taking the time!
Initialization
Events
Time - Elapsed game time is 1.00 seconds
Conditions
Actions
Set VariableSet MapLoopX = 0
Set VariableSet MapLoopY = 0
Set VariableSet MapSizeX = ((Integer(((Max X of (Entire map)) - (Min X of (Entire map))))) / 16)
Set VariableSet MapSizeY = ((Integer(((Max Y of (Entire map)) - (Min Y of (Entire map))))) / 16)
Set VariableSet MapSizer = (MapSizeY / 128)
Hashtable - Create a hashtable
Set VariableSet TerrainHash = (Last created hashtable)
Trigger - Turn on TerrainInit Loop <gen>
The reason why the hashtable is an integer and not a boolean is that I have a second trigger (TerrainInitLoopB) that loops to assign the other values, 1,2, or 3, when necessary
TerrainInitLoopA (Initially Off)
Events
Time - Every 0.01 seconds of game time
Conditions
Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
MapLoopX Less than MapSizeX
Then - Actions
For each (Integer MapLoopY) from 0 to ((MapSizeY / MapSizer) - 1), do (Actions)
Loop - Actions
Hashtable - Save 0 as MapLoopX of ((MapSection x (MapSizeY / MapSizer)) + MapLoopY) in BuildingHash.
Hashtable - Save 0 as MapLoopX of ((MapSection x (MapSizeY / MapSizer)) + MapLoopY) in DestructiblesHash.
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Terrain pathing at (Point(((Min X of (Entire map)) + (Real((8 x ((2 x MapLoopX) + 1))))), ((Min Y of (Entire map)) + (Real((8 x ((2 x ((MapSection x (MapSizeY / MapSizer)) + MapLoopY)) + 1))))))) of type Walkability is off) Equal to True
Then - Actions
Hashtable - Save 4 as MapLoopX of ((MapSection x (MapSizeY / MapSizer)) + MapLoopY) in TerrainHash.
Else - Actions
Hashtable - Save 0 as MapLoopX of ((MapSection x (MapSizeY / MapSizer)) + MapLoopY) in TerrainHash.
Set VariableSet MapLoopX = (MapLoopX + 1)
Else - Actions
Set VariableSet MapLoopX = 0
Set VariableSet MapSection = (MapSection + 1)
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
Doing it in GUI is a big reason you hit the OP limit more easily.
Every trigger action is at least one operation minimum (ignoring nested calls, because that's totally a thing GUI makes you do and absolutely contributes here), but often GUI functions are wrappers that just call a native directly with a different order for arguments, set some global variables, or do an integer loop automatically for you.
If conditions are all a series of annoying nested boolean functions.
Referring to things like "last created unit" and "entire map" are function calls that returns a global variable (instead of just being able to reference the variable directly).
These all contribute unnecessary operations that could potentially be avoided by writing this crucial part of your script in JASS or Lua. Your best bet to keeping this GUI only is to go through and assign a bunch of global variables to things that you will repeatedly use in the loop. The ones I see initially are Min/Max X/Y of the entire map. Also eliminating the need to create a point at each step of the loop would be large savings. This would be done by checking the pathability using the native directly, which uses XY coordinates instead of a point.
And uh, every point you test the pathability of currently leaks memory. Big yikes considering how many times these loops run!
There's a bunch of things that don't make much sense to me.
You are making pathing system, but you check pathing against "Entire map" - unless you are not using any map bounds (in which case "Entire map" could make sense) you should use "Playable map area". As an example, the 64x64 map has by default a 52x52 playable area, the rest are the blacked-out edges of the map. So you get a decrease of size from length 8192 (for map size 64x64) to 6656 (as map size 52x52).
The smallest pathing cell is 32x32 units big, yet in your initialization trigger you divide map size into 16-unit long cells and in the loop you check 8-unit long cells. The y-coordinate also seems fishy (or maybe just made too complex for no reason)
Checking terrain pathing does not check for unit collision and I think also does not check for player buildings (which is why a trick with item placing is used)
Anyway, what is this even needed for? You write you want to make pathing system, but what is the use for you pathing system?
Basically, what do you need to know the pathing of the entire map for? Perhaps there are some better approaches to resolve what you want to achieve
There's a bunch of things that don't make much sense to me.
You are making pathing system, but you check pathing against "Entire map" - unless you are not using any map bounds (in which case "Entire map" could make sense) you should use "Playable map area". As an example, the 64x64 map has by default a 52x52 playable area, the rest are the blacked-out edges of the map. So you get a decrease of size from length 8192 (for map size 64x64) to 6656 (as map size 52x52).
The smallest pathing cell is 32x32 units big, yet in your initialization trigger you divide map size into 16-unit long cells and in the loop you check 8-unit long cells. The y-coordinate also seems fishy (or maybe just made too complex for no reason)
Checking terrain pathing does not check for unit collision and I think also does not check for player buildings (which is why a trick with item placing is used)
Anyway, what is this even needed for? You write you want to make pathing system, but what is the use for you pathing system?
Basically, what do you need to know the pathing of the entire map for? Perhaps there are some better approaches to resolve what you want to achieve
Thanks for the input, there is a lot I am withholding for the sake of not complicating the question I had here.
I use the "Entire Map" because it will always be a multiple of 32 which guarantees that I can divide the number by 2 up to 5 times no matter what size the map may be.
I'm aware that the smallest pathing cell is a 32x32, but when it comes to the units that will use this pathing there is another element to consider.
When it comes to interacting with pathability, there are effectively 4 unit sizes:
Collision Size of 0.00-15.99 is on a 32x32 cell.
Collision Size of 16.00-31.99 is on a 64x64 cell.
Collision Size of 32.00-47.99 is on a 96x96 cell.
Collision Size of 48.00+ is on a 128x128 cell.
On the 32x32 and 96x96 cell, the unit is centered, but on the 64x64 and 128x128 they are offset by +(16,16) of center
After adjusting the equation to make them all centered, this distance from center to the outer edge of each unit is 16,32,48,64 meaning in some situations, the detection will fail and there are two choices that I found are this:
Make a loop that checks points every 32 units around each unit that is being knocked-back, this wouldn't be impossible, but would require a loop in the middle of the knock-back periodic trigger (have not posted) so I didn't go with this method first.
Make the grid store an integer 0-4, 4 meaning nothing can be thrown there, 3 meaning units up to collision size 15.99 can be thrown there, 2 meaning units up to collision size 31.99 can be thrown there, et c down to 0 meaning any unit of any size can be thrown through that cell. Having this, now I check the collision of X+16, X-16, Y+16, and Y-16 from the unit that is being knocked back.
If I don't do one of these two things, then if the unit's corner passes over an unwalkable area, the unit will slide until it clips one of the 4 points and it just doesn't look as clean. There are many potential work arounds, especially if I tailor it to fit one map, but I'm holding out to make a more universal system. I'll share what I have here: Let me just say, I have a lot of work to do with these, I know, but here it is so that you can better understand the system.
Please keep in mind that I am aware I have many leaking issues and lengthy formulas, my intention is to deal with these once I have a running system and to test it on a 32x32 map most often in order to forgive the leaks for the sake of testing
TerrainInitLoopB - (Initially Off)
Events
Time - Every 0.01 seconds of game time
Conditions
Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
MapLoopX Less than MapSizeX
Then - Actions
For each (Integer MapLoopY) from 0 to ((MapSizeY / MapSizer) - 1), do (Actions)
Loop - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Load MapLoopX of (((MapSizeY x MapSection) / MapSizer) + MapLoopY) from TerrainHash.) Equal to 4
Then - Actions
If ((Load (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) from TerrainHash.) Less than 4) then do (Hashtable - Save 3 as (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) from TerrainHash.) Less than 4) then do (Hashtable - Save 3 as (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) from TerrainHash.) Less than 4) then do (Hashtable - Save 3 as (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) from TerrainHash.) Less than 4) then do (Hashtable - Save 3 as (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) from TerrainHash.) Less than 4) then do (Hashtable - Save 3 as (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) from TerrainHash.) Less than 4) then do (Hashtable - Save 3 as (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) from TerrainHash.) Less than 4) then do (Hashtable - Save 3 as (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) from TerrainHash.) Less than 4) then do (Hashtable - Save 3 as (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) in TerrainHash.) else do (Do nothing)
Else - Actions
Set VariableSet MapLoopX = (MapLoopX + 1)
Else - Actions
Set VariableSet MapLoopX = 0
Set VariableSet MapSection = (MapSection + 1)
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
MapSection Equal to MapSizer
Then - Actions
Set VariableSet MapSection = 0
Trigger - Turn off (This trigger)
Trigger - Turn on TerrainInitLoopC <gen>
Else - Actions
TerrainInitLoopC - (Initially Off)
Events
Time - Every 0.01 seconds of game time
Conditions
Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
MapLoopX Less than MapSizeX
Then - Actions
For each (Integer MapLoopY) from 0 to ((MapSizeY / MapSizer) - 1), do (Actions)
Loop - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Load MapLoopX of (((MapSizeY x MapSection) / MapSizer) + MapLoopY) from TerrainHash.) Equal to 2
Then - Actions
If ((Load (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) from TerrainHash.) Less than 2) then do (Hashtable - Save 1 as (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) from TerrainHash.) Less than 2) then do (Hashtable - Save 1 as (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) from TerrainHash.) Less than 2) then do (Hashtable - Save 1 as (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) from TerrainHash.) Less than 2) then do (Hashtable - Save 1 as (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) from TerrainHash.) Less than 2) then do (Hashtable - Save 1 as (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) from TerrainHash.) Less than 2) then do (Hashtable - Save 1 as (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) from TerrainHash.) Less than 2) then do (Hashtable - Save 1 as (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) from TerrainHash.) Less than 2) then do (Hashtable - Save 1 as (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) in TerrainHash.) else do (Do nothing)
Else - Actions
Set VariableSet MapLoopX = (MapLoopX + 1)
Else - Actions
Set VariableSet MapLoopX = 0
Set VariableSet MapSection = (MapSection + 1)
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
MapSection Equal to MapSizer
Then - Actions
Set VariableSet MapSection = 0
Trigger - Turn off (This trigger)
Trigger - Turn on TerrainInitLoopD <gen>
Else - Actions
TerrainInitLoopD - (Initially Off)
Events
Time - Every 0.01 seconds of game time
Conditions
Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
MapLoopX Less than MapSizeX
Then - Actions
For each (Integer MapLoopY) from 0 to ((MapSizeY / MapSizer) - 1), do (Actions)
Loop - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Load MapLoopX of (((MapSizeY x MapSection) / MapSizer) + MapLoopY) from TerrainHash.) Equal to 3
Then - Actions
If ((Load (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) from TerrainHash.) Less than 3) then do (Hashtable - Save 2 as (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) from TerrainHash.) Less than 3) then do (Hashtable - Save 2 as (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) from TerrainHash.) Less than 3) then do (Hashtable - Save 2 as (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) from TerrainHash.) Less than 3) then do (Hashtable - Save 2 as (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) from TerrainHash.) Less than 3) then do (Hashtable - Save 2 as (MapLoopX - 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) from TerrainHash.) Less than 3) then do (Hashtable - Save 2 as (MapLoopX + 0) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) from TerrainHash.) Less than 3) then do (Hashtable - Save 2 as (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY - 1)) in TerrainHash.) else do (Do nothing)
If ((Load (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) from TerrainHash.) Less than 3) then do (Hashtable - Save 2 as (MapLoopX + 1) of ((MapSection x (MapSizeY / MapSizer)) + (MapLoopY + 0)) in TerrainHash.) else do (Do nothing)
Else - Actions
Set VariableSet MapLoopX = (MapLoopX + 1)
Else - Actions
Set VariableSet MapLoopX = 0
Set VariableSet MapSection = (MapSection + 1)
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
MapSection Equal to MapSizer
Then - Actions
Set VariableSet MapSection = 0
Trigger - Turn off (This trigger)
Trigger - Run DestructiblesInit <gen> (checking conditions)
Else - Actions
.
I am holding back the destructible and building pathing initialization for now, only because these triggers are a lot more straight forward and make similar grids through a less demanding process. The following knock-back system would normally check two other hashtables called "BuildingHash" and "DestructiblesHash" by the same method that "TerrainHash" is checked. These Triggers will need to be initialized by another trigger that doesn't exist yet, the idea is that many things might start a knock-back/up and could all share this input system, all you need to do is set a knock-back magnitude, a knock-up magnitude, face the unit the opposite direction you want it to go, add it to the group 'KnockedGroup', and run the trigger 'Knocked'.
Knocked
Events
Conditions
Actions
Unit Group - Pick every unit in KnockedGroup and do (Actions)
Loop - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(((Custom value of (Picked unit)) mod 100) - ((Custom value of (Picked unit)) mod 10)) Not equal to 0
Then - Actions
If ((((Custom value of (Picked unit)) mod 100000) - ((Custom value of (Picked unit)) mod 100)) Equal to 0) then do (Unit - Set the custom value of (Picked unit) to ((Custom value of (Picked unit)) + 90000)) else do (Do nothing)
If (((((Custom value of (Picked unit)) mod 100000) - ((Custom value of (Picked unit)) mod 100)) + (400 + (50 x (((Custom value of (Picked unit)) mod 100) - ((Custom value of (Picked unit)) mod 10))))) Less than 99900) then do (Unit - Set the custom value of (Picked unit) to ((Custom value of (Picked unit)) + (400 + (50 x (((Custom value of (Picked unit)) mod 100) - ((Custom value of (Picked unit)) mod 10)))))) else do (Unit - Set the custom value of (Picked unit) to (((Custom value of (Picked unit)) - (((Custom value of (Picked unit)) mod 100000) - ((Custom value of (Picked unit)) mod 100))) + 99900))
Unit - Set the custom value of (Picked unit) to (((Custom value of (Picked unit)) - (((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 100000))) + 4100000)
If ((Collision Size of (Picked unit)) Less than 48.00) then do (Unit - Set the custom value of (Picked unit) to (((Custom value of (Picked unit)) - (((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 100000))) + 3000000)) else do (Do nothing)
If ((Collision Size of (Picked unit)) Less than 32.00) then do (Unit - Set the custom value of (Picked unit) to (((Custom value of (Picked unit)) - (((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 100000))) + 2100000)) else do (Do nothing)
If ((Collision Size of (Picked unit)) Less than 16.00) then do (Unit - Set the custom value of (Picked unit) to (((Custom value of (Picked unit)) - (((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 100000))) + 1000000)) else do (Do nothing)
Unit - Set the custom value of (Picked unit) to ((Custom value of (Picked unit)) - (((Custom value of (Picked unit)) mod 100) - ((Custom value of (Picked unit)) mod 10)))
Unit - Add Storm Crow Form to (Picked unit)
Unit - Remove Storm Crow Form from (Picked unit)
Unit - Pause (Picked unit)
Animation - Change (Picked unit)'s animation speed to 300.00% of its original speed
Animation - Play (Picked unit)'s death animation
Else - Actions
Trigger - Turn on Knocking <gen>
Knocking - (Initially Off)
Events
Time - Every 0.01 seconds of game time
Conditions
Actions
If ((Number of units in KnockedGroup) Equal to 0) then do (Trigger - Turn off (This trigger)) else do (Do nothing)
If ((Number of units in KnockedGroup) Equal to 0) then do (Skip remaining actions) else do (Do nothing)
Unit Group - Pick every unit in KnockedGroup and do (Actions)
Loop - Actions
Animation - Change (Picked unit) flying height to ((Current flying height of (Picked unit)) + (0.30 x ((Real(((((Custom value of (Picked unit)) mod 100000) - ((Custom value of (Picked unit)) mod 100)) - 90000))) / 100.00))) at 0.00
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
((Custom value of (Picked unit)) mod 10) Greater than 0
Then - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
Then - Actions
Else - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
Then - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
Then - Actions
Unit - Set the custom value of (Picked unit) to (((Custom value of (Picked unit)) - ((Custom value of (Picked unit)) mod 10)) + (((Custom value of (Picked unit)) mod 10) / 2))
Unit - Make (Picked unit) face ((Facing of (Picked unit)) + 180.00)
Else - Actions
Unit - Set the custom value of (Picked unit) to (((Custom value of (Picked unit)) - ((Custom value of (Picked unit)) mod 10)) + (((Custom value of (Picked unit)) mod 10) / 2))
Unit - Make (Picked unit) face (0.00 - (Facing of (Picked unit)))
Else - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
(Load (((Integer(((X of (((Position of (Picked unit)) offset by ((-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - ((Custom value of (Picked unit)) mod 100000)) / 100000)))), (-16.00 x (Real(((((Custom value of (Picked unit)) mod 1000000) - (( Less than (5 - ((((Custom value of (Picked unit)) mod 10000000) - ((Custom value of (Picked unit)) mod 1000000)) / 1000000))
Then - Actions
Unit - Set the custom value of (Picked unit) to (((Custom value of (Picked unit)) - ((Custom value of (Picked unit)) mod 10)) + (((Custom value of (Picked unit)) mod 10) / 2))
Unit - Make (Picked unit) face (180.00 - (Facing of (Picked unit)))
Else - Actions
Unit - Set the custom value of (Picked unit) to (((Custom value of (Picked unit)) - ((Custom value of (Picked unit)) mod 10)) + (((Custom value of (Picked unit)) mod 10) / 2))
Unit - Make (Picked unit) face ((Facing of (Picked unit)) + 180.00)
Unit - Turn collision for (Picked unit) Off.
Unit - Move (Picked unit) instantly to ((Position of (Picked unit)) offset by (3.00 x (4.00 - (Square root((Real(((10 - ((Custom value of (Picked unit)) mod 10)) mod 10))))))) towards ((Facing of (Picked unit)) + 180.00) degrees.)
Unit - Turn collision for (Picked unit) On.
Else - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(((Custom value of (Picked unit)) mod 100000) - ((Custom value of (Picked unit)) mod 100)) Greater than 0
Then - Actions
Unit - Set the custom value of (Picked unit) to ((Custom value of (Picked unit)) - 100)
Else - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Current flying height of (Picked unit)) Less than ((Default flying height of (Picked unit)) + 0.01)
Then - Actions
Animation - Change (Picked unit) flying height to (Default flying height of (Picked unit)) at 0.00
Unit - Set the custom value of (Picked unit) to ((Custom value of (Picked unit)) - (((Custom value of (Picked unit)) mod 100000) - ((Custom value of (Picked unit)) mod 100)))
Unit Group - Remove (Picked unit) from KnockedGroup.
Unit - Unpause (Picked unit)
Animation - Change (Picked unit)'s animation speed to 100.00% of its original speed
Animation - Play (Picked unit)'s stand animation
Else - Actions
I am not concerned with unit collisions, I only want units to avoid being thrown into an area from which they cannot return, ie: the middle of a cliff, building, trees, or some other destructible/pathing blocker. You are correct that it does not check for buildings; it does not check for buildings or destructibles, but it does check for doodads and it checks for places where bridges and ramps allow walking that would otherwise be unwalkable rightly returning walkable, but a bit ironically, for bridges and ramps it does not make there unwalkable areas unwalkable, the walls of a bridge or ramp will return as walkable.
Any chance you'll share a link that would give me an example of this item trick?
This system is part of a larger knock-back and knock-up system that uses custom values to store each units cell size, the magnitude of a knock-back, the magnitude of a knock-up, the vertical velocity of the unit, the cell size of the units collision box, and whether the unit has the +(16,16) offset from the center of its cell.
The idea is that at the end of the day is that I can write any spell that gives a unit a knock-back magnitude and a knock-up magnitude, add it to a 'knocked units' group, and turn on the periodic trigger for moving all 'knocked units'. Using the custom value for the local data I hoped to save myself some trouble and make somewhat of a universal system for myself.
Ultimately, I may not apply this to the entire map, but I could easily shift it to any rectangular area of the map and right now I'm just trying to get a feasible version running.
Doing it in GUI is a big reason you hit the OP limit more easily.
Every trigger action is at least one operation minimum (ignoring nested calls, because that's totally a thing GUI makes you do and absolutely contributes here), but often GUI functions are wrappers that just call a native directly with a different order for arguments, set some global variables, or do an integer loop automatically for you.
If conditions are all a series of annoying nested boolean functions.
Referring to things like "last created unit" and "entire map" are function calls that returns a global variable (instead of just being able to reference the variable directly).
These all contribute unnecessary operations that could potentially be avoided by writing this crucial part of your script in JASS or Lua. Your best bet to keeping this GUI only is to go through and assign a bunch of global variables to things that you will repeatedly use in the loop. The ones I see initially are Min/Max X/Y of the entire map. Also eliminating the need to create a point at each step of the loop would be large savings. This would be done by checking the pathability using the native directly, which uses XY coordinates instead of a point.
And uh, every point you test the pathability of currently leaks memory. Big yikes considering how many times these loops run!
It's going to take some time, but I am beginning work on as much of this as I can. I do really want to keep it GUI, mostly because I thought it would be a fun idea to make GUI tools that I could potentially share with others who don't know JASS, but maybe I will just cave and learn it some day soon.
I do intend to do what you said about the Min/Max X/Y. One question: if I set these to globals and then use the globals in an equation do I need to do any more to clean this up, or is this fine since now I'm using a global (I thought Min/Max X/Y would have been globals lol).
Lastly, for now.. as far as the pathability point-test leak, this will be changing since I intend to go with the coordinate idea, but just for my own knowledge going forward, would this solve that leak? I know there are more leaks elsewhere and plenty of them I'm sure, but just as a start I'm wondering...
Set VariableSet tmpMapPoint = (Point(((Min X of (Entire map)) + (Real((8 x ((2 x MapLoopX) + 1))))), ((Min Y of (Entire map)) + (Real((8 x ((2 x ((MapSection x (MapSizeY / MapSizer)) + MapLoopY)) + 1)))))))
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Terrain pathing at tmpMapPoint of type Walkability is off) Equal to True
Then - Actions
Hashtable - Save 4 as MapLoopX of ((MapSection x (MapSizeY / MapSizer)) + MapLoopY) in TerrainHash.
Else - Actions
Hashtable - Save 0 as MapLoopX of ((MapSection x (MapSizeY / MapSizer)) + MapLoopY) in TerrainHash.
I've also made the change now that I make a temporary region at init, set the globals Min X/Y and the Size X/Y (not bother with Max because beyond this point I only use Min and Size). I call RemoveRect(udg_tmpRegion) and then use those globals to do the rest.
Looking at the total memory WC3 is using to compare the leaks, I've gone down from ~4,500 MB of memory to ~1,400 MB and it had ~1,400 MB before the loop started so I think I'm catching them all in this trigger now.
I am not concerned with unit collisions, I only want units to avoid being thrown into an area from which they cannot return, ie: the middle of a cliff, building, trees, or some other destructible/pathing blocker. You are correct that it does not check for buildings; it does not check for buildings or destructibles, but it does check for doodads and it checks for places where bridges and ramps allow walking that would otherwise be unwalkable rightly returning walkable, but a bit ironically, for bridges and ramps it does not make there unwalkable areas unwalkable, the walls of a bridge or ramp will return as walkable.
Any chance you'll share a link that would give me an example of this item trick?
This is the basic idea:
Move an item to a place and check if the item's actual position is within an acceptable threshold of that place.
A classic tutorial on it: About Movement
I made it like this for my own knockbacksystem:
This can be optimized if you only need to check walkability, but this is also used to be able to get closes walkable point to specified x,y.
JASS:
function updateWalkablePoint takes real x, real y returns nothing
call SetItemPosition(udg_CP_Item, x, y)
call MoveLocation(walkablePoint, GetItemX(udg_CP_Item), GetItemY(udg_CP_Item))
call SetItemVisible(udg_CP_Item, false)
endfunction
function isPointWalkable takes real x, real y returns boolean
call updateWalkablePoint(x, y)
return RAbsBJ(x - GetLocationX(walkablePoint)) < WALKABLE_EPSILON and RAbsBJ(y - GetLocationY(walkablePoint)) < WALKABLE_EPSILON
endfunction
In this code, first, I move an item to coordinate to check then I move a Point there, then hide the item. When items are moved, they are unhidden, that's why the hide is needed.
Then I check if distance between where I expected it and where it landed is less than 0.1 or something like that, if so, it is walkable.
Hopefully recreatable in GUI with this description.
Also note that if you have many items on your map, you'll need something slightly more complex that also hides existing item before moving the "checking item" and then unhides them after checking so they do not nudge the "checking item".
This is the basic idea:
Move an item to a place and check if the item's actual position is within an acceptable threshold of that place.
A classic tutorial on it: About Movement
I made it like this for my own knockbacksystem:
This can be optimized if you only need to check walkability, but this is also used to be able to get closes walkable point to specified x,y.
JASS:
function updateWalkablePoint takes real x, real y returns nothing
call SetItemPosition(udg_CP_Item, x, y)
call MoveLocation(walkablePoint, GetItemX(udg_CP_Item), GetItemY(udg_CP_Item))
call SetItemVisible(udg_CP_Item, false)
endfunction
function isPointWalkable takes real x, real y returns boolean
call updateWalkablePoint(x, y)
return RAbsBJ(x - GetLocationX(walkablePoint)) < WALKABLE_EPSILON and RAbsBJ(y - GetLocationY(walkablePoint)) < WALKABLE_EPSILON
endfunction
In this code, first, I move an item to coordinate to check then I move a Point there, then hide the item. When items are moved, they are unhidden, that's why the hide is needed.
Then I check if distance between where I expected it and where it landed is less than 0.1 or something like that, if so, it is walkable.
Hopefully recreatable in GUI with this description.
Also note that if you have many items on your map, you'll need something slightly more complex that also hides existing item before moving the "checking item" and then unhides them after checking so they do not nudge the "checking item".
Thanks! I'm going to check that out! For now, the system is working much better now that I have it in better shape as far as leaking goes.
Thanks to the help I've had so far, I can load a 256x256 map in 2 min which is a dramatic improvement from the 10+ min I was working with before.
If I get creative with this, I can have it load in the time it might take players chose set-up options by reducing the size of the region.
For the present, my collision system is working great so I'm not going to mess with it because I don't think the item system will add much for me. Currently I have a system where the units will bounce off of walls and I would have to reimagine the concept with the item system.
I think once I fix the leaks in the knockback system I'll be pretty happy.
Still, for the sake of bettering my editing skills I did want to read that regardless, so thank you.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.