Przeglądaj źródła

Inspector WIP - does not compile

Marko Pintera 11 lat temu
rodzic
commit
26e2b247be

+ 5 - 17
Inspector.txt

@@ -1,25 +1,13 @@
 
-My SerializableArray/list/dictionary approach is BROKEN:
- - I cannot determine their type just from an object, since object may be null. I need their parent field/collection type to do that.
+Add SerializableField.GetProperty
+ - Also GetObject & GetArray
+ - Remove existing GetValue/SetValue
 
-SerializableField
- - Normal GetValue/SetValue 
- - But also GetSerializableArray, GetSerializableList, GetSerializableDictionary
-
-SerializableArray
- - Also has GetSerializableArray, GetSerializableList, GetSerializableDictionary
+Do the same for SerializableObject/SerializableArray, etc.
 
  ------------------------------------------------
 
-My Inspector approach is BROKEN:
- - Number of array/list/dictionary elements in a field may dynamically change. How do I handle that? Current system just keeps the static fields.
-
-Rebuilding gui N times per second is OUT OF THE QUESTION
- - My GUI system isn't built for it
- - e.g. If I'm currently focusing on and element or writing in an input box refreshing the GUI would break it
- - Instead I must leave it to the user to track modifications, simply save old value and compare with new, and if it changed update the UI.
-  - I should potentially provide the user with overridable InspectableField. One that will automatically call CreateGUI when field value changes,
-    and user can then override it if he wants to draw custom controls. 
+Add ability to create InspectableObject directly from "object".
 
 -----------------------------------------------
 LESS IMPORTANT

+ 30 - 2
MBansheeEditor/Inspector/GenericInspector.cs

