-
-
Save capnslipp/8516384 to your computer and use it in GitHub Desktop.
/// @creator: Slipp Douglas Thompson | |
/// @license: Public Domain per The Unlicense. See <http://unlicense.org/>. | |
/// @purpose: Genericized Unity3D SerializedProperty value access. | |
/// @why: Because this functionality should be built-into Unity. | |
/// @usage: Use as you would a native SerializedProperty method; | |
/// e.g. `Debug.Log(mySerializedProperty.Value<Color>());` | |
/// @intended project path: Assets/Plugins/Editor/UnityEditor Extensions/SerializedPropertyValueExtension.cs | |
/// @interwebsouce: https://gist.github.com/capnslipp/8516384 | |
using System; | |
using System.Reflection; | |
using System.Collections.Generic; // for: KeyValuePair<,> | |
using UnityEngine; | |
using UnityEditor; | |
public static class SerializedPropertyValueExtension | |
{ | |
/// @note: switch/case derived from the decompilation of SerializedProperty's internal SetToValueOfTarget() method. | |
public static ValueT Value<ValueT>(this SerializedProperty thisSP) | |
{ | |
Type valueType = typeof(ValueT); | |
// First, do special Type checks | |
if (valueType.IsEnum) | |
return (ValueT)Enum.ToObject(valueType, thisSP.enumValueIndex); | |
// Next, check for literal UnityEngine struct-types | |
// @note: ->object->ValueT double-casts because C# is too dumb to realize that that the ValueT in each situation is the exact type needed. | |
// e.g. `return thisSP.colorValue` spits _error CS0029: Cannot implicitly convert type `UnityEngine.Color' to `ValueT'_ | |
// and `return (ValueT)thisSP.colorValue;` spits _error CS0030: Cannot convert type `UnityEngine.Color' to `ValueT'_ | |
if (typeof(Color).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.colorValue; | |
else if (typeof(LayerMask).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.intValue; | |
else if (typeof(Vector2).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.vector2Value; | |
else if (typeof(Vector3).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.vector3Value; | |
else if (typeof(Rect).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.rectValue; | |
else if (typeof(AnimationCurve).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.animationCurveValue; | |
else if (typeof(Bounds).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.boundsValue; | |
else if (typeof(Gradient).IsAssignableFrom(valueType)) | |
return (ValueT)(object)SafeGradientValue(thisSP); | |
else if (typeof(Quaternion).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.quaternionValue; | |
// Next, check if derived from UnityEngine.Object base class | |
if (typeof(UnityEngine.Object).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.objectReferenceValue; | |
// Finally, check for native type-families | |
if (typeof(int).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.intValue; | |
else if (typeof(bool).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.boolValue; | |
else if (typeof(float).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.floatValue; | |
else if (typeof(string).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.stringValue; | |
else if (typeof(char).IsAssignableFrom(valueType)) | |
return (ValueT)(object)thisSP.intValue; | |
// And if all fails, throw an exception. | |
throw new NotImplementedException("Unimplemented propertyType "+thisSP.propertyType+"."); | |
} | |
public static dynamic Value(this SerializedProperty thisSP) | |
{ | |
switch (thisSP.propertyType) { | |
case SerializedPropertyType.Integer: | |
return thisSP.intValue; | |
case SerializedPropertyType.Boolean: | |
return thisSP.boolValue; | |
case SerializedPropertyType.Float: | |
return thisSP.floatValue; | |
case SerializedPropertyType.String: | |
return thisSP.stringValue; | |
case SerializedPropertyType.Color: | |
return thisSP.colorValue; | |
case SerializedPropertyType.ObjectReference: | |
return thisSP.objectReferenceValue; | |
case SerializedPropertyType.LayerMask: | |
return thisSP.intValue; | |
case SerializedPropertyType.Enum: | |
int enumI = thisSP.enumValueIndex; | |
return new KeyValuePair<int, string>(enumI, thisSP.enumNames[enumI]); | |
case SerializedPropertyType.Vector2: | |
return thisSP.vector2Value; | |
case SerializedPropertyType.Vector3: | |
return thisSP.vector3Value; | |
case SerializedPropertyType.Rect: | |
return thisSP.rectValue; | |
case SerializedPropertyType.ArraySize: | |
return thisSP.intValue; | |
case SerializedPropertyType.Character: | |
return (char)thisSP.intValue; | |
case SerializedPropertyType.AnimationCurve: | |
return thisSP.animationCurveValue; | |
case SerializedPropertyType.Bounds: | |
return thisSP.boundsValue; | |
case SerializedPropertyType.Gradient: | |
return SafeGradientValue(thisSP); | |
case SerializedPropertyType.Quaternion: | |
return thisSP.quaternionValue; | |
default: | |
throw new NotImplementedException("Unimplemented propertyType "+thisSP.propertyType+"."); | |
} | |
} | |
/// Access to SerializedProperty's internal gradientValue property getter, in a manner that'll only soft break (returning null) if the property changes or disappears in future Unity revs. | |
static Gradient SafeGradientValue(SerializedProperty sp) | |
{ | |
BindingFlags instanceAnyPrivacyBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; | |
PropertyInfo propertyInfo = typeof(SerializedProperty).GetProperty( | |
"gradientValue", | |
instanceAnyPrivacyBindingFlags, | |
null, | |
typeof(Gradient), | |
new Type[0], | |
null | |
); | |
if (propertyInfo == null) | |
return null; | |
Gradient gradientValue = propertyInfo.GetValue(sp, null) as Gradient; | |
return gradientValue; | |
} | |
} |
Usefull but for some reason when I try to use this for a LayerMask it gives the next error:
"InvalidCastException: Cannot cast from source type to destination type.
CustomPlugins.SerializedPropertyValueExtension.Value[LayerMask](UnityEditor.SerializedProperty thisSP) (at Assets/Plugins/SerializedPropertyValueExtension.cs:28)
PlayerCustomEditor.OnInspectorGUI () (at Assets/Editor/Player/PlayerCustomEditor.cs:35)
UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor editor, Int32 editorIndex, Boolean forceDirty, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect, Boolean eyeDropperDirty) (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1162)
UnityEditor.DockArea:OnGUI()"
I testing with:
SerializedObject myScript = new SerializedObject(target);
SerializedProperty WhatIsGround = myScript.FindProperty("WhatIsGround");
Debug.Log(WhatIsGround.Value< LayerMask >());
public static dynamic Value(this SerializedProperty thisSP)
dynamic generates an error "Cannot define a class or member that utilizes 'dynamic' because the compiler required type 'System.Runtime.CompilerServices.DynamicAttribute' cannot be found. Are you missing a reference?"
How do I fix this? I'm using VisualStudio 2015 Enterprise Edition with Unity integration.
As long as I know the version of c# in Unity does not support dynamic which is available over c# 4.0
Hugely useful - thanks for this!