NodeCanvas Forums › Custom Nodes & Tasks › ExecuteFunction_Multiplatform improvement
Hello
I’ve added the ability to handle out/ref parameters.
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 |
using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using NodeCanvas.Framework; using NodeCanvas.Framework.Internal; using ParadoxNotion; using ParadoxNotion.Design; using ParadoxNotion.Serialization; using UnityEngine; namespace NodeCanvas.Tasks.Actions{ [Name("Execute Function (mp)")] [Category("✫ Script Control/Multiplatform")] [Description("Execute a function on a script and save the return if any. If function is an IEnumerator it will execute as a coroutine.")] public class ExecuteFunction_Multiplatform : ActionTask { [SerializeField] protected SerializedMethodInfo method; [SerializeField] protected List<BBObjectParameter> parameters = new List<BBObjectParameter>(); [SerializeField] protected List<bool> parameterIsByRef = new List<bool>(); [SerializeField] [BlackboardOnly] protected BBObjectParameter returnValue; private object[] args; private bool routineRunning; private MethodInfo targetMethod{ get {return method != null? method.Get() : null;} } public override System.Type agentType{ get {return targetMethod != null? targetMethod.RTReflectedType() : typeof(Transform);} } protected override string info{ get { if (method == null) return "No Method Selected"; if (targetMethod == null) return string.Format("<color=#ff6457>* {0} *</color>", method.GetMethodString() ); var returnInfo = targetMethod.ReturnType == typeof(void) || targetMethod.ReturnType == typeof(IEnumerator)? "" : returnValue.ToString() + " = "; var paramInfo = ""; for (var i = 0; i < parameters.Count; i++) paramInfo += (i != 0? ", " : "") + parameters[i].ToString(); return string.Format("{0}{1}.{2}({3})", returnInfo, agentInfo, targetMethod.Name, paramInfo ); } } public override void OnValidate(ITaskSystem ownerSystem){ if (method != null && method.HasChanged()){ SetMethod(method.Get()); } if (method != null && method.Get() == null){ Error( string.Format("Missing Method '{0}'", method.GetMethodString()) ); } } //store the method info on init protected override string OnInit(){ if (method == null){ return "No Method selected"; } if (targetMethod == null){ return string.Format("Missing Method '{0}'", method.GetMethodString()); } if (args == null) args = new object[parameters.Count]; if (parameterIsByRef.Count != parameters.Count) // i.e. those saved before I've added this parameterIsByRef = parameters.Select(p => false).ToList(); return null; } //do it by calling delegate or invoking method protected override void OnExecute(){ for (var i = 0; i < parameters.Count; i++) args[i] = parameters[i].value; if (targetMethod.ReturnType == typeof(IEnumerator)){ StartCoroutine( InternalCoroutine( (IEnumerator)targetMethod.Invoke(agent, args) )); return; } returnValue.value = targetMethod.Invoke(agent, args); for (int i = 0; i < parameters.Count; i++) { if (parameterIsByRef[i]) parameters[i].value = args[i]; } EndAction(); } protected override void OnStop(){ routineRunning = false; } IEnumerator InternalCoroutine(IEnumerator routine){ routineRunning = true; while(routineRunning && routine.MoveNext()){ if (routineRunning == false) yield break; yield return routine.Current; } if (routineRunning){ EndAction(); } } void SetMethod(MethodInfo method){ if (method == null){ return; } this.method = new SerializedMethodInfo(method); parameters.Clear(); parameterIsByRef.Clear(); var methodParameters = method.GetParameters(); for (var i = 0; i < methodParameters.Length; i++) { var p = methodParameters[i]; var pType = p.ParameterType; var newParam = new BBObjectParameter(pType.IsByRef ? pType.GetElementType() : pType) { bb = blackboard }; if (p.IsOptional) newParam.value = p.DefaultValue; parameters.Add(newParam); parameterIsByRef.Add(pType.IsByRef); } if (method.ReturnType != typeof(void) && targetMethod.ReturnType != typeof(IEnumerator)){ this.returnValue = new BBObjectParameter(method.ReturnType){bb = blackboard}; } else { this.returnValue = null; } } //////////////////////////////////////// ///////////GUI AND EDITOR STUFF///////// //////////////////////////////////////// #if UNITY_EDITOR protected override void OnTaskInspectorGUI(){ if (!Application.isPlaying && GUILayout.Button("Select Method")){ var menu = new UnityEditor.GenericMenu(); if (agent != null){ foreach(var comp in agent.GetComponents(typeof(Component)).Where(c => c.hideFlags != HideFlags.HideInInspector) ){ menu = EditorUtils.GetMethodSelectionMenu(comp.GetType(), typeof(object), typeof(object), SetMethod, 10, false, false, menu); } menu.AddSeparator("/"); } foreach (var t in UserTypePrefs.GetPreferedTypesList(typeof(Component))){ menu = EditorUtils.GetMethodSelectionMenu(t, typeof(object), typeof(object), SetMethod, 10, false, false, menu); } if ( NodeCanvas.Editor.NCPrefs.useBrowser){ menu.ShowAsBrowser("Select Method", this.GetType()); } else { menu.ShowAsContext(); } Event.current.Use(); } if (targetMethod != null){ GUILayout.BeginVertical("box"); UnityEditor.EditorGUILayout.LabelField("Type", agentType.FriendlyName()); UnityEditor.EditorGUILayout.LabelField("Method", targetMethod.Name); UnityEditor.EditorGUILayout.LabelField("Returns", targetMethod.ReturnType.FriendlyName()); if (targetMethod.ReturnType == typeof(IEnumerator)) GUILayout.Label("<b>This will execute as a Coroutine</b>"); GUILayout.EndVertical(); var paramNames = targetMethod.GetParameters().Select(p => p.Name.SplitCamelCase() ).ToArray(); for (var i = 0; i < paramNames.Length; i++){ EditorUtils.BBParameterField(paramNames[i], parameters[i]); } if (targetMethod.ReturnType != typeof(void) && targetMethod.ReturnType != typeof(IEnumerator)){ EditorUtils.BBParameterField("Save Return Value", returnValue, true); } } } #endif } } |
Thanks!
This is actually a great addition 🙂
I’ve just added your changes for the next version as well!