@@ -3,16 +3,44 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using BansheeEngine;
 
 namespace BansheeEditor
 {
     internal sealed class GenericInspector : Inspector
     {
+        private bool isInitialized;
+        private List<InspectableObjectBase> inspectableFields = new List<InspectableObjectBase>();
+
+        private void Initialize()
+        {
+            SerializableObject serializableObject = new SerializableObject(referencedObject);
+
+            foreach (var field in serializableObject.fields)
+            {
+                if (!field.Inspectable)
+                    continue;
+
+                if (field.HasCustomInspector)
+                    inspectableFields.Add(InspectableObjectBase.CreateCustomInspectable(field.CustomInspectorType, field.Name, field.GetProperty()));
+                else
+                    inspectableFields.Add(InspectableObjectBase.CreateDefaultInspectable(field.Name, field.GetProperty()));
+            }
+
+            // TODO - Add Component foldout
+
+            isInitialized = true;
+        }
+
         internal override void Refresh()
         {
-            InspectableObject obj = new InspectableObject(serializableObject);
+            if (!isInitialized)
+                Initialize();
 
-            obj.CreateGUI(GUI.layout);
+            foreach (var field in inspectableFields)
+            {
+                field.Refresh(GUI.layout);
+            }
         }
 
         internal override int GetOptimalHeight()

+ 103 - 0
MBansheeEditor/Inspector/InspectableArray.cs

@@ -0,0 +1,103 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    public class InspectableArray : InspectableObjectBase
+    {
+        private const int IndentAmount = 15;
+
+        private object oldPropertyValue;
+        private List<InspectableObjectBase> arrayElements = new List<InspectableObjectBase>();
+
+        private GUILabel guiLabel;
+        private GUILayout guiChildLayout;
+        private GUILayout guiContentLayout;
+        private bool isInitialized;
+
+        public InspectableArray(string title, SerializableProperty property)
+            : base(title, property)
+        {
+
+        }
+
+        protected void Initialize(GUILayout layout)
+        {
+            if (property.Type != SerializableField.FieldType.Array)
+                return;
+
+            guiLabel = new GUILabel(title);
+            layout.AddElement(guiLabel); // TODO - Add foldout and hook up its callbacks
+
+            guiChildLayout = layout.AddLayoutX();
+            guiChildLayout.AddSpace(IndentAmount);
+
+            guiContentLayout = guiChildLayout.AddLayoutY();
+
+            isInitialized = true;
+        }
+
+        protected override bool IsModified()
+        {
+            object newPropertyValue = property.GetValue<object>();
+            if (oldPropertyValue != newPropertyValue)
+            {
+                oldPropertyValue = newPropertyValue;
+
+                return true;
+            }
+
+            SerializableArray array = property.GetArray();
+            if (array.GetLength() != arrayElements.Count)
+                return true;
+
+            return base.IsModified();
+        }
+
+        protected override void Update(GUILayout layout)
+        {
+            base.Update(layout);
+
+            if (!isInitialized)
+                Initialize(layout);
+
+            // TODO - Update base GUI elements
+
+            SerializableArray array = property.GetArray();
+
+            for (int i = arrayElements.Count; i < array.GetLength(); i++)
+            {
+                InspectableObjectBase childObj = CreateDefaultInspectable(i + ".", array.GetProperty(i));
+                AddChild(childObj);
+                arrayElements.Add(childObj);
+
+                childObj.Refresh(layout);
+            }
+
+            for (int i = array.GetLength(); i < arrayElements.Count; i++)
+            {
+                arrayElements[i].Destroy();
+            }
+
+            arrayElements.RemoveRange(array.GetLength(), arrayElements.Count - array.GetLength());
+        }
+
+        public override void Destroy()
+        {
+            if (guiLabel != null)
+                guiLabel.Destroy();
+
+            if (guiContentLayout != null)
+                guiContentLayout.Destroy();
+
+            if (guiChildLayout != null)
+                guiChildLayout.Destroy();
+
+            base.Destroy();
+        }
+    }
+}

+ 64 - 0
MBansheeEditor/Inspector/InspectableInt.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    public class InspectableInt : InspectableObjectBase
+    {
+        private int oldPropertyValue;
+        private GUILabel guiLabel;
+        private bool isInitialized = false;
+
+        public InspectableInt(string title, SerializableProperty property)
+            :base(title, property)
+        {
+
+        }
+
+        protected void Initialize(GUILayout layout)
+        {
+            if(property.Type == SerializableField.FieldType.Int)
+            {
+                guiLabel = new GUILabel(title);
+                layout.AddElement(guiLabel); // TODO - Use an IntEditorField
+            }
+
+            isInitialized = true;
+        }
+
+        protected override bool IsModified()
+        {
+            int newPropertyValue = property.GetValue<int>();
+            if (oldPropertyValue != newPropertyValue)
+            {
+                oldPropertyValue = newPropertyValue;
+
+                return true;
+            }
+
+            return base.IsModified();
+        }
+
+        protected override void Update(GUILayout layout)
+        {
+            base.Update(layout);
+
+            if (!isInitialized)
+                Initialize(layout);
+
+            // TODO - Update GUI element(s) with value from property
+        }
+
+        public override void Destroy()
+        {
+            if (guiLabel != null)
+                guiLabel.Destroy();
+
+            base.Destroy();
+        }
+    }
+}

+ 55 - 145
MBansheeEditor/Inspector/InspectableObject.cs

