Esempio n. 1
0
 /// <summary>
 /// Constructor for internal use by the runtime.
 /// </summary>
 /// <param name="parent">Object that conains the field.</param>
 /// <param name="name">Name of the field.</param>
 /// <param name="flags">Flags that control whether the field is inspectable or serializable.</param>
 /// <param name="internalType">Internal C# type of the field.</param>
 private SerializableField(SerializableObject parent, string name, int flags, Type internalType)
 {
     this.parent = parent;
     this.name = name;
     this.flags = flags;
     this.type = SerializableProperty.DetermineFieldType(internalType);
     this.internalType = internalType;
 }
Esempio n. 2
0
        /// <inheritdoc/>
        protected internal override void Initialize()
        {
            if (InspectedObject != null)
            {
                int currentIndex = 0;
                SerializableObject serializableObject = new SerializableObject(InspectedObject.GetType(), InspectedObject);
                foreach (var field in serializableObject.Fields)
                {
                    if (!field.Inspectable)
                        continue;

                    string path = field.Name;
                    InspectableField inspectableField = InspectableField.CreateInspectable(this, field.Name, path,
                        currentIndex, 0, new InspectableFieldLayout(Layout), field.GetProperty());

                    inspectableFields.Add(inspectableField);
                    isEmpty = false;

                    currentIndex += inspectableField.GetNumLayoutElements();
                }

                base.SetVisible(!isEmpty);
            }
        }
 private static extern void Internal_CreateInstance(SerializableObject instance, Type objectType);
