Forums › 💬 NodeCanvas › ⚙️ Support › Automatic type conversions improvements
Hello
I’ve made some improvements (at least in my opinion 🙂 ) to the automatic type conversions:
– GameObject to Component
– Component to GameObject
– Component to Component
– GameObject/Component to Vector3 (position)
– GameObject/Component to Quaternion (rotation)
Automatic type conversion in things like GetOtherVariable, GetPropertyValue, ExecuteFunction, etc.
This greatly simplifies the graphs, as it removes the need for GetComponent, get_gameObject, etc that only add clutter. Of course, it the graph is supposed to perform active validation of its inputs than those actions are still necessary, at least some times.
Converter.cs
|
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 |
using ParadoxNotion; using System; using UnityEngine; namespace NodeCanvas.Framework { public static class Converter { /// <summary> /// Returns a function that converts a value from one type to another. /// </summary> public static Func<object, object> Get(Type fromType, Type toType) { // Normal assignment. if (toType.RTIsAssignableFrom(fromType)) return (value) => value; // Convertible to convertible. if (typeof(IConvertible).RTIsAssignableFrom(toType) && typeof(IConvertible).RTIsAssignableFrom(fromType)) { return (value) => { try { return Convert.ChangeType(value, toType); } catch { return !toType.RTIsAbstract() ? Activator.CreateInstance(toType) : null; } }; } // Unity object to bool. if (typeof(UnityEngine.Object).RTIsAssignableFrom(fromType) && toType == typeof(bool)) return (value) => value != null; // GameObject to component. if (fromType == typeof(GameObject) && typeof(Component).RTIsAssignableFrom(toType)) return (value) => (value as GameObject)?.GetComponent(toType); // Component to GameObject. if (typeof(Component).RTIsAssignableFrom(fromType) && toType == typeof(GameObject)) return (value) => (value as Component)?.gameObject; // Component to Component. if (typeof(Component).RTIsAssignableFrom(fromType) && typeof(Component).RTIsAssignableFrom(toType)) return (value) => (value as Component)?.gameObject.GetComponent(toType); // GameObject to Vector3 (position). if (fromType == typeof(GameObject) && toType == typeof(Vector3)) return (value) => { return value != null ? (value as GameObject).transform.position : Vector3.zero; }; // Component to Vector3 (position). if (typeof(Component).RTIsAssignableFrom(fromType) && toType == typeof(Vector3)) return (value) => { return value != null ? (value as Component).transform.position : Vector3.zero; }; // GameObject to Quaternion (rotation). if (fromType == typeof(GameObject) && toType == typeof(Quaternion)) return (value) => { return value != null ? (value as GameObject).transform.rotation : Quaternion.identity; }; // Component to Quaternion (rotation). if (typeof(Component).RTIsAssignableFrom(fromType) && toType == typeof(Quaternion)) return (value) => { return value != null ? (value as Component).transform.rotation : Quaternion.identity; }; // Quaternion to Vector3 (Euler angles). if (fromType == typeof(Quaternion) && toType == typeof(Vector3)) return (value) => ((Quaternion)value).eulerAngles; // Vector3 (Euler angles) to Quaternion. if (fromType == typeof(Vector3) && toType == typeof(Quaternion)) return (value) => Quaternion.Euler((Vector3)value); // Vector2 to Vector3. if (fromType == typeof(Vector2) && toType == typeof(Vector3)) return (value) => (Vector3)(Vector2)value; // Vector3 to Vector2. if (fromType == typeof(Vector3) && toType == typeof(Vector2)) return (value) => (Vector2)(Vector3)value; return null; } } } |
Variable.cs
|
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 |
///Checks whether a conversion to type is possible public bool CanConvertTo(Type toType){ return GetGetConverter(toType) != null; } ///Gets a Func<object> that converts the value ToType if possible. Null if not. public Func<object> GetGetConverter(Type toType) { // Normal assignment, written this way avoids boxing in some cases. if (toType.RTIsAssignableFrom(varType)) return () => value; var converter = Converter.Get(varType, toType); if (converter == null) return null; return () => converter(value); } ///Checks whether a conversion from type is possible public bool CanConvertFrom(Type fromType){ return GetSetConverter(fromType) != null; } ///Gets an Action<object> that converts the value fromType if possible. Null if not. public Action<object> GetSetConverter(Type fromType) { // Normal assignment, written this way avoids boxing in some cases. if (varType.RTIsAssignableFrom(fromType)) return (arg) => value = arg; var converter = Converter.Get(fromType, varType); if (converter == null) return null; return (arg) => value = converter(arg); } |
GetOtherBlackboardVariable.cs
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
protected override void OnExecute(){ var targetVar = agent.GetVariable(targetVariableName.value); if (targetVar == null){ EndAction(false); return; } var getter = targetVar.GetGetConverter(saveAs.varType); if (getter == null) { EndAction(false); return; } saveAs.value = getter(); EndAction(true); } |
GetPropertyOfVariable_Multiplatform.cs (an extension of GetProperty that can get properties of a variable of arbitrary type, as GetProperty gets from its agent, which must derive from Component).
|
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 |
using NodeCanvas.Framework; using NodeCanvas.Framework.Internal; using ParadoxNotion; using ParadoxNotion.Design; using ParadoxNotion.Serialization; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using UnityEngine; namespace NodeCanvas.Tasks.Actions { [Name("Get Property of Variable (mp)", -1)] [Category("✫ Script Control/Multiplatform")] [Description("Get a property of a variable and save it to the blackboard")] public class GetPropertyOfVariable_Multiplatform : ActionTask { [SerializeField] [BlackboardOnly] protected BBObjectParameter variable; [SerializeField] protected SerializedMethodInfo method; [SerializeField] [BlackboardOnly] protected BBObjectParameter returnValue; protected MethodInfo targetMethod => method?.Get(); //https://nodecanvas.paradoxnotion.com/forums/topic/getfield-from-arbitrary-value-not-just-scripts/ //bool arrayToList; //MethodInfo collectionClear; //MethodInfo collectionAdd; protected override string info { get { if (method == null) return "No Property Selected"; if (targetMethod == null) return string.Format("<color=#ff6457>* {0} *</color>", method.GetMethodString()); var mInfo = targetMethod.IsStatic ? targetMethod.RTReflectedType().FriendlyName() : variable.ToString(); return $"{returnValue} = {mInfo}.{targetMethod.Name}"; } } public override void OnValidate(ITaskSystem ownerSystem) { if (method != null && method.HasChanged()) SetMethod(method.Get()); if (method != null && method.Get() == null) Error($"Missing Property '{method.GetMethodString()}'"); } protected override string OnInit() { if (method == null) return "No Property selected"; if (targetMethod == null) return $"Missing Property '{method.GetMethodString()}'"; return null; } protected override void OnExecute() { object value = null; if (targetMethod.IsStatic) value = targetMethod.Invoke(null, null); else if (variable.value == null) Error($"Null variable value '{variable}'"); else { // Convert the source value to the method's type first. var varValue = variable.value; var converter = Converter.Get(varValue.GetType(), targetMethod.RTReflectedType()); if (converter == null) Error($"Cannot convert from '{varValue.GetType().FullName}' to '{targetMethod.RTReflectedType().FullName}'"); else value = targetMethod.Invoke(converter(variable.value), null); } if (value != null && returnValue != null) { var converter = Converter.Get(value.GetType(), returnValue.varType); if (converter == null) Error($"Cannot convert from '{value.GetType().FullName}' to '{returnValue.varType.FullName}'"); else returnValue.value = converter(value); } EndAction(); } void SetMethod(MethodInfo method) { if (method == null) return; this.method = new SerializedMethodInfo(method); var returnType = method.ReturnType; //arrayToList = returnType.IsArray && returnValue.value != null && returnValue.value.GetType().Implements(ICollection<ArrayElementType>) returnValue.SetType(returnType); } ///---------------------------------------------------------------------------------------------- ///---------------------------------------UNITY EDITOR------------------------------------------- #if UNITY_EDITOR protected override void OnTaskInspectorGUI() { if (!Application.isPlaying && GUILayout.Button("Select Property")) { var menu = new UnityEditor.GenericMenu(); if (variable.value != null) { menu = EditorUtils.GetInstanceMethodSelectionMenu(variable.value.GetType(), typeof(object), typeof(object), SetMethod, 0, true, true, menu); menu.AddSeparator("/"); } foreach (var t in UserTypePrefs.GetPreferedTypesList(typeof(object))) { menu = EditorUtils.GetStaticMethodSelectionMenu(t, typeof(object), typeof(object), SetMethod, 0, true, true, menu); menu = EditorUtils.GetInstanceMethodSelectionMenu(t, typeof(object), typeof(object), SetMethod, 0, true, true, menu); } if (Editor.NCPrefs.useBrowser) menu.ShowAsBrowser("Select Property", GetType()); else menu.ShowAsContext(); Event.current.Use(); } if (targetMethod != null) { GUILayout.BeginVertical("box"); UnityEditor.EditorGUILayout.LabelField("Type", targetMethod.RTReflectedType().FriendlyName()); UnityEditor.EditorGUILayout.LabelField("Property", targetMethod.Name); UnityEditor.EditorGUILayout.LabelField("Property Type", targetMethod.ReturnType.FriendlyName()); GUILayout.EndVertical(); if (!targetMethod.IsStatic) Editor.BBParameterEditor.ParameterField("Variable", variable, true); Editor.BBParameterEditor.ParameterField("Save As", returnValue, true); } } #endif } } |
Moved all the conversion code to its own file, both to simplify Variable.cs and to be able to use it outside of Variable class.
Hey,
Thanks. Moving the conversions outside of Variable.cs is something I indeed wanted to do sometime now 🙂
I already have a very similar (standalone) converter made for FlowCanvas, thus using this for both NodeCanvas and FlowCanvas would of course be better.
The GetPropertyOfVariable is also very interesting and useful by the way 🙂