@@ -7,177 +7,87 @@ using BansheeEngine;
 
 namespace BansheeEditor
 {
-    public sealed class InspectableObject
+    public class InspectableObject : InspectableObjectBase
     {
-        private delegate object Getter();
-        private delegate void Setter(object value);
-
         private const int IndentAmount = 15;
 
-        private object referencedObject;
+        private object oldPropertyValue;
+
+        private GUILabel guiLabel;
+        private GUILayout guiChildLayout;
+        private GUILayout guiContentLayout;
+        private bool isInitialized;
 
-        public InspectableObject(object obj)
+        public InspectableObject(string title, SerializableProperty property)
+            : base(title, property)
         {
-            referencedObject = obj;
+            
         }
 
-        public void CreateGUI(GUILayout layout, SerializableField field)
+        protected void Initialize(GUILayout layout)
         {
-            if (IsTypePrimitive(field.Type))
-            {
-                Getter getter = () => field.GetValue<object>();
-                Setter setter = (object value) => field.SetValue(value);
+            if (property.Type != SerializableField.FieldType.Object)
+                return;
 
-                CreatePrimitiveField(layout, name, type, getter, setter);
-            }
-            else
+            guiLabel = new GUILabel(title);
+            layout.AddElement(guiLabel); // TODO - Add foldout and hook up its callbacks
+
+            guiChildLayout = layout.AddLayoutX();
+            guiChildLayout.AddSpace(IndentAmount);
+
+            guiContentLayout = guiChildLayout.AddLayoutY();
+
+            SerializableObject serializableObject = property.GetObject();
+
+            foreach (var field in serializableObject.fields)
             {
-                CreateComplexField(layout, name, type, field.GetValue<object>());
+                if (!field.Inspectable)
+                    continue;
+
+                if (field.HasCustomInspector)
+                    AddChild(CreateCustomInspectable(field.CustomInspectorType, field.Name, field.GetProperty()));
+                else
+                    AddChild(CreateDefaultInspectable(field.Name, field.GetProperty()));
             }
-        }
 
-        public void CreateGUI(GUILayout layout)
-        {
-            CreateComplexTypeChildren(layout, SerializableField.FieldType.Object, referencedObject);
+            isInitialized = true;
         }
 
-        private void CreatePrimitiveField(GUILayout layout, string name, SerializableField.FieldType type, Getter getter, Setter setter)
+        protected override bool IsModified()
         {
-            layout.AddElement(new GUILabel(name)); // TODO - Use an IntEditorField, or others for other types
+            object newPropertyValue = property.GetValue<object>();
+            if (oldPropertyValue != newPropertyValue)
+            {
+                oldPropertyValue = newPropertyValue;
+
+                return true;
+            }
+
+            return base.IsModified();
         }
 
-        private void CreateComplexField(GUILayout layout, string name, SerializableField.FieldType type, object obj)
+        protected override void Update(GUILayout layout)
         {
-            layout.AddElement(new GUILabel(name)); // TODO - Add foldout and hook up its callbacks
-
-            GUILayout childLayout = layout.AddLayoutX();
-            childLayout.AddSpace(IndentAmount);
+            base.Update(layout);
 
-            GUILayout childContent = childLayout.AddLayoutY();
+            if (!isInitialized)
+                Initialize(layout);
 
-            CreateComplexTypeChildren(childContent, type, obj);
+            // TODO - Update GUI element(s) with value from property
         }
 
-        private void CreateComplexTypeChildren(GUILayout layout, SerializableField.FieldType type, object obj)
+        public override void Destroy()
         {
-            if (obj == null)
-                return;
+            if (guiLabel != null)
+                guiLabel.Destroy();
 
-            switch (type)
-            {
-                case SerializableField.FieldType.Object:
-                    {
-                        SerializableObject serializableObject = new SerializableObject(obj);
-
-                        foreach (var field in serializableObject.fields)
-                        {
-                            if (!field.Inspectable)
-                                continue;
-
-                            CreateGUI(layout, field);
-                        }
-                    }
-                    break;
-                case SerializableField.FieldType.Array:
-                    {
-                        SerializableArrayInfo arrayInfo = new SerializableArrayInfo(obj);
-
-                        Array array = (Array)obj;
-                        if (array.Rank > 1) // TODO - I didn't bother implementing multi-rank arrays yet but it is a simple job
-                            throw new NotImplementedException();
-
-                        if (IsTypePrimitive(arrayInfo.ElementType))
-                        {
-                            for (int i = 0; i < array.GetLength(0); i++)
-                            {
-                                int curIdx = i; // To avoid lambda passing by reference
-
-                                Getter getter = () => array.GetValue(curIdx);
-                                Setter setter = (object value) => array.SetValue(value, curIdx);
-
-                                CreatePrimitiveField(layout, i + ".", arrayInfo.ElementType, getter, setter);
-                            }
-                        }
-                        else
-                        {
-                            for (int i = 0; i < array.GetLength(0); i++)
-                            {
-                                CreateComplexField(layout, i + ".", arrayInfo.ElementType, array.GetValue(i));
-                            }
-                        }
-                    }
-                    break;
-                case SerializableField.FieldType.List:
-                    {
-                        SerializableListInfo listInfo = new SerializableListInfo(obj);
-                        IList list = (IList)obj;
-
-                        if (IsTypePrimitive(listInfo.ElementType))
-                        {
-                            for (int i = 0; i < list.Count; i++)
-                            {
-                                int curIdx = i; // To avoid lambda passing by reference
-
-                                Getter getter = () => list.Item[curIdx];
-                                Setter setter = (object value) => list.Item[curIdx] = value;
-
-                                CreatePrimitiveField(layout, i + ".", listInfo.ElementType, getter, setter);
-                            }
-                        }
-                        else
-                        {
-                            for (int i = 0; i < array.GetLength(0); i++)
-                            {
-                                CreateComplexField(layout, i + ".", listInfo.ElementType, list.Item[i]);
-                            }
-                        }
-                    }
-                    break;
-                case SerializableField.FieldType.Dictionary:
-                    {
-                        SerializableDictionaryInfo dictionaryInfo = new SerializableDictionaryInfo(obj);
-                        IDictionary dictionary = (IDictionary)obj;
-
-                        IEnumerator enumerator = dictionary.Keys.GetEnumerator();
-                        int curIdx = 0;
-                        while (enumerator.MoveNext())
-                        {
-                            if (IsTypePrimitive(dictionaryInfo.KeyType))
-                            {
-                                Getter getter = () => enumerator.Current;
-                                Setter setter = (object value) => { }; // TODO - No setter!
-
-                                CreatePrimitiveField(layout, curIdx + ".", dictionaryInfo.KeyType, getter, setter);
-                            }
-                            else
-                            {
-                                CreateComplexField(layout, curIdx + ".", dictionaryInfo.KeyType, enumerator.Current);
-                            }
-
-                            curIdx++;
-                        }
-                    }
-                    break;
-                default:
-                    throw new Exception("Invalid complex field type.");
-            }
-        }
+            if (guiContentLayout != null)
+                guiContentLayout.Destroy();
 
-        private bool IsTypePrimitive(SerializableField.FieldType fieldType)
-        {
-            switch (fieldType)
-            {
-                case SerializableField.FieldType.Object:
-                    return false;
-                case SerializableField.FieldType.Array:
-                    return false;
-                case SerializableField.FieldType.List:
-                    return false;
-                case SerializableField.FieldType.Dictionary:
-                    return false;
-            }
+            if (guiChildLayout != null)
+                guiChildLayout.Destroy();
 
-            return true;
+            base.Destroy();
         }
     }
 }

