Lemonwired 2 Posted March 10, 2019 Hi All, First time on these forums. I've created a unoptimized script that clears roads of obstacles to make it easier for AI to navigate past the clutter. It works to clear the roads but takes so damn long to execute. Any ideas here? Basically, the script works by looking for all roads within a radius then obtains all the nearest objects on that road and hides them. //0 = [player,1000,10] execvm "clearroad.sqf"; //here's what to put in the init.sqf //item 1 is the object that will be the center e.g. player //item 2 is the radius where all the roads that will be impacted e.g. 1000m around the player //item 3 is the area of the road that will be cleared e.g. 10 meters of road to be cleared _pos = _this select 0; _radius = _this select 1; _radius2 = _this select 2; _roadList= getpos _pos nearroads _radius; _notobjects = []; _i = 1; _arraycount = count _roadList; while {_i < _arraycount} do { _arr = nearestObjects [getpos (_roadList select _i), [], _radius2]; _arr2 = nearestterrainObjects [getpos (_roadList select _i), [], _radius2]; _notobjects = _notobjects + _arr + _arr2; _i = _i + 1; }; {hideobject _x} foreach _notobjects; kind regards, Lemon Share this post Link to post Share on other sites
gc8 977 Posted March 10, 2019 So you need to run this script once or more times? 1000 meters for roads is slow.. Share this post Link to post Share on other sites
Lemonwired 2 Posted March 10, 2019 Just the once at mission start would be needed. Share this post Link to post Share on other sites
gc8 977 Posted March 10, 2019 Well one trick to make it faster is to put the code in EachFrame EH. eachFrameEH = addMissionEventHandler ["EachFrame", { removeMissionEventHandler ["EachFrame",eachFrameEH]; // Code here _pos = player; _radius = 1000; _radius2 = 10; _roadList= getpos _pos nearroads _radius; _notobjects = []; _i = 1; _arraycount = count _roadList; while {_i < _arraycount} do { _arr = nearestObjects [getpos (_roadList select _i), [], _radius2]; _arr2 = nearestterrainObjects [getpos (_roadList select _i), [], _radius2]; _notobjects = _notobjects + _arr + _arr2; _i = _i + 1; }; {hideobject _x} foreach _notobjects; }]; Run that in init.sqf for example 1 1 1 Share this post Link to post Share on other sites
Lemonwired 2 Posted March 10, 2019 Thanks gc8! I'll give this a try 😊 Share this post Link to post Share on other sites
Lemonwired 2 Posted March 11, 2019 Just another query on this, I have inputted both nearestObjects & nearestterrainObjects to run on the same road to ensure ALL types of objects are cleared. Does anyone know if this is overkill? For example, can nearestObjects clear terrain objects as well? I'm looking to use this on any terrain. Kind regards, Lemon Share this post Link to post Share on other sites
HazJ 1289 Posted March 11, 2019 @gc8 Why are you using EachFrame? Not a trick at all. That will run x times per frame. while loop will run every second per frame. Not ideal at all, especially with a while loop inside. @Lemonwired No need to run while loop and set _i so just use for loop: for "_i" from 0 to (count _roadList) do { _arr = nearestObjects [getPos (_roadList select _i), [], _radius2] append nearestTerrainObjects [getPos (_roadList select _i), [], _radius2]; _notobjects pushBack_arr; }; Not tested but you get the idea. https://community.bistudio.com/wiki/for https://community.bistudio.com/wiki/append https://community.bistudio.com/wiki/pushBack I also recommend using params command. https://community.bistudio.com/wiki/params params ["_pos", "_radius", "_radius2"]; // You can set default values like so: params [ ["_pos", [0, 0, 0]], ["_radius", 100], ["_radius2", 200] ]; Not all roads can be hidden. I don't see any of your script being a reliable solution. If you are using a pre-defined path for AI vehicles then there are other ways to make AI follow road. https://community.bistudio.com/wiki/forceFollowRoad https://community.bistudio.com/wiki/setDriveOnPath 1 Share this post Link to post Share on other sites
Lemonwired 2 Posted March 11, 2019 Thanks HazJ! I was just looking into using "append" & "For" instead of "_notobjects = _notobjects + _arr;" and "while". Not sure what you meant by "Not all roads can be hidden". The purpose of this is to clear the road of obstacles, which custom terrains like 'Mogadishu' that have to much clutter for AI to navigate around. What I had previously worked but was extremely slow to do all the hiding of objects. I also have the AI dynamically moving to different locations across the entire map (similar to Arma 2's Warfare/CTI), the issue is they get stuck behind a wreck in the middle of the road for example. Right now, I'm not on my gaming desktop to test, but I'm looking to use nearobjects instead of nearestobjects because sorting shouldn't be required. And I'm getting rid of nearestterrainobjects. Also, please note, this will just be for singleplayer. Here's the script I have now (calling it cleanroads.sqf or whatever), I'll test it when I get home: _pos = _this select 0; _radius = _this select 1; _radius2 = _this select 2; _roadList= getpos _pos nearroads _radius; _notobjects = []; for "_i" from 0 to (count _roadList) do { _arr = getPos (_roadList select _i) nearObjects _radius2; _notobjects append _arr; }; {hideobject _x} foreach _notobjects; I'll give feedback once tested for anyone else that wants to use a script to remove road obstacles from their missions. Share this post Link to post Share on other sites
HazJ 1289 Posted March 11, 2019 You'll need nearestTerrainObjects to get certain map objects. You can also use forEach loop. 1 Share this post Link to post Share on other sites
Lemonwired 2 Posted March 11, 2019 16 minutes ago, HazJ said: You'll need nearestTerrainObjects to get certain map objects. You can also use forEach loop. 😭 I was hoping I could get away with it. Thanks again HazJ. Here's the latest script I'll soon test: _pos = _this select 0; _radius = _this select 1; _radius2 = _this select 2; _roadList= getpos _pos nearroads _radius; _notobjects = []; for "_i" from 0 to (count _roadList) do { _arr = getPos (_roadList select _i) nearObjects _radius2 append nearestTerrainObjects [getPos (_roadList select _i), [], _radius2]; _notobjects append _arr; }; {hideobject _x} foreach _notobjects; Share this post Link to post Share on other sites
HazJ 1289 Posted March 11, 2019 Use params as I suggested. Change: _pos = _this select 0; _radius = _this select 1; _radius2 = _this select 2; params ["_pos", "_radius", "_radius2"]; Or: // You can set default values like so: params [ ["_pos", [0, 0, 0]], ["_radius", 100], ["_radius2", 200] ]; Might just be easier to use a forEach in first place rather than create the array then iterate through and hideObject, etc... Share this post Link to post Share on other sites
gc8 977 Posted March 11, 2019 6 hours ago, HazJ said: Why are you using EachFrame? Not a trick at all. That will run x times per frame. while loop will run every second per frame. Not ideal at all, especially with a while loop inside. The each frame EH is removed immediately so it runs only once 1 Share this post Link to post Share on other sites
Grumpy Old Man 3545 Posted March 11, 2019 19 minutes ago, gc8 said: The each frame EH is removed immediately so it runs only once And the benefit from doing so will be what exactly? Cheers 1 1 Share this post Link to post Share on other sites
gc8 977 Posted March 11, 2019 1 hour ago, Grumpy Old Man said: And the benefit from doing so will be what exactly? The code is run almost immediately Share this post Link to post Share on other sites
Grumpy Old Man 3545 Posted March 11, 2019 7 hours ago, Lemonwired said: 😭 I was hoping I could get away with it. Thanks again HazJ. Here's the latest script I'll soon test: _pos = _this select 0; _radius = _this select 1; _radius2 = _this select 2; _roadList= getpos _pos nearroads _radius; _notobjects = []; for "_i" from 0 to (count _roadList) do { _arr = getPos (_roadList select _i) nearObjects _radius2 append nearestTerrainObjects [getPos (_roadList select _i), [], _radius2]; _notobjects append _arr; }; {hideobject _x} foreach _notobjects; Doubt this will work, since append doesn't return anything. 49 minutes ago, gc8 said: The code is run almost immediately Why not just a simple call? Got any numbers on that to compare eachFrame+instant removal / call / execVM? Anyway I doubt the time until the snippet is being executed is relevant here, since the snippet itself takes quite a while to finish. Testing this (fixed to make it work): params [ ["_pos", player], ["_radius", 100], ["_radius2", 10] ]; _roadList= getpos _pos nearroads _radius; _notobjects = []; for "_i" from 0 to (count _roadList) do { _objs = getpos (_roadList select _i) nearObjects _radius2; {hideObjectGlobal _x} foreach _objs; _terrObjs = nearestTerrainObjects [getPos (_roadList select _i), [], _radius2]; {hideObjectGlobal _x} foreach _terrObjs; }; //Result: //0.669565 ms The culprit here is that you neither need the for loop (no _i is needed), nor the assignment of all roads and nearby objects to variables, and also can skip the additional forEach loop to hide objects, and condense all operations down to as few as possible: params [ ["_centerPos", getPosATL player], ["_roadRadius", 100], ["_objectRadius", 10] ]; _centerPos nearRoads _roadRadius apply { getPos _x nearObjects _objectRadius apply {hideObjectGlobal _x}; nearestTerrainObjects [getPos _x, [], _objectRadius] apply {hideObjectGlobal _x}; }; //Result: //0.330003 ms This works in less than half the duration of the previous snippet. Tested on Altis, Lakka at [12345.4,15669.5,0.00154877]. To further increase speed you could use map center as _centerPos, check all roads on the entire map and run this as custom function from CfgFunctions during preInit. Doubt you'll notice a wait of more than 1-2 seconds once upon mission start. Cheers 5 1 Share this post Link to post Share on other sites
gc8 977 Posted March 11, 2019 10 minutes ago, Grumpy Old Man said: Got any numbers on that to compare eachFrame+instant removal / call / execVM? I have tested that but don't have any numbers now. if scripts get laggy I put them to eachFrame to make them run faster What may happen when doing this is a lag spike (if the script is heavy) as the engine is trying to run the code in single frame Share this post Link to post Share on other sites
Grumpy Old Man 3545 Posted March 11, 2019 20 minutes ago, gc8 said: I have tested that but don't have any numbers now. if scripts get laggy I put them to eachFrame to make them run faster What may happen when doing this is a lag spike (if the script is heavy) as the engine is trying to run the code in single frame Worst case is that the scripts you put into the eachFrame EH will run on the next frame. At 60fps this means it can take up to 16.67ms until the snippet is actually run. Compare that to the 0.33ms runtime of the snippet above, doubt "call" delays this in any order of significance. Cheers Share this post Link to post Share on other sites
gc8 977 Posted March 11, 2019 8 minutes ago, Grumpy Old Man said: Worst case is that the scripts you put into the eachFrame EH will run on the next frame. Not if you remove the EH as first thing inside the EH Share this post Link to post Share on other sites
mrcurry 496 Posted March 11, 2019 41 minutes ago, gc8 said: Not if you remove the EH as first thing inside the EH What GOM meant was that registering the event frame 1 means the first execution might be delayed until frame 2, hence the delayed execution. Generally OnEachFrame helps by running the code in unscheduled but other than that I see no real improvement. Take for example my Voronoi generation. A scheduled run would take 5-8s but an unscheduled pre-init run is <1s. If you can get away with a pre-init exec it should be preferred IMO. 1 Share this post Link to post Share on other sites
gc8 977 Posted March 11, 2019 @mrcurry Yes I believe pre-init is also fast (But function dependencies is a problem). You only need to use tricks like this when your mission is running so many scripts that the script execution gets longer and longer. I once had script, which do to the amount of other scripts running, took about two minutes to run. So I needed solution to fix this and eachFrame helped there Share this post Link to post Share on other sites
HazJ 1289 Posted March 11, 2019 @Grumpy Old Man I thought append was the same as _arr1 + _arr2 hehe. Just faster? Share this post Link to post Share on other sites
pierremgi 4829 Posted March 12, 2019 10 hours ago, HazJ said: @Grumpy Old Man I thought append was the same as _arr1 + _arr2 hehe. Just faster? Seems to be. 1 Share this post Link to post Share on other sites
Lemonwired 2 Posted March 12, 2019 Huge thanks @Grumpy Old Man ! This crazy amount of optimisation was exactly what I was after, I'll test this when I get the chance. By the way, with the script it will hide editor place objects including the player if near a road. So I'll need to throw in something that prevents editor placed units from being deleted, perhaps the following: nearObjects ["Man", _roadRadius]? How could I incorporate that into Grumpy Old Man's code? _EXCLUDE = nearObjects ["Man", _roadRadius]; _centerPos nearRoads _roadRadius apply { getPos _x nearObjects _objectRadius - _EXCLUDE apply {hideObjectGlobal _x}; nearestTerrainObjects [getPos _x, [], _objectRadius] apply {hideObjectGlobal _x}; }; Thanks in advance all! Everyone has been so extremely helpful. 1 Share this post Link to post Share on other sites
Grumpy Old Man 3545 Posted March 12, 2019 13 hours ago, HazJ said: @Grumpy Old Man I thought append was the same as _arr1 + _arr2 hehe. Just faster? It's similar in function and faster but not in outcome, + returns the resulting array while append modifies the first array without returning anything on its own: _arr = [1,2,3] + [4,5,6] systemChat str _arr;//prints [1,2,3,4,5,6] _arr = [1,2,3] append [4,5,6] systemChat str _arr;//prints nothing _arr1 = [1,2,3]; _arr = _arr1 append [4,5,6]; systemChat str _arr1;//prints [1,2,3,4,5,6] In the case of the snippet above it's not needed to store any of the retrieved roads and objects in an array, so I used apply to simply hide all objects returned from the respective commands. Apply also doesn't run on an empty array, in case a road doesn't have any nearby objects this could throw errors needing another if check. 1 hour ago, Lemonwired said: Huge thanks @Grumpy Old Man ! This crazy amount of optimisation was exactly what I was after, I'll test this when I get the chance. By the way, with the script it will hide editor place objects including the player if near a road. So I'll need to throw in something that prevents editor placed units from being deleted, perhaps the following: nearObjects ["Man", _roadRadius]? How could I incorporate that into Grumpy Old Man's code? Thanks in advance all! Everyone has been so extremely helpful. You can do an isKindOf check (only needed for nearObjects) to exclude infantry objects from being removed: params [ ["_centerPos", getPosATL player], ["_roadRadius", 100], ["_objectRadius", 10] ]; _centerPos nearRoads _roadRadius apply { (getPos _x nearObjects _objectRadius select {!(typeOf _x isKindOf "Man")}) apply {hideObjectGlobal _x}; nearestTerrainObjects [getPos _x, [], _objectRadius] apply {hideObjectGlobal _x}; }; Cheers 1 1 Share this post Link to post Share on other sites
Lemonwired 2 Posted March 12, 2019 Thanks again Grumpy Old Man, Works brilliantly and much more faster. Regards, Lemon 1 Share this post Link to post Share on other sites