Esempio n. 4
0
        /// <summary>
        /// Searches the scene object hierarchy to find a property at the given path.
        /// </summary>
        /// <param name="root">Root scene object to which the path is relative to.</param>
        /// <param name="path">Path to the property, where each element of the path is separated with "/".
        ///
        ///                    Path elements prefixed with "!" signify names of child scene objects (first one relative to
        ///                    <paramref name="root"/>. Name of the root element should not be included in the path.
        ///
        ///                    Path element prefixed with ":" signify names of components. If a path doesn't have a
        ///                    component element, it is assumed the field is relative to the scene object itself (only
        ///                    "Position", "Rotation" and "Scale" fields are supported in such case). Only one component
        ///                    path element per path is allowed.
        ///
        ///                    Path entries with no prefix are considered regular script object fields. Each path must have
        ///                    at least one such entry.
        ///
        ///                    A field path can be followed by an indexer [n] where n is a zero-based index. Such paths
        ///                    are assumed to be referencing an index within an array or a list.
        ///
        ///                    A field path can also be followed by a suffix (after the indexer, if any) separated from the
        ///                    path name with ".". This suffix is not parsed internally, but will be returned as
        ///                    <paramref name="suffix"/>.
        ///
        ///                    Path examples:
        ///                     :MyComponent/myInt (path to myInt variable on a component attached to the root object)
        ///                     :MyComponent/myArray[0] (path to first element of myArray on the same component as above)
        ///                     !childSO/:MyComponent/myInt (path to myInt variable on a child scene object)
        ///                     !childSO/Position (path to the scene object position)
        ///                     :MyComponent/myVector.z (path to the z component of myVector on the root object)
        /// </param>
        /// <param name="suffix">Suffix of the last field entry, if it has any. Contains the suffix separator ".".</param>
        /// <returns>If found, property object you can use for setting and getting the value from the property, otherwise
        ///          null.</returns>
        internal static SerializableProperty FindProperty(SceneObject root, string path, out string suffix)
        {
            suffix = null;

            if (string.IsNullOrEmpty(path) || root == null)
            {
                return(null);
            }

            string trimmedPath = path.Trim('/');

            string[] entries = trimmedPath.Split('/');

            // Find scene object referenced by the path
            SceneObject so      = root;
            int         pathIdx = 0;

            for (; pathIdx < entries.Length; pathIdx++)
            {
                string entry = entries[pathIdx];

                if (string.IsNullOrEmpty(entry))
                {
                    continue;
                }

                // Not a scene object, break
                if (entry[0] != '!')
                {
                    break;
                }

                string childName = entry.Substring(1, entry.Length - 1);
                so = so.FindChild(childName);

                if (so == null)
                {
                    break;
                }
            }

            // Child scene object couldn't be found
            if (so == null)
            {
                return(null);
            }

            // Path too short, no field entry
            if (pathIdx >= entries.Length)
            {
                return(null);
            }

            // Check if path is referencing a component, and if so find it
            Component component = null;

            {
                string entry = entries[pathIdx];
                if (entry[0] == ':')
                {
                    string componentName = entry.Substring(1, entry.Length - 1);

                    Component[] components = so.GetComponents();
                    component = Array.Find(components, x => x.GetType().Name == componentName);

                    // Cannot find component with specified type
                    if (component == null)
                    {
                        return(null);
                    }
                }
            }

            // Look for a field within a component
            if (component != null)
            {
                pathIdx++;
                if (pathIdx >= entries.Length)
                {
                    return(null);
                }

                SerializableObject componentObj = new SerializableObject(component);

                StringBuilder pathBuilder = new StringBuilder();
                for (; pathIdx < entries.Length - 1; pathIdx++)
                {
                    pathBuilder.Append(entries[pathIdx] + "/");
                }

                // Check last path entry for suffix and remove it
                int suffixIdx = entries[pathIdx].LastIndexOf(".");
                if (suffixIdx != -1)
                {
                    string entryNoSuffix = entries[pathIdx].Substring(0, suffixIdx);
                    suffix = entries[pathIdx].Substring(suffixIdx, entries[pathIdx].Length - suffixIdx);

                    pathBuilder.Append(entryNoSuffix);
                }
                else
                {
                    pathBuilder.Append(entries[pathIdx]);
                }

                return(componentObj.FindProperty(pathBuilder.ToString()));
            }
            else // Field is one of the builtin ones on the SceneObject itself
            {
                if ((pathIdx + 1) < entries.Length)
                {
                    return(null);
                }

                string entry = entries[pathIdx];
                if (entry == "Position")
                {
                    SerializableProperty property = new SerializableProperty(
                        SerializableProperty.FieldType.Vector3,
                        typeof(Vector3),
                        () => so.LocalPosition,
                        (x) => so.LocalPosition = (Vector3)x);

                    return(property);
                }
                else if (entry == "Rotation")
                {
                    SerializableProperty property = new SerializableProperty(
                        SerializableProperty.FieldType.Vector3,
                        typeof(Vector3),
                        () => so.LocalRotation.ToEuler(),
                        (x) => so.LocalRotation = Quaternion.FromEuler((Vector3)x));

                    return(property);
                }
                else if (entry == "Scale")
                {
                    SerializableProperty property = new SerializableProperty(
                        SerializableProperty.FieldType.Vector3,
                        typeof(Vector3),
                        () => so.LocalScale,
                        (x) => so.LocalScale = (Vector3)x);

                    return(property);
                }

                return(null);
            }
        }