+ 97 - 0
MBansheeEditor/Inspector/InspectableObjectBase.cs

@@ -0,0 +1,97 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using BansheeEngine;
+ 
+namespace BansheeEditor
+{
+    public class InspectableObjectBase
+    {
+        private List<InspectableObjectBase> children = new List<InspectableObjectBase>();
+        private InspectableObjectBase parent;
+
+        protected SerializableProperty property;
+        protected string title;
+
+        public InspectableObjectBase(string title, SerializableProperty property)
+        {
+            this.title = title;
+            this.property = property;
+        }
+
+        protected void AddChild(InspectableObjectBase child)
+        {
+            if (child.parent == this)
+                return;
+
+            if (child.parent != null)
+                child.parent.RemoveChild(child);
+
+            children.Add(child);
+            child.parent = this;
+        }
+
+        protected void RemoveChild(InspectableObjectBase child)
+        {
+            children.Remove(child);
+            child.parent = null;
+        }
+
+        public virtual void Refresh(GUILayout layout)
+        {
+            if (IsModified())
+                Update(layout);
+
+            for (int i = 0; i < children.Count; i++)
+                children[i].Refresh(layout);
+        }
+
+        protected virtual bool IsModified()
+        {
+            return false;
+        }
+
+        protected virtual void Update(GUILayout layout)
+        {
+            // Do nothing
+        }
+
+        public virtual void Destroy()
+        {
+            InspectableObjectBase[] childrenCopy = children.ToArray();
+            for (int i = 0; i < childrenCopy.Length; i++)
+                children[i].Destroy();
+
+            children.Clear();
+
+            if (parent != null)
+                parent.RemoveChild(this);
+        }
+
+        public static InspectableObjectBase CreateDefaultInspectable(string title, SerializableProperty property)
+        {
+            switch (property.Type)
+            {
+                case SerializableField.FieldType.Int:
+                    return new InspectableInt(title, property);
+                case SerializableField.FieldType.Object:
+                    return new InspectableObject(title, property);
+                case SerializableField.FieldType.Array:
+                    return new InspectableArray(title, property);
+                // TODO - Add all other types
+            }
+
+            throw new Exception("No inspector exists for the provided field type.");
+        }
+
+        public static InspectableObjectBase CreateCustomInspectable(Type inspectableType, string title, SerializableProperty property)
+        {
+            if (!inspectableType.IsSubclassOf(typeof (InspectableObjectBase)))
+                throw new Exception("Invalid inspector type.");
+
+            return (InspectableObjectBase)Activator.CreateInstance(inspectableType, title, property);
+        }
+    }
+}

