Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Full Inspector: Inspector and serialization for structs, dicts, generics, interfaces

Discussion in 'Assets and Asset Store' started by sient, Jan 23, 2014.

  1. Zicandar

    Zicandar

    Joined:
    Feb 10, 2014
    Posts:
    388
    Hello!
    Really really awesome asset! One of the best I've got from the asset store!
    One "bug" I've found: floats stored in "object" types seem to be deserialized as Double's when using JSON.

    And one wish I have: Could you put in release dates in the change-log? Would be easier keeping track of what new stuff has come compared to the version you have released!

    Other then that, really awesome asset!

    // Zicandar
     
  2. sanpats

    sanpats

    Joined:
    Aug 24, 2011
    Posts:
    343
  3. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Ouch, that's a bug with Json.NET itself. If you're using the version of Json.NET that FI ships with, I would suggest submitting an issue here. If you're using the version from the asset store, I'd suggest submitting a bug to the maintainer.

    Sure, I'll try and include them in the next update. You can currently see all of the release dates here.

    Thanks.
     
  4. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    I don't see a reason why it wouldn't. Unity Serializer deals with serializing the scene, while FI deals with behavior specific serialization. FI is completely compatible with the default Unity serialization system after calling SaveState(), so it should work fine with Unity Serializer.

    If, for some reason, you can't call SaveState() on the objects before serialization (for example, you don't know which objects will be serialized), there is the shotgun style approach with FullInspectorSaveManager.SaveAll(). Fair warning though, it won't be as fast as manually saving objects (it saves every object and uses reflection / Unity APIs to query for all of the objects).

    Let me know if you have other questions.

    Thanks.
     
  5. mcmorry

    mcmorry

    Joined:
    Dec 2, 2012
    Posts:
    580
    I'm interested too on this feature. I tried to use the files on github but I get a compilation error:
    Assets/FullInspector2/Modules/Attributes/Editor/SingleItemEditorAttributePropertyEditor.cs(13,62): error CS0266: Cannot implicitly convert type `FullInspector.PropertyEditorChain' to `FullInspector.IPropertyEditor'. An explicit conversion exists (are you missing a cast?)

    I changed the line 13 to
    Code (csharp):
    1.  
    2. private IPropertyEditor _itemEditor = PropertyEditor.Get(typeof(T), null).FirstEditor;
    3.  
    It works, but I'm not sure if it's correct. Could you please confirm?
    Thanks
     
  6. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Yea, that's the correct change. However, in 2.3 FI ships with this feature by default. Simply annotate an IList type with [SingleItemListEditor] and you're good to go, no code importing needed. The version of GitHub is also out-of-date and the version that FI ships with looks nicer.

    Here's an image of the updated version:


    ---

    ---


    Thanks.
     
  7. mcmorry

    mcmorry

    Joined:
    Dec 2, 2012
    Posts:
    580
    Oh thanks! I didn't find this info in the change log.

    I'm really enjoying using FI2. It's a master piece of work that brings Unity to a new level!
    Before to use this I had only two options to setup my game levels properly. Create a special scene to be used as level editor to test any random combinations, or to create custom editors for the inspector. In Both case it required a lot of time. Now I can use directly my classes as DB and build on it some editor only code to test directly in the editor mode my settings (thanks to the InspectorButton attribute) without even going in play mode!
     
  8. mcmorry

    mcmorry

    Joined:
    Dec 2, 2012
    Posts:
    580
    I've seen that I can't use divider and margin attributes with buttons. Could you please include this feature?

    Also is there a way to access, from an editor script, the current selected item of the SingleItemListEditor attribute?

    Thanks
     
  9. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    I'm glad to see you're enjoying Full Inspector. :)

    I'll get to work on it. I've created an issue Also is there a way to access, from an editor script, the current selected item of the SingleItemListEditor attribute?
    [/QUOTE]

    I haven't had a chance to test this code, but here's some steps for how to get the edited item:

    First, you need to make a simple modification to SingleItemEditorAttributePropertyEditor to make the metadata public.
    Code (csharp):
    1.  
    2. // change line 278 of FullInspector2\Modules\Collections\Editor\SingleItemEditorAttributePropertyEditor.cs from
    3. private class ItemMetadata {
    4.     public int CurrentIndex;
    5. }
    6.  
    7. // to
    8. public class ItemMetadata {
    9.     public int CurrentIndex;
    10. }
    11.  
    After that, if you have the edited list you can access the currently edited item with
    Code (csharp):
    1.  
    2. using FullInspector.Internal;
    3.  
    4. bool TryGetActiveEditingItem(IList<T> list, out T item) {
    5.     var metadata = ObjectMetadata<SingleItemEditorAttributePropertyEditor.ItemMetadata>.Get(list);
    6.     int index = metadata.CurrentIndex;
    7.  
    8.     if (index < 0 || index >= list.Count) {
    9.         item = default(T);
    10.         return false;
    11.     }
    12.  
    13.     item = list[index];
    14.     return true;
    15. }
    16.  
    Let me know if there is anything else I can do.

    Thanks.
     
  10. Cripplette

    Cripplette

    Joined:
    Sep 18, 2012
    Posts:
    66
    Hi,

    I have tested your asset and it is really awesome, but I don't want to dig too much into it ....
    This asset is too much low-level, I can't just use it in a project, every single feature of it should be available natively in Unity.

    UT should recruit you and let you rewrite their inspectors/serialization which lack so many features.

    Congratz
     
  11. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    I'm really excited about 2.4 -- it's going to enhance productivity even more by providing a more intelligent inspector that just works.

    Here's a sneak peak of what I call inline object editing:

    $Full Inspector Inline Object Editor.gif

    All done with the minimal code below:

    $YscLzy3.png
     
  12. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Hey,

    As you are all probably aware, Unity 4.5 came out today. It includes ISerializationCallbackReceiver, which greatly simplifies some FI user stories.

    FI 2.4 will be targeting Unity 4.5. If you need a custom version for Unity 4.3, send me an email and we'll work something out.

    Thanks
     
  13. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hey @sient: Wonder if you could help me out here. Trying to setup protobuf-net in Unity to try out and mess with on my own. I read that there's some pre-compiled step you have to do first (ex). But In your examples, I never saw anything like that. You just include protobuf and use it normally. When should I worry about that pre-compiled thing? - There's also a protogen folder... Can you tell me about the setup required in general?

    Thank you!
     
    Last edited: May 28, 2014
  14. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    protobuf-net works fine for non-AOT platforms without any additional work, so you can just use it as is and everything should be fine.

    If you're using an AOT platform, like iOS, then protobuf-net needs extra work as it uses Reflection.Emit to create serializers. However, it supports creating precompiled serializer assemblies. Due to some assembly reference requirements, you're going to have to separate all of your [ProtoContract] types into a separate dll (a DTO or data-type only assembly) so that it doesn't reference scripts in Assembly-CSharp, etc. Due to the additional Assembly-CSharp non-reference requirement, you'll have to compile Full Inspector into a separate DLL as well (I can probably provide these if you need them). So, in the end you'll have FullInspector.dll, DataTypes.dll, and then the rest of your code. Whenever you update one of your serialized types you have to recompile DataTypes.dll. Full Inspector includes an experimental "Compile protobuf-net serialization DLL" that attempts to automatically create the DataTypes.dll, but I've had issues with it in the past with assembly references.

    So, it's complicated, but definately possible to use protobuf-net on AOT platforms.

    I have a section in the docs on this here.

    Let me know if that clarifies things.

    Thanks
     
  15. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Thanks for your reply. But I thought that Unity uses AOT compilation on all platforms? otherwise why can't we use say, 'dynamic'? is it because the Mono version is quite old, or cause of the AOT thing?

    So you're saying, if I'm not targeting an AOT platform, I don't need this precompiled thing and I could just reference protobuf normally.

    I checked out this compiled dll you generated, it contained a lot of write/read methods. Question is, what am I supposed to do with this assembly? - Say I wanted to serialize a V3, should I use "MyGeneratedSerializer".Write/Read(myVector);? - So now I don't use protobuf "Serizlizer.*" (which resolves to RuntimeTypeModel.Default.*)

    Also, what AOT platforms out there other than iOS?

    Thank you!
     
  16. halfbot

    halfbot

    Joined:
    Sep 22, 2013
    Posts:
    19
    Ok I just did a happy dance after seeing this!!! Amazing work. I want that feature so bad and I didn't even realize it until is saw that gif
     
  17. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Nope, AOT is only used for a couple platforms, like iOS and WebPlayer. If System.Reflection.Emit is available, then it's not AOT (as *Emit is runtime code generation). afaik, dynamic is a .NET feature introduced in .NET 4, whereas Unity targets 3.5ish.

    Correct.

    The assembly is generated by protobuf-net itself. The read/write methods are used internally by protobuf. If you look inside ProtoBufNetSerializer.cs you'll see these lines

    Code (csharp):
    1.  
    2. #if UNITY_IOS || UNITY_WEBPLAYER
    3. #define PROTOBUF_USE_PRECOMPILER
    4. #endif
    5.  
    followed by

    Code (csharp):
    1. static ProtoBufNetSerializer() {
    2. #if PROTOBUF_USE_PRECOMPILER
    3.     // If we are using a precompiler, then we want to locate the type model class that we
    4.     // are using for serialization. We do this via reflection and not statically so that we
    5.     // don't generate compiler errors complaining about a missing type.
    6.     Type serializer = TypeCache.FindType(ProtoBufNetSettings.PrecompiledSerializerTypeName,
    7.         ProtoBufNetSettings.PrecompiledSerializerTypeName, /*willThrow:*/false);
    8.     if (serializer == null) {
    9.         Debug.LogError(string.Format("Unable to load precompiled protobuf-net serializer " +
    10.             "(searched for type {0}); have you compiled it using " +
    11.             "\"Window/Full Inspector/Compile protobuf-net Serialization DLL\"?",
    12.             ProtoBufNetSettings.PrecompiledSerializerTypeName));
    13.     }
    14.  
    15.     else {
    16.         _serializer = (TypeModel)Activator.CreateInstance(serializer);
    17.     }
    18. #else
    19.     var serializer = TypeModelCreator.CreateModel();
    20.     serializer.Compile();
    21.     _serializer = serializer;
    22. #endif
    23. }
    24.  
    So to answer your question, you yourself don't do anything with the assembly. FI will use it automatically when creating the protobuf-net serializer.

    What I'd like to eventually migrate to with protobuf-net is to decompile the generated assembly into a C# file that Unity will normally compile, and then just use that in the protobuf-net serializer, so no need to muck around with a separate DTO assembly and the like. However, I've been having a really hard time finding a decompiler that will work within Unity's restricted mono environment (Unity actually ships .NET 4 with Unity, which is what I assume they use to run some certain C# libraries, instead of mono). Unfortunately, based on my investigations, I don't think this will be doable in the near future, as this will be a ton of work with perhaps minimal payoff if I have to write the decompiler myself.

    WebPlayer, iOS, and the consoles (PS3 etc) afaik.
     
  18. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Happy you're excited! Just send me an email if you want a preview build.
     
  19. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Thanks a lot for the very-detailed answer! Appreciate it! - For your decompilation, have you looked at Mono Cecil? (not sure if it perfectly helps) You probably know about it. Unity uses it actually in some of its assemblies in Editor/Data/Managed. Another thing: do you have to write and use this decomiler within Unity's environment? why not just write it outside of Unity and provide it as an external tool?
     
    Last edited: May 29, 2014
  20. vToMy

    vToMy

    Joined:
    Apr 19, 2013
    Posts:
    22
    inline object editing is my dream! :D
    Really looking forward to it.
     
  21. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Yea, I've played around with Mono.Cecil quite a bit (incidentally I just used the same DLL that Unity ships). I toyed around with it yesterday some more after your comment, and wrote a pretty basic decompiler (though it doesn't always generate the correct code, especially with branches).

    However, on further thought I decided to spend some time investigating running the decompiler as an external tool. I've been hesitant to do this simply because it introduces another dependency which might not work, but on further thought I figured the pain was worthwhile, at least to give the option. So today I've written a project that interfaces with NRefactory and decompiles a protobuf assembly so that it'll recompile as C# (which is a bit trickier than it seems -- lots of postprocessing steps on the generated C# file to get it to compile).

    It works out well, but the precompiled C# serializer can become hilariously long -- 12k+ lines easily.

    tl;dr: The next release will include protobuf-net support for AOT platforms that's painless -- no more DTO assemblies, etc. Just one click to generate the C# file before exporting to iOS, WebPlayer, etc.
     
  22. mcmorry

    mcmorry

    Joined:
    Dec 2, 2012
    Posts:
    580
    Wow this is very interesting. I wanted to use protobuf in the beginning, but the many steps involved stopped me to try and I moved to Json.
    Looking forward for this new big improvement of the plugin! :p
     
  23. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    With the next release, managing which serializer you want to use for your project will become even easier. While this may seem like a small feature, it means much less inconvenience for those who don't use the default (look closely and you'll see there is a new serializer :) -- I'll post more about this later).

    This also makes it so that WebPlayer, iOS, etc, build correctly on an initial FI installation.

    $importer.gif
     
  24. PrisonerOfLies

    PrisonerOfLies

    Joined:
    Dec 6, 2013
    Posts:
    105
    i just downloaded the trial version and already hit a problem. i cant derive from BaseBehaviour, then i went to look at the sample codes and realised the interface is quite different from the one shown in the tutorial video. Is the trial version made to have to use the namespace FullInspector.Samples ?
     
  25. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Can you verify that you are using the FullInspector namespace? BaseBehavior is actually FullInspector.BaseBehavior. (and BaseBehavior is just a "type synonym" for BaseBehavior<JsonNetSerializer>).

    I just imported the trial into an empty project but cannot reproduce the issue.

    Here's the code I used to test:
    Code (csharp):
    1.  
    2. using FullInspector;
    3. using System.Collections.Generic;
    4.  
    5. public class MyBehavior : BaseBehavior {
    6.     public Dictionary<int, int> Test;
    7. }
    8.  
    The tutorial video just contains some code I put together just for that -- the samples are generally a bit more directed in what they demonstrate. If you copy/paste the tutorial video's code into a new file, however, it should work fine.

    Thanks. Let me know if that resolves the issue.
     
  26. PrisonerOfLies

    PrisonerOfLies

    Joined:
    Dec 6, 2013
    Posts:
    105
    I will paste the codes later
     
  27. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    One of things that I've found useful as I've been using FI myself is to draw a property or field with a disabled UI instead of allowing the user to edit it. This is really useful, for example, when you have a behavior that generates other behaviors and stores references to them, but you still want to show the reference in the inspector. These reference shouldn't be modifiable by the user -- so don't let them!

    This feature, [InspectorDisabled], can be easily added to FI using the regular extension system. Here's the code in a gist for convenience.

    Code (csharp):
    1.  
    2. using System;
    3. using UnityEngine;
    4.  
    5. namespace FullInspector {
    6.     /// <summary>
    7.     /// Draws the regular property editor but with a disabled GUI. With the current
    8.     /// implementation this is not compatible with other attribute editors.
    9.     /// </summary>
    10.     [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    11.     public class InspectorDisabledAttribute : Attribute {
    12.     }
    13. }
    14.  
    and the editor (needs to be in an Editor folder)
    Code (csharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. namespace FullInspector.Modules.Attributes {
    6.     [CustomAttributePropertyEditor(typeof(InspectorDisabledAttribute), ReplaceOthers = true)]
    7.     public class InspectorDisabledAttributeEditor<T> : AttributePropertyEditor<T, InspectorDisabledAttribute> {
    8.         protected override T Edit(Rect region, GUIContent label, T element, InspectorDisabledAttribute attribute) {
    9.             PropertyEditorChain chain = PropertyEditor.Get(typeof(T), null);
    10.  
    11.             EditorGUI.BeginDisabledGroup(true);
    12.             element = chain.FirstEditor.Edit(region, label, element);
    13.             EditorGUI.EndDisabledGroup();
    14.  
    15.             return element;
    16.         }
    17.  
    18.         protected override float GetElementHeight(GUIContent label, T element, InspectorDisabledAttribute attribute) {
    19.             PropertyEditorChain chain = PropertyEditor.Get(typeof(T), null);
    20.             return chain.FirstEditor.GetElementHeight(label, element);
    21.         }
    22.     }
    23. }
    24.  
    And here's how it looks in Full Inspector:

    editor.PNG code.PNG
     
  28. RobbGraySBL

    RobbGraySBL

    Joined:
    Feb 4, 2014
    Posts:
    40
    It would be great if I could somehow remove the type dropdown box in an array of generic/abstract types.

    My most extreme example would be something like this:
    public object[] parameters;

    The object array is created with the proper types so editing them is great, and it works great unless you click on the type drop down box since it's object it basically freezes for a few minutes while it create a list of every single type until it errors out.

    Great plugin though.
     
  29. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Yea, I've stumbled upon that issue a few times myself.

    The array/list requirement makes this tricky to deal with, as attribute editors are applied to the entire array itself, not to the elements. However, by introducing a wrapper type and having our array defined as Wrap[] instead of object[], everything will work fine as we can apply an attribute to the element inside of Wrap.

    Here's an example, detailing exactly what I mean:

    Code (csharp):
    1.  
    2. using FullInspector;
    3. using System.Collections.Generic;
    4.  
    5. public class SkipInheritanceTest : BaseBehavior {
    6.     // If we don't have an array/list, we can apply the attribute directly
    7.     [InspectorSkipInheritance]
    8.     public object MyObj;
    9.  
    10.     // Otherwise we need a wrapper type to apply the attribute to each element of the list
    11.     public struct Wrap {
    12.         public Wrap(object obj) { MyObj = obj; }
    13.  
    14.         [InspectorSkipInheritance]
    15.         public object MyObj;
    16.     }
    17.     public Wrap[] MyArr;
    18.  
    19.     [InspectorButton]
    20.     public void SetInt() {
    21.         MyObj = 3;
    22.         MyArr = new[] { new Wrap(3) };
    23.     }
    24. }
    25.  
    Now, the only tricky part is this [InspectorSkipInheritance] attribute, but that's extremely easy to write using the normal extension system.

    The attribute implementation is available in this gist, or below.

    note: With the current implementation, if the element is null, then the regular dropdown will be shown. When a type is selected from the dropdown, then the dropdown will go away.

    InspectorSkipInheritanceAttribute.cs
    Code (csharp):
    1.  
    2. using System;
    3.  
    4. namespace FullInspector {
    5.     /// <summary>
    6.     /// Adding [InspectorSkipInheritance] will prevent the drop-down type selection editor from
    7.     /// being shown.
    8.     /// </summary>
    9.     [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    10.     public class InspectorSkipInheritanceAttribute : Attribute, IInspectorAttributeOrder {
    11.         double IInspectorAttributeOrder.Order {
    12.             get {
    13.                 return double.MinValue;
    14.             }
    15.         }
    16.     }
    17. }
    18.  
    Editor/InspectorSkipInheritanceAttributeEditor.cs
    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. namespace FullInspector.Modules.Attributes {
    5.     [CustomAttributePropertyEditor(typeof(InspectorSkipInheritanceAttribute), ReplaceOthers = true)]
    6.     public class InspectorSkipInheritanceAttributeEditor<T> : AttributePropertyEditor<T, InspectorSkipInheritanceAttribute> {
    7.  
    8.         private static IPropertyEditor GetEditor(object element) {
    9.             if (element == null) {
    10.                 return PropertyEditor.Get(typeof(T), null).FirstEditor;
    11.             }
    12.             return PropertyEditor.Get(element.GetType(), null).FirstEditor;
    13.         }
    14.  
    15.         protected override T Edit(Rect region, GUIContent label, T element, InspectorSkipInheritanceAttribute attribute) {
    16.             return GetEditor(element).Edit(region, label, element);
    17.         }
    18.  
    19.         protected override float GetElementHeight(GUIContent label, T element, InspectorSkipInheritanceAttribute attribute) {
    20.             return GetEditor(element).GetElementHeight(label, element);
    21.         }
    22.  
    23.         public override GUIContent GetFoldoutHeader(GUIContent label, object element) {
    24.             return GetEditor(element).GetFoldoutHeader(label, element);
    25.         }
    26.  
    27.         protected override T OnSceneGUI(T element, InspectorSkipInheritanceAttribute attribute) {
    28.             return GetEditor(element).OnSceneGUI(element);
    29.         }
    30.     }
    31. }
    32.  
    Thanks. Let me know if this solves your issue.
     
  30. Glader

    Glader

    Joined:
    Aug 19, 2013
    Posts:
    456
    When I try to assign an object reference for a public interface field on a script I get this warning, is this intended?



    Also, it seems I'm failing to serialize as when I've added the references to the fields via the inspector they reset on entering playmode. I'm using the non generic BaseBehaviour, I tried with protobuf and was getting some interesting errors. I'm also overriding Awake and calling the base as the first operation.

    What exactly do I need to change so that this works properly?
     
  31. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    The warning you see should be harmless, but the bug is interesting non-the-less. Constructing Component types is tricky because they are directly tied into a GameObject instance.

    I've made a modification to the object allocator to take this into account. When constructing a Component derived type, it will first try to fetch an instance from the currently selected GameObject, and if that fails, it will add a new instance. If there is no selected object, then it will construct a "fake" component that won't show a warning but will report itself as null.

    If you want the fix immediately, then you can apply the patch below, or if you'd like, I can send you the latest master, which contains the fix.

    Can you set FullInspector2\Core\FullInspectorSettings.EnableWarnings to true? If you don't get any warnings, would you mind sending me a sample so I can reproduce it on my end? I haven't had any problems like this except when there is an exception within the serializer, which will cause the property to be set to value that Unity deserialized into it (null).

    Thanks.

    Patch for the warning: In FullInspector2\Core\Utility\InspectedType.cs change CreateInstance (around line 70) to
    Code (csharp):
    1.  
    2. /// <summary>
    3. /// Creates a new instance of the type that this metadata points back to. If this type has a
    4. /// default constructor, then Activator.CreateInstance will be used to construct the type
    5. /// (or Array.CreateInstance if it an array). Otherwise, an uninitialized object created via
    6. /// FormatterServices.GetSafeUninitializedObject is used to construct the instance.
    7. /// </summary>
    8. public object CreateInstance() {
    9.     // Unity requires special construction logic for types that derive from
    10.     // ScriptableObject. The normal inspector reflection logic will create ScriptableObject
    11.     // instances if FullInspectorSettings.AutomaticReferenceInstantation has been set to
    12.     // true.
    13.     if (typeof(ScriptableObject).IsAssignableFrom(ReflectedType)) {
    14.         return ScriptableObject.CreateInstance(ReflectedType);
    15.     }
    16.  
    17.     // HACK: Constructing components is tricky, as they require a GameObject context. We
    18.     //       fetch a GameObject from the active selection for the context to add the
    19.     //       component to. If that doesn't work, then we construct an unformatted instance,
    20.     //       which will be reported to the underlying system as null.
    21.     //
    22.     // TODO: Can this support multi-object selection? Very, very dirty.
    23.     if (typeof(Component).IsAssignableFrom(ReflectedType)) {
    24.         if (Selection.activeGameObject != null) {
    25.             // Try to fetch an existing instance
    26.             Component component = Selection.activeGameObject.GetComponent(ReflectedType);
    27.             if (component != null) {
    28.                 return component;
    29.             }
    30.  
    31.             // Failed -- add a new instance
    32.             return Selection.activeGameObject.AddComponent(ReflectedType);
    33.         }
    34.  
    35.         Debug.LogWarning("No selected game object; constructing an unformatted instance (which will be null) for " + ReflectedType);
    36.         return FormatterServices.GetSafeUninitializedObject(ReflectedType);
    37.     }
    38.  
    39.     if (HasDefaultConstructor == false) {
    40.         return FormatterServices.GetSafeUninitializedObject(ReflectedType);
    41.     }
    42.  
    43.     if (_isArray) {
    44.         // we have to start with a size zero array otherwise it will have invalid data
    45.         // inside of it
    46.         return Array.CreateInstance(ElementType, 0);
    47.     }
    48.  
    49.     try {
    50.         return Activator.CreateInstance(ReflectedType, /*nonPublic:*/ true);
    51.     }
    52.     catch (MissingMethodException e) {
    53.         throw new InvalidOperationException("Unable to create instance of " + ReflectedType + "; there is no default constructor", e);
    54.     }
    55.     catch (TargetInvocationException e) {
    56.         throw new InvalidOperationException("Constructor of " + ReflectedType + " threw an exception when creating an instance", e);
    57.     }
    58.     catch (MemberAccessException e) {
    59.         throw new InvalidOperationException("Unable to access constructor of " + ReflectedType, e);
    60.     }
    61. }
    62.  
     
  32. Glader

    Glader

    Joined:
    Aug 19, 2013
    Posts:
    456
    This is what I'm not encountering with warnings enabled. Oddly I now get an exception as well. I've another problem as well but I'll only bother you with that after this one is solved as it's not nearly as important. Firstly, I must ask, am I suppose to target my interface's properties with attributes indicating what to serialize? I'm not doing that and if I'm suppose to that could be the problem. Anyway, here it the new log.

    The first 3 warnings are unrelated.




    I have not used the patch you posted yet.

    Edit: And when I try to assign the interface and enter playmode this occurs.

     
  33. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Those are really weird errors. Can you either give me a sample to repro with or a callstack on the InvalidOperationError and the two serialization errors? (so warning #1, #5, and #6).
     
  34. Glader

    Glader

    Joined:
    Aug 19, 2013
    Posts:
    456
    Here is a pastebin to the callstack for the InvalidOperationException as well as the two warnings about a serialization error. I only included one of them because they're basically the same.

    http://pastebin.com/PJgwuQdb

    This is the class implementing the ITextBox. I created it as a test after purchasing the asset about an hour ago.



    UIInput is an NGUI class that inherits from MonoBehaviour.
     
  35. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    I can't reproduce the problem. If applying the allocation fix doesn't correct the issue, would you mind trying the latest master?

    If you're willing to try the current master, let me know which serializers you're using an I'll send you an appropriate build with them already imported.

    Otherwise, can you provide a more complete sample that I can reproduce with? ie, an empty project.

    Thanks.
     
  36. Glader

    Glader

    Joined:
    Aug 19, 2013
    Posts:
    456
    I will create a new project right now and forward it to you soon.

    On a side note I encounter problems with BaseBehaviour using Protobuf-net. An exception was thrown that referenced a type in compiled dll that I use for Packet classes that uses Protobuf-net for serialization even though no field or property was attempting to expose said type.

    Anyway, let me go make a project real quick.
     
  37. Glader

    Glader

    Joined:
    Aug 19, 2013
    Posts:
    456
    Sent you a test project in a conversation.
     
  38. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Got it. The serialization errors are because Json.NET does not support custom converters with inheritance. What's happening here is that Json.NET detects that the runtime type of the object is a UnityEngine.Object type, so it sends it through the UnityObject converter, which just serializes the object as an int. When Json.NET comes to deserialize the object, it notices that the deserialized type is an interface so it tries to lookup type information -- except that none got serialized, because it sent it through the converter without including type info.

    However, I switched the serializer over to Full Serializer and it handles it correctly (woo! :)).

    The allocation issues are also fixed in the latest master. Would you like the master build?

    The protobuf-net code is likely having troubles because FI goes through all user types and if they include a protobuf-net annotation it includes them in the protobuf-net type model (the protobuf-net serializer does not support adding types to the serialization model dynamically, so it has to be done beforehand). What's the exception?

    Thanks.
     
  39. Glader

    Glader

    Joined:
    Aug 19, 2013
    Posts:
    456
    Sure I'd love the most up-to-date build from your master branch if you could. If I want to utilize what you linked, the Full Serializer, do I need to integrate that with your project or is that available in your project somewhere that I don't see it?

    Here is a pastebin for the Protobuf-net exception when trying to use that as the serializer.

    The type it's pointing to is contained in a DLL compiled from anoher solution. It's just a DLL that holds a ton of serializable types to be sent over the wire. I'm not exposing it as a field or anything on the class though so I'm not sure what the issue is. It's not even referenced in the class inheriting from BaseBehavior either.

    http://pastebin.com/Hn5r5fRq

    Edit: The type mentioned's only private member is a parameterless constructor for protobuf-net.
     
  40. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Full Serializer was developed specifically for Full Inspector, so that there was a serializer that just worked, without any extra muckery.

    re: protobuf: I'll see if I can add in some filtering methods to the protobuf-net serializer so it can ignore selected types/assemblies when creating the type model.

    I'll send you a PM with the master build.
     
  41. jrhee

    jrhee

    Joined:
    Dec 5, 2013
    Posts:
    74
    Hi Sient, great tool, thanks for sharing!

    Just wanted to see if there are any plans for:

    1. Collapsible classes/collections? I'm working with custom classes which take up lots of space in the inspector. For example, say I had the following:

    Main List (Characters)
    > Element 0 (Character)
    >> var 1 (Name)
    >>> Sub List (Stats)
    >>>> Element 0 (HP)

    With the original inspector I'm able to collapse Character and Stats; would be a huge help if we could do the same with FI.

    2. GUI performance enhancements? When I have a list of about 20 classes in a list displayed in my inspector, it becomes quite choppy when scrolling through/sorting the data.

    Thanks!
     
    Last edited: Jun 7, 2014
  42. jrhee

    jrhee

    Joined:
    Dec 5, 2013
    Posts:
    74
    Just came across [SingleItemListEditor]. Ignore above post, this resolves both points. Now to get it working for dictionaries ;)

    Great work!
     
  43. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    FI will automatically display a drop-down once a property has grown past a certain height. At the moment, it's 80 pixels. If you want to adjust this, do a search for "MinimumFoldoutHeight" and adjust it to your favorite value. It's been moved to FullInspectorSettings for the next release, but at the moment it's probably in FullInspector2/Core/Editor/PropertyEditors/ReflectedPropertyEditor.cs


    Yep, the next release reduces memory allocation by a factor of five.
     
  44. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Awesome! You might need a wrapper type. For example,

    Code (csharp):
    1.  
    2. public class DictionaryDemo : BaseBehavior {
    3.     public Dictionary<string, Proxy<ActualValue>> MyDict;
    4.  
    5.     public struct Proxy<T> {
    6.         [SingleItemListEditor]
    7.         public List<T> Value;
    8.     }
    9.  
    10.     public class ActualValue {
    11.         /* data inside of the dictionary key */
    12.         public int A, b, c, d, e, f, g;
    13.     }
    14. }
    15.  
    I would like to eventually get a more powerful attribute drawer system so that you can apply it directly to a type, but it works well-enough for now.

    Thanks.
     
  45. jrhee

    jrhee

    Joined:
    Dec 5, 2013
    Posts:
    74
    The issue I was having wasn't with the MinFoldoutHeight, it was that Rotorz seems to break the foldout code in ReflectedPropertyEditor.EditProperty(). Here's what my inspector looked like:

    1.jpg

    The draggable handles in ReorderableListControl.DrawListItem() block the foldout. When I remove the ItemContentPosition offset (line 596-7), I can see the foldout handles for my child lists/classes, but still can't collapse list items:

    2.jpg

    Awesome :)

    Thanks for the tip! Until I have more time I'll probably just hack pieces from IDictionaryPropertyEditor into a separate SingleItemEditorAttributePropertyEditor implementation as a short term fix since this may become moot with collapsing working on lists + performance enhancements in 2.4.
     
    Last edited: Jun 8, 2014
  46. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    I've finished rewriting the metadata engine (phew, it took a number of attempts -- very tricky) and it works wonderfully. It allows for metadata to be stored based on actual editing tree, regardless of the current object instance being edited. This means that foldouts will now work for structs, but the new metadata engine is even more powerful than that.

    Here's an example of what it enables:

    new metadata engine.gif

    The new metadata engine made adding animation a snap :).

    As always, if you want a preview release just send me an email or PM.

    re: jrhee: Hopefully the new foldout system solves your issue, as shown in the gif.


    Code for the gif above:
    Code (csharp):
    1.  
    2. using FullInspector;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public interface IDemoInterface { }
    7.  
    8. public class StructObject1 : IDemoInterface {
    9.     public int IntVal;
    10.     public Transform Transform;
    11.     public float Float1, Float2, Float3;
    12. }
    13.  
    14. public class StructObject2 : IDemoInterface {
    15.     public float FloatVal;
    16. }
    17.  
    18. public class StructObjectHuge : IDemoInterface {
    19.     public int Int1, Int2, Int3, Int4, Int5, Int6, Int7, Int8;
    20. }
    21.  
    22. public class Demo : BaseBehavior {
    23.     public Dictionary<string, IDemoInterface> InterfaceDictionary;
    24. }
    25.  
     
    rcalt2vt and jrhee like this.
  47. jrhee

    jrhee

    Joined:
    Dec 5, 2013
    Posts:
    74
    Looks great, thanks!
     
  48. rcalt2vt

    rcalt2vt

    Joined:
    Jun 6, 2009
    Posts:
    36
    Jaw, meet floor...
    Seriously, awesome to see. Looking forward to the next version!
     
  49. Suike

    Suike

    Joined:
    Jul 25, 2013
    Posts:
    16
    Hi,

    I would like to know if this asset can serialize and show in the inspector serializable C# classes that dont derive from UnityEngine.Object as reference instead of value, and if it can serialize protected fields.

    ex:
    [Serializable]
    public class Node
    {
    [SeializeField] protected NodeConnection[] connections
    }

    [Serializable]
    public class NodeConnection
    {
    [SeializeField] protected Node a; // needs to contains reference, not a copy
    [SeializeField] protected Node b; // needs to contains reference, not a copy
    }

    And does it onyl support MonoBehaviours, or ScriptableObjects too?
     
    Last edited: Jun 27, 2014
  50. sient

    sient

    Joined:
    Aug 9, 2013
    Posts:
    602
    Yes, ScriptableObjects are fully supported (just derive from BaseScriptableObject). Protected/private fields are also supported.

    Serialization by reference depends on a number of different factors.

    First, each member of a BaseBehavior is serialized independently -- so instances may be shared among members, but across members they will not be shared. This only applies to members on Base* types.

    Second, if the serializer serializes everything by value (ex, Json.NET will serialize by value if you do not add IsReference=true to the JsonObject annotation), then you won't get references.

    The first limitation can be overcome by deriving from BaseScriptableObject -- then the object will always be serialized by reference.