Esempio n. 5
0
 private static extern void Internal_CreateInstance(SerializableObject instance, Type objectType);
        /// <summary>
        /// Expands or collapses the set of rows displaying all fields of a serializable object.
        /// </summary>
        /// <param name="parent">Parent row element whose children to expand/collapse.</param>
        /// <param name="obj">Object describing the type of the object whose fields to display.</param>
        /// <param name="expand">True to expand, false to collapse.</param>
        private void ToggleObjectFoldout(Element parent, SerializableObject obj,
            bool expand)
        {
            parent.childLayout.Clear();
            parent.children = null;

            parent.indentLayout.Active = expand;

            if (expand)
                AddObjectRows(parent, obj);
        }
        /// <summary>
        /// Registers a set of rows for all components in a <see cref="SceneObject"/>, as well as its transform and
        /// child objects.
        /// </summary>
        /// <param name="parent">Row element under which to create the new rows.</param>
        private void AddSceneObjectRows(Element parent)
        {
            string soName = "!" + parent.so.Name;
            Component[] components = parent.so.GetComponents();

            parent.children = new Element[components.Length + 2];

            SpriteTexture soIcon = EditorBuiltin.GetEditorIcon(EditorIcon.SceneObject);
            SpriteTexture compIcon = EditorBuiltin.GetEditorIcon(EditorIcon.Component);

            // Add transform
            parent.children[0] = AddFoldoutRow(parent.childLayout, soIcon, "Transform", parent.so,
                null, parent.path + "/" + soName, ToggleTransformFoldout);

            // Add components
            for (int i = 0; i < components.Length; i++)
            {
                Component childComponent = components[i];
                Action<Element, bool> toggleCallback =
                    (toggleParent, expand) =>
                {
                    SerializableObject componentObject = new SerializableObject(childComponent.GetType(), childComponent);
                    ToggleObjectFoldout(toggleParent, componentObject, expand);
                };

                string name = childComponent.GetType().Name;
                string path = parent.path + "/" + soName + "/:" + name;
                parent.children[i + 1] = AddFoldoutRow(parent.childLayout, compIcon, name, parent.so, childComponent, path,
                    toggleCallback);
            }

            // Add children
            if (parent.so.GetNumChildren() > 0)
            {
                parent.children[parent.children.Length - 1] = AddFoldoutRow(parent.childLayout, soIcon, "Children",
                    parent.so, null, parent.path + "/" + soName, ToggleChildFoldout);
            }
        }
        /// <summary>
        /// Registers a new row in the layout for the provided property. The type of row is determined by the property type.
        /// </summary>
        /// <param name="parent">Parent foldout row to which to append the new elements.</param>
        /// <param name="name">Name of the field the property belongs to.</param>
        /// <param name="path">Slash separated path to the field from its parent object.</param>
        /// <param name="property">Property to create the row for.</param>
        /// <param name="element">Element containing data for the newly created row. Only valid if method returns true.
        ///                       </param>
        /// <returns>Returns true if the row was successfully added, false otherwise.</returns>
        private bool AddPropertyRow(Element parent, string name, string path, SerializableProperty property, 
            out Element element)
        {
            switch (property.Type)
            {
                case SerializableProperty.FieldType.Bool:
                case SerializableProperty.FieldType.Float:
                case SerializableProperty.FieldType.Int:
                case SerializableProperty.FieldType.Color:
                case SerializableProperty.FieldType.Vector2:
                case SerializableProperty.FieldType.Vector3:
                case SerializableProperty.FieldType.Vector4:
                    element = AddFieldRow(parent.childLayout, name, parent.so, parent.comp, path, property.Type);
                    return true;
                case SerializableProperty.FieldType.Object:
                    {
                        Action<Element, bool> toggleCallback =
                            (toggleParent, expand) =>
                            {
                                SerializableObject childObject = new SerializableObject(property.InternalType, null);
                                ToggleObjectFoldout(toggleParent, childObject, expand);
                            };

                        element = AddFoldoutRow(parent.childLayout, null, name, parent.so, parent.comp, path, toggleCallback);
                    }
                    return true;
                case SerializableProperty.FieldType.Array:
                    {
                        Action<Element, bool> toggleCallback =
                            (toggleParent, expand) =>
                            {
                                SerializableArray childObject = property.GetArray();

                                if (childObject != null)
                                    ToggleArrayFoldout(toggleParent, childObject, expand);
                            };

                        element = AddFoldoutRow(parent.childLayout, null, name, parent.so, parent.comp, path, toggleCallback);
                    }
                    return true;
                case SerializableProperty.FieldType.List:
                    {
                        Action<Element, bool> toggleCallback =
                            (toggleParent, expand) =>
                            {
                                SerializableList childObject = property.GetList();

                                if (childObject != null)
                                    ToggleListFoldout(toggleParent, childObject, expand);
                            };

                        element = AddFoldoutRow(parent.childLayout, null, name, parent.so, parent.comp, path, toggleCallback);
                    }
                    return true;
            }

            element = new Element();
            return false;
        }
        /// <summary>
        /// Registers a set of rows for all child fields of the provided object.
        /// </summary>
        /// <param name="parent">Parent foldout row to which to append the new elements.</param>
        /// <param name="serializableObject">Type of the object whose fields to display.</param>
        private void AddObjectRows(Element parent, SerializableObject serializableObject)
        {
            List<Element> elements = new List<Element>();
            foreach (var field in serializableObject.Fields)
            {
                if (!field.Animable)
                    continue;

                string propertyPath = parent.path + "/" + field.Name;

                Element element;
                if(AddPropertyRow(parent, field.Name, propertyPath, field.GetProperty(), out element))
                    elements.Add(element);
            }

            // Handle special fields
            if (serializableObject.Type == typeof(Animation))
            {
                Animation anim = serializableObject.Object as Animation;
                MorphShapes morphShapes = anim?.SceneObject.GetComponent<Renderable>()?.Mesh?.MorphShapes;

                if (morphShapes != null)
                {
                    string propertyPath = parent.path + "/MorphShapes";

                    Action<Element, bool> toggleCallback =
                        (toggleParent, expand) =>
                        {
                            toggleParent.childLayout.Clear();
                            toggleParent.children = null;

                            toggleParent.indentLayout.Active = expand;

                            if (expand)
                            {
                                List<Element> childElements = new List<Element>();
                                MorphChannel[] channels = morphShapes.Channels;
                                for (int i = 0; i < channels.Length; i++)
                                {
                                    string channelName = channels[i].Name;

                                    string framePropertyPath = parent.path + "/MorphShapes/Frames/" + channelName;
                                    string weightPropertyPath = parent.path + "/MorphShapes/Weight/" + channelName;

                                    childElements.Add(AddFieldRow(toggleParent.childLayout, channelName + " (Frames)",
                                        toggleParent.so, toggleParent.comp, framePropertyPath,
                                        SerializableProperty.FieldType.Float));

                                    childElements.Add(AddFieldRow(toggleParent.childLayout, channelName + " (Weight)",
                                        toggleParent.so, toggleParent.comp, weightPropertyPath,
                                        SerializableProperty.FieldType.Float));
                                }

                                toggleParent.children = childElements.ToArray();
                            }
                        };

                    elements.Add(AddFoldoutRow(parent.childLayout, null, "MorphShapes", parent.so, parent.comp,
                        propertyPath, toggleCallback));
                }
            }

            parent.children = elements.ToArray();
        }
