NodeCanvas Forums › General Discussion › [PERFORMANCE] Better ActionTask ?
Hello, I wrote a new ActionTask to lower the use of if statement and remove the latch dirty thing.
The main idea was to define pointer of function instead of if statement in a single function.
What do you about it ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
using System; using System.Collections; using ParadoxNotion.Services; using ParadoxNotion.Serialization; using ParadoxNotion.Serialization.FullSerializer; using NodeCanvas.Framework.Internal; using UnityEngine; namespace NodeCanvas.Framework { ///Base class for actions. Extend this to create your own. T is the agentType required by the action. ///Generic version where T is the AgentType (Component or Interface) required by the Action. ///For GameObject, use 'Transform' abstract public class ActionTask : ActionTask where T : class { sealed public override Type agentType { get { return typeof(T); } } new public T agent { get { return base.agent as T; } } } ///---------------------------------------------------------------------------------------------- #if UNITY_EDITOR //handles missing types [fsObject(Processor = typeof(fsRecoveryProcessor<ActionTask, MissingAction>))] #endif ///Base class for all actions. Extend this to create your own. abstract public class ActionTask : Task { public delegate Status ExecuteDelegate(Component agent, IBlackboard blackboard); private Status status = Status.Resting; private float timeStarted; //private bool latch; ///The time in seconds this action is running if at all public float elapsedTime => ( isRunning ? ownerSystem.elapsedTime - timeStarted : 0 ); ///Is the action currently running? public bool isRunning => status == Status.Running; ///Is the action currently paused? public bool isPaused { get; private set; } ///---------------------------------------------------------------------------------------------- ///Used to call an action for standalone execution providing a callback. ///Be careful! *This will make the action execute as a coroutine* public void ExecuteIndependent(Component agent, IBlackboard blackboard, Action callback) { if ( !isRunning ) { MonoManager.current.StartCoroutine(IndependentActionUpdater(agent, blackboard, callback)); } } //The internal updater for when an action has been called with a callback parameter and only then. //This is only used and usefull if user needs to execute an action task completely as standalone. IEnumerator IndependentActionUpdater(Component agent, IBlackboard blackboard, Action callback) { while ( Execute(agent, blackboard) == Status.Running ) { yield return null; } if ( callback != null ) { callback(status); } } ///---------------------------------------------------------------------------------------------- [System.Obsolete("Use 'Execute'")] public Status ExecuteAction(Component agent, IBlackboard blackboard) { return Execute(agent, blackboard); } public ActionTask() { Execute = StartExecuteFlow; } private Status StartExecuteFlow(Component agent, IBlackboard blackboard) { Execute = RunExecuteFlow; //Next time we will have to run if (!Set(agent, blackboard)) { return Status.Failure; } timeStarted = ownerSystem.elapsedTime; status = Status.Running; OnExecute(); if (status == Status.Running) { OnUpdate(); } return status; } private Status RunExecuteFlow(Component agent, IBlackboard blackboard) { if (!Set(agent, blackboard)) { return Status.Failure; } OnUpdate(); return status; } ///Ticks the action for the provided agent and blackboard public ExecuteDelegate Execute; /*(Component agent, IBlackboard blackboard) { if ( isPaused ) { OnResume(); } isPaused = false; if ( status == Status.Running ) { OnUpdate(); latch = false; return status; } //latch is used to be able to call EndAction anywhere if ( latch ) { latch = false; return status; } if ( !Set(agent, blackboard) ) { latch = false; return Status.Failure; } timeStarted = ownerSystem.elapsedTime; status = Status.Running; OnExecute(); if ( status == Status.Running ) { OnUpdate(); } latch = false; return status; }*/ ///Ends the action either in success or failure. Ending with null means that it's a cancel/interrupt. ///Null is used by the external system. You should use true or false when calling EndAction within it. public void EndAction() { EndAction(true); } public void EndAction(bool success) { EndAction((bool?)success); } public void EndAction(bool? success) { if ( status != Status.Running ) { //if (success == null) // latch = false; Execute = StartExecuteFlow; return; } if(success != null) //latch { Execute = (Component agent, IBlackboard blackboard) => { Execute = StartExecuteFlow; return status; }; } else { Execute = StartExecuteFlow; } //latch = success != null ? true : false; //isPaused = false; status = success == null ? Status.Resting : ( success == true ? Status.Success : Status.Failure ); OnStop(success == null); } ///Pause the action from updating and calls OnPause public void Pause() { if ( status != Status.Running ) { return; } isPaused = true; Execute = (Component agent, IBlackboard blackboard) => { Execute = RunExecuteFlow; OnResume(); isPaused = false; if (status == Status.Running) return RunExecuteFlow(agent, blackboard); else return StartExecuteFlow(agent, blackboard); }; OnPause(); } ///---------------------------------------------------------------------------------------------- ///Called once when the actions is executed. virtual protected void OnExecute() { } ///Called every frame, if and while the action is running and until it ends. virtual protected void OnUpdate() { } ///Called whenever the action ends due to any reason with the argument denoting whether the action was interrupted or finished properly. virtual protected void OnStop(bool interrupted) { OnStop(); } ///Called whenever the action ends due to any reason. virtual protected void OnStop() { } ///Called when the action gets paused virtual protected void OnPause() { } ///Called when the action resumes after being paused virtual protected void OnResume() { } ///---------------------------------------------------------------------------------------------- } } |
Hey!
Have you run any performance comparison tests?
Thanks!
Join us on Discord: https://discord.gg/97q2Rjh
Unfortunately not intensively.
But theorically it s better. I just try to lower CPU but yep, no time to run a real test :/
Hello,
Made test and it’s not efficient ?
Hey,
I have’t tested this yet, but I honestly can’t see how this can be faster :). Anyway, maybe I will just test and see. Also the way actions are designed, the latch flag is required for some of them. Some actions will need to be refactored before removing the latch flag.
Join us on Discord: https://discord.gg/97q2Rjh
Oh sorry the message is not clear, I made some test and it seems it’s not efficient.
In fact we suppose the CPU can’t made some running optimization due to pointer of function. So conditionnals seem better in this case 😉