+ 2 - 2
MBansheeEditor/Inspector/Inspector.cs

@@ -9,12 +9,12 @@ namespace BansheeEditor
     public abstract class Inspector
     {
         protected GUIPanel GUI;
-        protected SerializableObject serializableObject;
+        protected object referencedObject;
 
         internal void Initialize(GUIPanel gui, object instance)
         {
             GUI = gui;
-            serializableObject = new SerializableObject(instance);
+            referencedObject = instance;
         }
 
         internal void SetArea(int x, int y, int width, int height)

+ 3 - 0
MBansheeEditor/MBansheeEditor.csproj

@@ -45,7 +45,10 @@
     <Compile Include="EditorWindow.cs" />
     <Compile Include="Inspector\CustomInspector.cs" />
     <Compile Include="Inspector\GenericInspector.cs" />
+    <Compile Include="Inspector\InspectableArray.cs" />
     <Compile Include="Inspector\InspectableObject.cs" />
+    <Compile Include="Inspector\InspectableObjectBase.cs" />
+    <Compile Include="Inspector\InspectableInt.cs" />
     <Compile Include="Inspector\Inspector.cs" />
     <Compile Include="Inspector\InspectorWindow.cs" />
     <Compile Include="Program.cs" />

+ 2 - 3
MBansheeEngine/MBansheeEngine.csproj

@@ -84,11 +84,10 @@
     <Compile Include="Resource.cs" />
     <Compile Include="SceneObject.cs" />
     <Compile Include="ScriptObject.cs" />
-    <Compile Include="SerializableArrayInfo.cs" />
-    <Compile Include="SerializableDictionaryInfo.cs" />
+    <Compile Include="SerializableArray.cs" />
     <Compile Include="SerializableField.cs" />
-    <Compile Include="SerializableListInfo.cs" />
     <Compile Include="SerializableObject.cs" />
+    <Compile Include="SerializableProperty.cs" />
     <Compile Include="SerializeObject.cs" />
     <Compile Include="SerializeField.cs" />
     <Compile Include="SpriteTexture.cs" />

+ 9 - 8
MBansheeEngine/Program.cs

@@ -116,21 +116,22 @@ namespace BansheeEngine
                 Debug.Log(i + ". " + obj.fields[i].Name + " - " + obj.fields[i].Type.ToString());
             }
 
