using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace AdvancedInspector { /// /// ScriptableObject are not serializable within the scope of a GameObject. /// Therefore, they are improper to prefab, copy and so on. /// This class' goal is to provide a polymorphic solution and give an easy way of handling up front. /// /// Derived from Object; no polymorphism supported. /// Derived from ScriptableObject; cannot be prefabed, copied, duplicated or instanced. /// Derived from MonoBehaviour; can only live on a GameObject. /// /// A ComponentMonoBehaviour is created from another MonoBehaviour; its parent. /// A parent can be another ComponentMonoBehaviour. /// If the parent is destroyed, the subcomponent is destroyed too. /// If a subcomponent is found without a parent, it's destroyed too. /// [AdvancedInspector] public abstract class ComponentMonoBehaviour : MonoBehaviour { [SerializeField] private MonoBehaviour owner; /// /// The owner of a "subcomponent". /// Use to know if this component lost its parent. /// If so, the AdvancedInspector will delete any unused component. /// public MonoBehaviour Owner { get { return owner; } set { if (value != null) owner = value; } } /// /// A subcomponent is not visible the normal way in the Inspector. /// It's shown as being part of another item. /// protected virtual void Reset() { hideFlags = HideFlags.HideInInspector; } /// /// Called when the inspector is about to destroy this one. /// Loop in all the internal and destroy sub-components. /// public void Erase() { foreach (FieldInfo info in GetFields(GetType(), false)) { object value = info.GetValue(this); if (value is ComponentMonoBehaviour) { ComponentMonoBehaviour component = value as ComponentMonoBehaviour; if (component.Owner == Owner) component.Erase(); } } DestroyImmediate(this, true); } /// /// Instanciate an existing Component on the same owner GameObject /// public ComponentMonoBehaviour Instantiate() { return Instantiate(gameObject, Owner); } /// /// Instanciate an existing Component on the same owner GameObject but with a new onwer. /// public ComponentMonoBehaviour Instantiate(MonoBehaviour owner) { return Instantiate(gameObject, owner); } /// /// Instanciate an existing Component on the target GameObject. /// public ComponentMonoBehaviour Instantiate(GameObject go, MonoBehaviour owner) { return CopyObject(go, owner, this) as ComponentMonoBehaviour; } private static object CopyObject(GameObject go, MonoBehaviour owner, object original) { if (original == null) return null; Type type = original.GetType(); if (type == typeof(string)) return ((string)original).Clone(); else if (type.Namespace == "System") return original; else if (typeof(IList).IsAssignableFrom(type)) return CopyList(go, owner, (IList)original); else if (typeof(ComponentMonoBehaviour).IsAssignableFrom(type) && ((ComponentMonoBehaviour)original).Owner == owner) return CopyComponent(go, owner, (ComponentMonoBehaviour)original); else if (typeof(Component).IsAssignableFrom(type)) return original; else if (typeof(ScriptableObject).IsAssignableFrom(type)) return ScriptableObject.Instantiate((ScriptableObject)original); else if (typeof(UnityEngine.Object).IsAssignableFrom(type)) return original; else if (type.IsClass) return CopyClass(go, owner, original); else return original; } private static IList CopyList(GameObject go, MonoBehaviour owner, IList original) { Type type = original.GetType(); IList copy; if (type.IsArray) { copy = Array.CreateInstance(type.GetElementType(), original.Count); for (int i = 0; i < original.Count; i++) copy[i] = CopyObject(go, owner, original[i]); } else { copy = Activator.CreateInstance(type) as IList; for (int i = 0; i < original.Count; i++) copy.Add(CopyObject(go, owner, original[i])); } return copy; } private static ComponentMonoBehaviour CopyComponent(GameObject go, MonoBehaviour owner, ComponentMonoBehaviour original) { Type type = original.GetType(); ComponentMonoBehaviour copy = go.AddComponent(original.GetType()) as ComponentMonoBehaviour; foreach (FieldInfo info in GetFields(type, false)) { if (info.IsLiteral) continue; info.SetValue(copy, CopyObject(go, copy, info.GetValue(original))); } copy.Owner = owner; return copy; } private static object CopyClass(GameObject go, MonoBehaviour owner, object original) { Type type = original.GetType(); object copy = Activator.CreateInstance(type); foreach (FieldInfo info in GetFields(type, false)) { if (info.IsLiteral) continue; info.SetValue(copy, CopyObject(go, owner, info.GetValue(original))); } return copy; } private static List GetFields(Type type, bool recursive) { List infos; if (recursive) infos = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance).ToList(); else infos = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).ToList(); if (type.BaseType != null && type.BaseType != typeof(object)) infos.AddRange(GetFields(type.BaseType, true)); return infos; } } }