Esempio n. 10
0
        /// <summary>
        /// Tests serializable properties used for inspection.
        /// </summary>
        static void UnitTest2_SerializableProperties()
        {
            SerializableObject obj = new SerializableObject(typeof(UT1_SerzCls), new UT1_SerzCls());

            SerializableProperty prop = obj.Fields[0].GetProperty();
            prop.SetValue(33);
            DebugUnit.Assert(prop.GetValue<int>() == 33);

            SerializableProperty prop2 = obj.Fields[2].GetProperty();

            UT1_SerzCls child = new UT1_SerzCls();
            child.anotherValue2 = "potato";
            prop2.SetValue<UT1_SerzCls>(child);

            DebugUnit.Assert(prop2.GetValue<UT1_SerzCls>() != null);
            DebugUnit.Assert(prop2.GetValue<UT1_SerzCls>().anotherValue2 == "potato");
        }
Esempio n. 11
0
        /// <summary>
        /// Searches the scene object hierarchy to find a property at the given path.
        /// </summary>
        /// <param name="root">Root scene object to which the path is relative to.</param>
        /// <param name="path">Path to the property, where each element of the path is separated with "/". 
        /// 
        ///                    Path elements prefixed with "!" signify names of child scene objects (first one relative to 
        ///                    <paramref name="root"/>. Name of the root element should not be included in the path. 
        /// 
        ///                    Path element prefixed with ":" signify names of components. If a path doesn't have a
        ///                    component element, it is assumed the field is relative to the scene object itself (only 
        ///                    "Translation", "Rotation" and "Scale" fields are supported in such case). Only one component
        ///                    path element per path is allowed.
        /// 
        ///                    Path entries with no prefix are considered regular script object fields. Each path must have
        ///                    at least one such entry. 
        /// 
        ///                    A field path can be followed by an indexer [n] where n is a zero-based index. Such paths
        ///                    are assumed to be referencing an index within an array or a list.
        /// 
        ///                    A field path can also be followed by a suffix (after the indexer, if any) separated from the
        ///                    path name with ".". This suffix is not parsed internally, but will be returned as 
        ///                    <paramref name="suffix"/>.
        /// 
        ///                    Path examples:
        ///                     :MyComponent/myInt (path to myInt variable on a component attached to this object)
        ///                     :MyComponent/myArray[0] (path to first element of myArray on the same component)
        ///                     !childSO/:MyComponent/myInt (path to myInt variable on a child scene object)
        ///                     !childSO/Translation (path to the scene object translation)
        ///                     :MyComponent/myVector.z (path to the z component of myVector on this object)
        ///                    </param>
        /// <param name="suffix">Suffix of the last field entry, if it has any. Contains the suffix separator ".".</param>
        /// <returns>If found, property object you can use for setting and getting the value from the property, otherwise 
        ///          null.</returns>
        internal static SerializableProperty FindProperty(SceneObject root, string path, out string suffix)
        {
            suffix = null;

            if (string.IsNullOrEmpty(path) || root == null)
                return null;

            string trimmedPath = path.Trim('/');
            string[] entries = trimmedPath.Split('/');

            // Find scene object referenced by the path
            SceneObject so = root;
            int pathIdx = 0;
            for (; pathIdx < entries.Length; pathIdx++)
            {
                string entry = entries[pathIdx];

                if (string.IsNullOrEmpty(entry))
                    continue;

                // Not a scene object, break
                if (entry[0] != '!')
                    break;

                string childName = entry.Substring(1, entry.Length - 1);
                so = so.FindChild(childName);

                if (so == null)
                    break;
            }

            // Child scene object couldn't be found
            if (so == null)
                return null;

            // Path too short, no field entry
            if (pathIdx >= entries.Length)
                return null;

            // Check if path is referencing a component, and if so find it
            Component component = null;
            {
                string entry = entries[pathIdx];
                if (entry[0] == ':')
                {
                    string componentName = entry.Substring(1, entry.Length - 1);

                    Component[] components = so.GetComponents();
                    component = Array.Find(components, x => x.GetType().Name == componentName);

                    // Cannot find component with specified type
                    if (component == null)
                        return null;
                }
            }

            // Look for a field within a component
            if (component != null)
            {
                pathIdx++;
                if (pathIdx >= entries.Length)
                    return null;

                SerializableObject componentObj = new SerializableObject(component);

                StringBuilder pathBuilder = new StringBuilder();
                for (; pathIdx < entries.Length - 1; pathIdx++)
                    pathBuilder.Append(entries[pathIdx] + "/");

                // Check last path entry for suffix and remove it
                int suffixIdx = entries[pathIdx].LastIndexOf(".");
                if (suffixIdx != -1)
                {
                    string entryNoSuffix = entries[pathIdx].Substring(0, suffixIdx);
                    suffix = entries[pathIdx].Substring(suffixIdx, entries[pathIdx].Length - suffixIdx);

                    pathBuilder.Append(entryNoSuffix);
                }
                else
                    pathBuilder.Append(entries[pathIdx]);

                return componentObj.FindProperty(pathBuilder.ToString());
            }
            else // Field is one of the builtin ones on the SceneObject itself
            {
                if ((pathIdx + 1) < entries.Length)
                    return null;

                string entry = entries[pathIdx];
                if (entry == "Position")
                {
                    SerializableProperty property = new SerializableProperty(
                        SerializableProperty.FieldType.Vector3,
                        typeof(Vector3),
                        () => so.LocalPosition,
                        (x) => so.LocalPosition = (Vector3)x);

                    return property;
                }
                else if (entry == "Rotation")
                {
                    SerializableProperty property = new SerializableProperty(
                        SerializableProperty.FieldType.Vector3,
                        typeof(Vector3),
                        () => so.LocalRotation.ToEuler(),
                        (x) => so.LocalRotation = Quaternion.FromEuler((Vector3)x));

                    return property;
                }
                else if (entry == "Scale")
                {
                    SerializableProperty property = new SerializableProperty(
                        SerializableProperty.FieldType.Vector3,
                        typeof(Vector3),
                        () => so.LocalScale,
                        (x) => so.LocalScale = (Vector3)x);

                    return property;
                }

                return null;
            }
        }