+            SerializableProperty prop = obj.fields[0].GetProperty();
+            Debug.Log("Old value: " + prop.GetValue<int>());
+            prop.SetValue<int>(33);
+            Debug.Log("New value: " + prop.GetValue<int>());
 
-            Debug.Log("Old value: " + obj.fields[0].GetValue<int>());
-            val.SetValue<int>(33);
-            Debug.Log("New value: " + obj.fields[0].GetValue<int>());
-
-            Debug.Log("Old value: " + (obj.fields[2].GetValue<DbgSerzCls>() == null));
+            SerializableProperty prop2 = obj.fields[2].GetProperty();
+            Debug.Log("Old value: " + (prop2.GetValue<DbgSerzCls>() == null));
 
             DbgSerzCls child = new DbgSerzCls();
             child.anotherValue2 = "ass";
-            obj.fields[2].SetValue<DbgSerzCls>(child);
+            prop2.SetValue<DbgSerzCls>(child);
 
-            if (obj.fields[2].GetValue<DbgSerzCls>() == null)
+            if (prop2.GetValue<DbgSerzCls>() == null)
                 Debug.Log("New value: null");
             else
-                Debug.Log("New value: " + obj.fields[2].GetValue<DbgSerzCls>().anotherValue2);
+                Debug.Log("New value: " + prop2.GetValue<DbgSerzCls>().anotherValue2);
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]

+ 40 - 0
MBansheeEngine/SerializableArray.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace BansheeEngine
+{
+    #pragma warning disable 649
+    public sealed class SerializableArray : ScriptObject
+    {
+        private SerializableField.FieldType elementType;
+        private Type internalElementType;
+        private Array referencedArray;
+
+        public SerializableField.FieldType ElementType
+        {
+            get { return elementType; }
+        }
+
+        internal SerializableArray(Array array)
+        {
+            referencedArray = array;
+            Internal_CreateInstance(this, referencedArray);
+        }
+
+        public SerializableProperty GetProperty(int elementIdx)
+        {
+            SerializableProperty.Getter getter = () => referencedArray.GetValue(elementIdx);
+            SerializableProperty.Setter setter = (object value) => referencedArray.SetValue(value, elementIdx);
+
+            return new SerializableProperty(ElementType, internalElementType, getter, setter);
+        }
+
+        public int GetLength()
+        {
+            return referencedArray.GetLength(0); // TODO - Support multi-rank arrays
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(SerializableArray instance, object obj);
+    }
+}

+ 0 - 28
MBansheeEngine/SerializableDictionaryInfo.cs

@@ -1,28 +0,0 @@
-using System.Runtime.CompilerServices;
-
-namespace BansheeEngine
-{
-    public sealed class SerializableDictionaryInfo : ScriptObject
-    {
-        private SerializableField.FieldType keyType;
-        private SerializableField.FieldType valueType;
-
-        public SerializableField.FieldType KeyType
-        {
-            get { return keyType; }
-        }
-
-        public SerializableField.FieldType ValueType
-        {
-            get { return valueType; }
-        }
-
-        public SerializableDictionaryInfo(object obj)
-        {
-            Internal_CreateInstance(this, obj);
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(SerializableDictionaryInfo instance, object obj);
-    }
-}

+ 14 - 37
MBansheeEngine/SerializableField.cs

@@ -47,6 +47,16 @@ namespace BansheeEngine
             get { return type; }
         }
 
+        public bool HasCustomInspector
+        {
+            get { return false; } // TODO - Add [UseCustomInspector(typeof(InspecableType))] attribute and parse it
+        }
+
+        public Type CustomInspectorType
+        {
+            get { return null; } // TODO - See above. Return type from UseCustomInspector attribute
+        }
+
         public string Name
         {
             get { return name; }
@@ -119,7 +129,6 @@ namespace BansheeEngine
                     throw new Exception("Cannot determine field type. Found an unsupported generic type.");
                 }
 
-
                 // Otherwise the type must be an object, unless some error occurred
                 return FieldType.Object;
             }
@@ -127,44 +136,12 @@ namespace BansheeEngine
             return FieldType.Array;
         }
 
-        public T GetValue<T>()
-        {
-            if (!typeof(T).IsAssignableFrom(internalType))
-                throw new Exception("Attempted to retrieve a serializable value using an invalid type. Provided type: " + typeof(T) + ". Needed type: " + internalType);
-
-            return (T)Internal_GetValue(mCachedPtr, parent.referencedObject);
-        }
-
-        public void SetValue<T>(T value)
-        {
-            if (!typeof(T).IsAssignableFrom(internalType))
-                throw new Exception("Attempted to set a serializable value using an invalid type. Provided type: " + typeof(T) + ". Needed type: " + internalType);
-
-            Internal_SetValue(mCachedPtr, parent.referencedObject, value);
-        }
-
-        public SerializableArrayInfo GetSerializableArrayInfo()
-        {
-            if (type != FieldType.Array)
-                throw new Exception("Attempting to retrieve array information from a field that doesn't contain an array.");
-
-            return new SerializableArrayInfo(GetValue<object>());
-        }
-
-        public SerializableListInfo GetSerializableListInfo()
-        {
-            if (type != FieldType.List)
-                throw new Exception("Attempting to retrieve array information from a field that doesn't contain an array.");
-
-            return new SerializableListInfo(GetValue<object>());
-        }
-
-        public SerializableDictionaryInfo GetSerializableDictionaryInfo()
+        public SerializableProperty GetProperty()
         {
-            if (type != FieldType.Dictionary)
-                throw new Exception("Attempting to retrieve array information from a field that doesn't contain an array.");
+            SerializableProperty.Getter getter = () => Internal_GetValue(mCachedPtr, parent.referencedObject);
+            SerializableProperty.Setter setter = (object value) => Internal_SetValue(mCachedPtr, parent.referencedObject, value);
 
-            return new SerializableDictionaryInfo(GetValue<object>());
+            return new SerializableProperty(type, internalType, getter, setter);
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]

+ 1 - 0
MBansheeEngine/SerializableObject.cs

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
 
 namespace BansheeEngine
 {
+    #pragma warning disable 649
     public sealed class SerializableObject : ScriptObject
     {
         internal object referencedObject;

+ 63 - 0
MBansheeEngine/SerializableProperty.cs

@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public sealed class SerializableProperty
+    {
+        internal delegate object Getter();
+        internal delegate void Setter(object value);
+
+        private SerializableField.FieldType type;
+        private Type internalType;
+        private Getter getter;
+        private Setter setter;
+
+        internal SerializableProperty(SerializableField.FieldType type, Type internalType, Getter getter, Setter setter)
+        {
+            this.type = type;
+            this.internalType = internalType;
+            this.getter = getter;
+            this.setter = setter;
+        }
+
+        public SerializableField.FieldType Type
+        {
+            get { return type; }
+        }
+
+        public T GetValue<T>()
+        {
+            if (!typeof(T).IsAssignableFrom(internalType))
+                throw new Exception("Attempted to retrieve a serializable value using an invalid type. Provided type: " + typeof(T) + ". Needed type: " + internalType);
+
+            return (T)getter();
+        }
+
+        public void SetValue<T>(T value)
+        {
+            if (!typeof(T).IsAssignableFrom(internalType))
+                throw new Exception("Attempted to set a serializable value using an invalid type. Provided type: " + typeof(T) + ". Needed type: " + internalType);
+
+            setter(value);
+        }
+
+        public SerializableObject GetObject()
+        {
+            if (type != SerializableField.FieldType.Object)
+                throw new Exception("Attempting to retrieve object information from a field that doesn't contain an object.");
+
+            return new SerializableObject(GetValue<object>());
+        }
+
+        public SerializableArray GetArray()
+        {
+            if (type != SerializableField.FieldType.Array)
+                throw new Exception("Attempting to retrieve array information from a field that doesn't contain an array.");
+
+            return new SerializableArray(GetValue<Array>());
+        }
+    }
+}