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

[Open Source] VFW (135): Drawers. Save System and full exposure

Discussion in 'Assets and Asset Store' started by vexe, Sep 2, 2014.

  1. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @BTStone are you saying that you expect the checkbox to be checked when you set `swapped` to true from your code? That's totally not gonna happen. 'swapped' is a boolean parameter passed by value, if you change it in DoSwap it's not going to affect the caller in anyway. Having visible parameters from the inspector is just a quick way to give those parameters values from the editor and use them to invoke the method. Calling DoSwap(true or false) has no affect on the checkbox in the editor, you're just calling your method with true or false.
     
    BTStone likes this.
  2. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,422
    Ah! I see, thanks for the clarification! :)
     
  3. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    Wow - if I understand you correctly this is a huge change of philosophy. Are you really saying to avoid VFW's serialization features, and don't do stuff like have interface types serialized and exposed in the editor? Just live with what Unity provides except maybe some extra custom dictionary serialization code?

    What does this mean for the future of VFW as a project in terms of its development? While it's a return to VFW's beginnings, it seems like a big change based on where the project is now.

    Hope you do this, would definitely watch it.
     
  4. AlexKertis

    AlexKertis

    Joined:
    Aug 28, 2015
    Posts:
    6
    Hi, Vexe!

    I've tried VFW on the latest version of Unity5 and it worked totally perfect. Our designers were amazed by inspector improvements and I really like how VFW handles serialization of polymorphic data types (was looking for an asset to solve this specific issue).

    The problem is we are currently using Unity4 due to some very specific problems regarding oculus rift usage on Unity5. So I was trying to make VFW work on Unity4: deleted some stuff related to Animator, done several type conversions like double to float, used non-generic version of LoadAssetAtPath(), changed GUIStyles.HelpBox from EditorStyles.helpBox to EditorStyles.label. After these changes all compile errors were gone. But now when I try to select a game object on a scene with BetterBehaviour component attached, I get a NullRef exception thrown by the type initializer for Vexe.Editor.GUIStyles (I can provide more info on this - just need to know what kind of information matters).

    So my question is: is it possible to make VFW work on Unity4 (even without some functionality like Animator usage it still provides great tools for our designers)? And more specific question is how to deal with exception described above?

    Thanks.
     
    baguwka likes this.
  5. NeonTanto

    NeonTanto

    Joined:
    Jun 19, 2013
    Posts:
    156
    Hi guys! I will try some intresting stuff. I send it to Vexe, maybe he is add it to next version, may be no, but I think thsi will be usefull for VFW users.
    So, what I do? First of all I add this into Members.cs
    Code (CSharp):
    1. public Action onSceneGuiAction = () => {};
    then I add this into BaseEditor.cs
    Code (CSharp):
    1. protected void OnSceneGUI() {
    2.     gui.onSceneGuiAction();
    3. }
    Than I create custom annotation and drawer. In drawer I can subscribe to this delegate
    Code (CSharp):
    1. gui.onSceneGuiAction += MyDrawerAction;
    and have some profit with using Handles or other OnSceneGUI stuff without custom editors, only with attribute.

    I use it for integration VFW with custom handles. This is result achived with adding my attribute to AnimationCurve field.
     
    Rodolfo-Rubens and Seneral like this.
  6. Dr-Soops

    Dr-Soops

    Joined:
    Oct 4, 2013
    Posts:
    1
    I don't have much experience with unity but i stumbled upon this when i ran into unity not supporting interfaces in the ui. So i tried to import the package and i keep getting errors with the projects having the wrong .net compilers, ambiguous references to Tuple and ambiguous references for extension methods. Am i doing something wrong or does everyone stumble through these till it works?
     
  7. NeonTanto

    NeonTanto

    Joined:
    Jun 19, 2013
    Posts:
    156
    Hi guys! I glad to announce some intresting stuff for VFW =)
    pict02.png pict01.PNG
    Now we can use GUILayout inside RabbitGUI! After some refactoring work I create pull request to Vexe.
     
  8. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hey guys, long time no post. But I moved to a different place, trying to settle down, got a better rig, getting to know the city, making connections and finding a job. I'm still alive, and so is VFW. Let me know if you're close to London ON

    @Ghopper21 check out the gstring thread, read the long post I wrote there. I give an example of an item system designed in a data oriented/driven manner. Single Item class, each item has its unique data associated with it, no virtuals, nothing fancy, nothing complicated, no BS.

    @AlexKertis yeah sure it should work for Unity 4 but I think you need at least 4.6 to support serialization callbacks. Just let me know exactly what the errors are, you should be able to fix them all.

    @mf_andreich has been doing some great stuff, thanks!
     
    Last edited: Oct 23, 2015
  9. majeric

    majeric

    Joined:
    Aug 17, 2010
    Posts:
    89
    I'm tryingVFW for the first time and right out of the box I'm getting the following error with the first class I've tried adapting to it:

    Code (CSharp):
    1. ArgumentOutOfRangeException: Cannot be negative.
    2. Parameter name: length
    3. System.String.Substring (Int32 startIndex, Int32 length) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/String.cs:348)
    4. Vexe.Runtime.Extensions.TypeExtensions.<get_getNiceName>m__5D (System.Type type) (at Assets/Plugins/Vexe/Runtime/Library/Extensions/TypeExtensions.cs:265)
    5. Vexe.Runtime.Extensions.DelegateExtensions+<Memoize>c__AnonStoreyD`2[System.Type,System.String].<>m__2A (System.Type n) (at Assets/Plugins/Vexe/Runtime/Library/Extensions/DelegateExtensions.cs:38)
    6. Vexe.Runtime.Extensions.TypeExtensions.GetNiceName (System.Type type) (at Assets/Plugins/Vexe/Runtime/Library/Extensions/TypeExtensions.cs:289)
    7. Vexe.Runtime.Types.RuntimeMember..ctor (System.Reflection.MemberInfo memberInfo, System.Type memberType, System.Object memberTarget) (at Assets/Plugins/Vexe/Runtime/Types/Others/RuntimeMember.cs:91)
    8. Vexe.Runtime.Types.RuntimeMember.TryWrapField (System.Reflection.FieldInfo field, System.Object target, Vexe.Runtime.Types.RuntimeMember& result) (at Assets/Plugins/Vexe/Runtime/Types/Others/RuntimeMember.cs:107)
    9. Vexe.Runtime.Types.RuntimeMember.WrapMember (System.Reflection.MemberInfo member, System.Object target) (at Assets/Plugins/Vexe/Runtime/Types/Others/RuntimeMember.cs:179)
    10. Vexe.Runtime.Types.RuntimeMember.WrapMembers (IEnumerable`1 members, System.Object target) (at Assets/Plugins/Vexe/Runtime/Types/Others/RuntimeMember.cs:164)
    11. Vexe.Runtime.Serialization.ISerializationLogic.GetSerializableMembers (System.Type type, System.Object target) (at Assets/Plugins/Vexe/Runtime/Serialization/SerializationLogic.cs:41)
    12. Vexe.Runtime.Serialization.ISerializationLogic.<ISerializationLogic>m__83 (System.Type type) (at Assets/Plugins/Vexe/Runtime/Serialization/SerializationLogic.cs:31)
    13. Vexe.Runtime.Extensions.DelegateExtensions+<Memoize>c__AnonStoreyD`2[System.Type,Vexe.Runtime.Types.RuntimeMember[]].<>m__2A (System.Type n) (at Assets/Plugins/Vexe/Runtime/Library/Extensions/DelegateExtensions.cs:38)
    14. Vexe.Runtime.Helpers.RuntimeHelper.IsModified (UnityEngine.Object target, Vexe.Runtime.Serialization.SerializerBackend serializer, Vexe.Runtime.Serialization.SerializationData data) (at Assets/Plugins/Vexe/Runtime/Library/Helpers/RuntimeHelper.cs:80)
    15. Vexe.Runtime.Types.BetterBehaviour.OnBeforeSerialize () (at Assets/Plugins/Vexe/Runtime/Types/Core/BetterBehaviour.cs:40)
    16. UnityEditor.HostView:OnInspectorUpdate()
     
  10. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @majeric Can you show the code you used to trigger the error?
     
  11. majeric

    majeric

    Joined:
    Aug 17, 2010
    Posts:
    89
    @vexe I'd private message it to you but I'd rather not a public forum. I'm happy to post a solution once we figure it out though.
     
  12. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    It seems you're inheriting BetterBehaviour, for starters try BaseBehaviour instead, see if the errors go away.
     
  13. majeric

    majeric

    Joined:
    Aug 17, 2010
    Posts:
    89
    Unfortunately not:

    Code (CSharp):
    1. ype) (at Assets/Plugins/Vexe/Runtime/Library/Extensions/TypeExtensions.cs:289)
    2. Vexe.Runtime.Extensions.TypeExtensions.<get_getNiceName>m__5D (System.Type type) (at Assets/Plugins/Vexe/Runtime/Library/Extensions/TypeExtensions.cs:275)
    3. Vexe.Runtime.Extensions.DelegateExtensions+<Memoize>c__AnonStoreyD`2[System.Type,System.String].<>m__2A (System.Type n) (at Assets/Plugins/Vexe/Runtime/Library/Extensions/DelegateExtensions.cs:38)
    4. Vexe.Runtime.Extensions.TypeExtensions.GetNiceName (System.Type type) (at Assets/Plugins/Vexe/Runtime/Library/Extensions/TypeExtensions.cs:289)
    5. Vexe.Editor.Types.EditorMember..ctor (System.Reflection.MemberInfo memberInfo, System.Type memberType, System.String memberName, System.Object rawTarget, UnityEngine.Object unityTarget, Int32 targetId, System.Attribute[] attributes) (at Assets/Plugins/Editor/Vexe/Types/EditorMember.cs:111)
    6. Vexe.Editor.Types.EditorMember.WrapMember (System.Reflection.MemberInfo memberInfo, System.Object rawTarget, UnityEngine.Object unityTarget, Int32 id, System.Attribute[] attributes) (at Assets/Plugins/Editor/Vexe/Types/EditorMember.cs:329)
    7. Vexe.Editor.Types.EditorMember.WrapMember (System.Reflection.MemberInfo memberInfo, System.Object rawTarget, UnityEngine.Object unityTarget, Int32 id) (at Assets/Plugins/Editor/Vexe/Types/EditorMember.cs:318)
    8. Vexe.Editor.GUIs.BaseGUI+Cache.<get_GetMember>m__90 (Tuple`2 x) (at Assets/Plugins/Editor/Vexe/GUIs/BaseGUI/Controls/Members.cs:188)
    9. Vexe.Runtime.Extensions.DelegateExtensions+<Memoize>c__AnonStoreyD`2[Vexe.Runtime.Types.Tuple`2[System.Reflection.MemberInfo,System.Int32],Vexe.Editor.Types.EditorMember].<>m__2A (Tuple`2 n) (at Assets/Plugins/Vexe/Runtime/Library/Extensions/DelegateExtensions.cs:38)
    10. Vexe.Editor.GUIs.BaseGUI.Member (System.Reflection.MemberInfo member, System.Object rawTarget, UnityEngine.Object unityTarget, Int32 id, Boolean ignoreComposition, Vexe.Editor.Types.EditorMember& wrappedMember) (at Assets/Plugins/Editor/Vexe/GUIs/BaseGUI/Controls/Members.cs:39)
    11. Vexe.Editor.GUIs.BaseGUI.Member (System.Reflection.MemberInfo info, System.Object rawTarget, UnityEngine.Object unityTarget, Int32 id, Boolean ignoreComposition) (at Assets/Plugins/Editor/Vexe/GUIs/BaseGUI/Controls/Members.cs:23)
    12. Vexe.Editor.Internal.MembersCategory.Draw (UnityEngine.Object target) (at Assets/Plugins/Editor/Vexe/Editors/Internal/MembersCategory.cs:113)
    13. Vexe.Editor.Editors.BaseEditor.OnGUI () (at Assets/Plugins/Editor/Vexe/Editors/BaseEditor.cs:439)
    14. Vexe.Editor.GUIs.RabbitGUI.OnGUI (System.Action guiCode, Vector4 padding, Int32 targetId) (at Assets/Plugins/Editor/Vexe/GUIs/RabbitGUI/RabbitGUI.cs:189)
    15. Vexe.Editor.Editors.BaseEditor.OnInspectorGUI () (at Assets/Plugins/Editor/Vexe/Editors/BaseEditor.cs:181)
    16. UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor editor, Int32 editorIndex, Boolean forceDirty, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect, Boolean eyeDropperDirty) (at /Users/builduser/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1211)
    17. UnityEditor.DockArea:OnGUI()
     
  14. majeric

    majeric

    Joined:
    Aug 17, 2010
    Posts:
    89
    I've also noticed that the "Add Component" button isn't aligned properly. It sits over top of the Inspector for the behaviour.
     
  15. majeric

    majeric

    Joined:
    Aug 17, 2010
    Posts:
    89
    The essential problem is this. Stack doesn't seem to like the editor very much. I tried displaying it but I get a "Object doesn't have any visible members" warning. I'm not quite sure how to address that.

    I suspect my problem in the above error is compounded by my weird use of generics. to manage dictionary serialization.



    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using Vexe.Runtime.Types;
    6.  
    7. [Serializable]
    8. public class StackExampleBehaviour : BetterBehaviour {
    9.  
    10.     [Serializable]
    11.     public enum MyEnum {
    12.         No,
    13.         Yes,
    14.         Maybe
    15.     }
    16.    
    17.     [Serializable]
    18.     public struct MyStruct
    19.     {
    20.         public MyEnum value;
    21.     }
    22.  
    23.     [SerializeField]private Stack<MyStruct> structStack = new Stack<MyStruct>();
    24.  
    25.     public MyStruct[] list;
    26.  
    27.     public void Start()
    28.     {
    29.         structStack.Push(new MyStruct() {value = MyEnum.Maybe});
    30.         structStack.Push(new MyStruct() {value = MyEnum.Yes});
    31.         structStack.Push(new MyStruct() {value = MyEnum.No});
    32.         structStack.Push(new MyStruct() {value = MyEnum.No});
    33.        
    34.         list = structStack.ToArray();
    35.     }
    36.  
    37.    
    38.  
    39. }
     
  16. majeric

    majeric

    Joined:
    Aug 17, 2010
    Posts:
    89
    How do you do a custom inspector that adds a button to the inspector?
    It can't be just a show call on a method because it needs to make a UnityEditor calls to AssetDatabase.


    I tried the following but unlike the Unity CustomInspector. There's no base function to call to generate the default drawer for my object.


    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEditor;
    4. using Vexe.Runtime.Types;
    5. using Vexe.Editor;
    6. using Vexe.Editor.Drawers;
    7. using Vexe.Editor.Types;
    8.  
    9. [InitializeOnLoad]
    10. public static class CustomMapper
    11. {
    12.     static CustomMapper()
    13.     {
    14.         MemberDrawersHandler.Mapper.Insert<ScenerioManager, ScenerioBuilder>();
    15.     }
    16. }
    17.  
    18.  
    19. public class ScenerioBuilder : ObjectDrawer<ScenerioManager>
    20. {
    21.     public override void OnGUI()
    22.     {
    23.        
    24.         //ScenerioManager myScript = (ScenerioManager)target;
    25.         if(GUILayout.Button("Generate Audio"))
    26.         {
    27.             GenerateAudio(memberValue);
    28.         }
    29.     }
     
  17. majeric

    majeric

    Joined:
    Aug 17, 2010
    Posts:
    89
    Sadly, it's not obvious to me how one overrides a default Mono/Better/BaseBehaivour Inspector editor to add GUI but otherwise display the default Inspector.

    what is the VFW version of this:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEditor;
    4.  
    5. [CustomEditor(typeof(SomeScript))]
    6. public class SomeScriptEditor : Editor
    7. {
    8.     public override void OnInspectorGUI()
    9.     {
    10.         DrawDefaultInspector();
    11.  
    12.         EditorGUILayout.HelpBox("This is a help box", MessageType.Info);
    13.     }
    14. }
     
  18. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @majeric I did not get notified of your posts for some reason.

    The stack shows "no members visible" because there's no drawer associated with it so it falls back to the default recursive drawer which tries to reflect the object and finds fields it can expose. If you want to expose stacks you need to write an object drawer similar to SequenceDrawer

    You're trying to do things the Unity way, Vfw has its own GUI layouting system which is not compatible with Unity's layout. If you read any of the drawers or any of the example scripts you would have known this by now. Instead of EditorGUILayout.Helpbox you use gui.Helpbox, instead of GUILayout.Button, gui.Button, etc.

    If you want to derive from Vfw base editor you could just inherit BaseBehaviourEditor if memory serves well and override the OnGUI method from which you can call base.OnGUI if you want the default stuff drawn. Just take a peek at BaseEditor.cs

    To add a custom button, I normally use [Show] on a method - You say you need to access AssetDatabase, what's preventing you from doing it in the method? Just make sure to wrap up your editor-only stuff with #if UNITY_EDITOR. Or you could create a custom inspector, inherit BaseBehaviourEditor, override OnGUI, call base.OnGUI and finally gui.Button("My Button").

    Please read the example scripts I rely on them to document and demo most of Vfw stuff.
     
  19. majeric

    majeric

    Joined:
    Aug 17, 2010
    Posts:
    89
    I ended up using a RecursiveDrawer within my custom drawer. I was essentially looking for the "Identity Drawer", a custom draw that returned the default presentation. I didn't need Unity's solution.
     
  20. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Glad to hear your figured it out! Yes RecursiveDrawer is the default fallback drawer - it is the drawer that draws classes, structs, interfaces and anything that doesn't have a custom drawer registered.
     
  21. KhaledM

    KhaledM

    Joined:
    Sep 29, 2013
    Posts:
    41
    Hi,
    Is it safe to delete BetterEditorPrefs.asset
    I want to add it to the ignore list in source control when merging two branches, is that ok?
     
  22. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hey there,
    that asset file stores the data for your editors and drawers: foldout values etc. You can ignore it that's fine but if you try to delete it VFW will create a new one (or at least it should). Give it a try and see how it goes.
     
    KhaledM likes this.
  23. moses85

    moses85

    Joined:
    Nov 21, 2015
    Posts:
    2
    Hi! I started using your framework a month ago and it is looking good, I had some worries if it's going to break something but it didn't. But I have read one of your last post about how it's best to avoid deriving from BetterBehaviour and using frameworks serialization only from fast save?

    Can you elaborate on what should be the best practice how to use this framework. When to derive from what?
    Thanks man!
     
  24. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hey @moses85!

    Sure, rule of thumb is pretty straight-forward: just keep it simple. For your behaviour classes inherit from BaseBehaviour, for your scriptable objects inherit from BaseScriptableObject.

    This will give you all the editor powers of Vfw without the extra overhead of custom serialization, instead, you'll be getting the default Unity serialization which is stupid and limiting to what you can do yes, but like I mentioned in other posts it's better to just live with it and work around it (at least unless we get a better system from Unity). An example of that would dictionaries, Unity doesn't know how to serialize nor draw them, which is why I went with the SerializableDictionary.cs hack, you still have to subclass it to create your own dictionaries i.e. [Serializable] class Lookup : SerializableDictionary<string, int> {} but hey it's better than nothing.

    Now if you go with BetterBehaviour there's less hacking but more headaches in the long run. You can directly use Dictionary<T, K> and have generic and polymorphic fields etc. But serialization is either very slow or unreliable depending on what serializer you go with. My experience with it: it's just not worth the headache. It's just one of those things that you first think it's a good idea but doesn't turn out to be as good as you thought.

    After years of writing tools for Unity and using my framework I would say:
    1. Use Vfw to get advanced inspector views and better inspectability and debugging of your objects data
    2. Use Fast Save to help you implement your runtime/in-game saving/loading solutions.
    3. Don't use any custom serialization system unless it's proven to be very fast and reliable, until we have such a system stick to Unity's
    4. Do *not* trust the Unity's scene file format to safely persist and keep your data. I've lost data countless times where I leave my project in a working state, take a detour writing some tool, and then when I get back to the project it's a total mess, errors everywhere, nobody knows why or what's going on. You're pretty much screwed and you have to do it all again... Instead of Unity's fixing those problems they're doing fancy lighting stuff. sigh
    .Instead of all that nonsense (this works for me, might not work for you), here's what I do:
    1. I do all my setup and initialization from code (Awake etc), I don't have 'any' serializable fields, I have a giant database that stores information about levels, items, whathaveyou. Database gets initialized when game starts. The DB is a scriptable object (.asset file)
    2. I store all my database information and critical data (data that would be a pain to re-setup if lost) in my own custom files and formats, if I lose any scene file or whatever I still have my custom files, all it takes is a button click and everything is connected back. A data pattern that could help you with this is the property container stuff I mentioned in my gstring thread, which brings me to another topic:
    3. pack your data in containers (arrays, lists, dictionaries etc) instead of using plain fields if you can help it. This makes it *super* easy to save/load your stuff, and you could dynamically remove/add data fields without having to worry about versioning or whatever, this is not always possible but nice if you can do it.
    4. And finally, I don't have more than one level of inheritance (usually just inherit from a base script and that's it), rarely any virtuals or any oOp nonsense, I keep it as simple and stupid as possible.
    Hope that helps!
     
    Last edited: Nov 21, 2015
  25. moses85

    moses85

    Joined:
    Nov 21, 2015
    Posts:
    2
    Ok! What about BetterPrefs? Is there any advantage of using that instead of PlayerPrefs?
    And if I got it right, we should avoid betterbehaviour because of serialization but we should use FastSave instead?
     
  26. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @moses85: BetterPrefs I mostly used for persisting editor data (foldouts etc), it could be useful in runtime game code as well, never tried it though. Benefits of using it instead of PlayerPrefs? well it's faster for one cause it's just dictionaries, not accessing registry which also means it's safer, but you also have to keep in mind that you have to save those dictionaries somehow, to disk or even to a string and then store in PlayerPrefs :D

    Yes always start with BaseBehaviour, and use FastSave to build your custom in-game saving solutions.
     
  27. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    224
    Hi!

    I've just upgraded my project to Unity5.3 and it seems that InspectorTitlebar now receives an additional 4th parameter expandable to tell whether the property should have a foldout.

    As a hotfix I added true as 4th argument of EditorGUI.InspectorTitlebar on line 828 (vfw134.unitypackage) / 830 (current master version):

    Code (CSharp):
    1.         public override bool InspectorTitlebar(bool foldout, UnityObject target)
    2.         {
    3.             var data = new ControlData(GUIContent.none, GUIStyles.None, null, ControlType.Foldout);
    4.  
    5.             Rect position;
    6.             if (CanDrawControl(out position, data))
    7.             {
    8.                 return EditorGUI.InspectorTitlebar(position, foldout, target, true);
    9.             }
    10.  
    11.             return foldout;
    12.         }

    However, you probably want to change RabbitGUI.InspectorTitlebar altogether by adding some optional boolean parameter to its signature and pass it to Unity's own InspectorTitlebar.

    I don't know that much about editor inspector titlebar and I am not using it myself, so I won't send a pull request. Nevertheless, the fix should be quite simple.
     
  28. cjmanca

    cjmanca

    Joined:
    Dec 6, 2013
    Posts:
    10
    Is there any way to get it to use existing PropertyDrawer scripts to display properties which have already been coded? I'd hate to have to go through and re-code all existing PropertyDrawer scripts to work with this framework.

    Or at the very least, has anyone got a version of the UniRx InspectorDisplayDrawer that works with this framework?

    EDIT: I managed to get all the UniRx ReactiveProperties/ReactiveCollection to display in the framework, but I'd still be interested to know about other custom PropertyDrawers.
     
    Last edited: Dec 15, 2015
  29. pointcache

    pointcache

    Joined:
    Sep 22, 2012
    Posts:
    577
    Hello i was not able to locate the event system you show in the video, its just not present in repository or the package file.
     
  30. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    224
    Hi again!

    Other changes in Unity5.3: Application.LoadLevel in deprecated, use

    Code (CSharp):
    1. using UnityEngine.SceneManagement;
    2.  
    3. SceneManager.LoadScene("MyScene");
    instead.

    I don't know if it's worth making an issue out of this since it will simply trigger a warning in the console. I'll try to make a pull request myself if you don't fix it in a few days.

    The bug with InspectorTitlebar above prevents compilation, so for this one I guess I'll file an issue.
     
  31. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @huulong could you make a PR for these two changes?
     
  32. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    224
    I've just opened an issue for the 1st one, I'm waiting a little for some comments on the way to fix it.
    For the 2nd one I can send the pull request in a few minutes I think.

    UPDATE: just sent PR for the 2nd issue. I realized it was just in an example script, (SceneTransition.cs), but anyway that's done.
     
    Last edited: Dec 24, 2015
  33. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    224
    Ugh, just realized when building that there was also loading stuff in Vexe\Runtime\Library\Helpers\RuntimeHelper.cs in GetCurrentSceneName()... I'll try to update that too tomorrow. Since theare multiple scenes now, I guess I'll just make it return the name of the active scene.

    Sorry for saying that on the forum again, I'll make a proper PR later.
     
  34. kobyle

    kobyle

    Joined:
    Feb 23, 2015
    Posts:
    92
    Heya, how did you solve it? can you share some code?

    Thanks
     
  35. NavyFish

    NavyFish

    Joined:
    Aug 16, 2013
    Posts:
    28
    Hello again! Glad to hear you've relocated and gotten settled. Moving is always a bit stressful, at least for me.

    I'm trying to build a custom editor for one of my classes, and when it came time to extend ObjectDrawer<T>, my project could not locate its namespace (Vexe.Editor.Drawers). I'm using VS2015 with the VS Tools For Unity 2.1. I imported the vfw134.unitypackage into my project and had no error messages at that time. Any idea why that namespace wouldn't be imported, and, how might I do so manually? (The multiple VS projects that Unity creates always confused the hell out of me...)

    Thanks for the help! :) What a great framework..
     
  36. NavyFish

    NavyFish

    Joined:
    Aug 16, 2013
    Posts:
    28
    Ah, figured it out. I had created the custom drawer's script in C#, and hadn't located it in an "Editor" folder. Unity automatically moved the file *out* of the C# Editor project, and thus the script lost access to the Vexe.Editor assemblies. It took me awhile to notice that.

    Sigh.
     
  37. KhaledM

    KhaledM

    Joined:
    Sep 29, 2013
    Posts:
    41
    Hi @vexe
    Is there a way to make BetterBehaviour work with UniMerge, it make the compare correctly but cannot apply modifications even on scalar variables (bool, float,...etc)

    Thanks
     
  38. ScriptGeek

    ScriptGeek

    Joined:
    Mar 4, 2011
    Posts:
    45
    Is there a way to use RabbitGUI with a non-editor custom object, which does not derive from MonoBehaviour or Editor? Basically I want to know if I can use VFW's RabbitGUI as a GUI Framework for an editor tool that does not derive from Unity's Editor class. This tool uses legacy IMGUI code to lay down GUI elements from both GUILayout and EditorGUILayout classes when running Unity edit mode, but GUILayout and EditorGUILayout is difficult to develop non trivial GUIs without weird bugs.
     
  39. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @KhaledM: First, try to use BaseBehaviour instead of BetterBehaviour if you can (see above). Never used UniMerge before so I can't give an accurate answer. But, assuming you're having an issue where you have two base scripts you'd like to inherit from (BaseBehaviour and UniMerge) but obviously you can't (no multi-inheritance in C#). Similar to the guy that was asking about UNetBehaviour or whatever Unity's network script is, you basically have to duplicate code:

    Code (csharp):
    1.  
    2. public class MyOwnBehavoiur : UniMergeOrSomething
    3. {
    4.         // Copy paste the code in BaseBehaviour
    5. }
    6.  
    Then you write a custom editor for MyOwnBehaviour:

    Code (csharp):
    1.  
    2. [CustomEditor(typeof(MyOwnBehaviour))]
    3. public class MyOwnEditor : BaseEditor
    4. {
    5.      // empty
    6. }
    7.  
    Now, MyOwnBehaviour combines both the editor power you get from BaseBehaviour and has your UniMerge stuff.
    Let me know if that didn't help.

    @ScriptGeek: Sure. There's an example script (EditorWindowExample.cs IIRC) that uses RabbitGUI inside an EditorWindow. There's no way to not use Unity's GUI Layout system unless you write your own inspector from scratch, then you could implement your own layout system around GUI/EditorGUI classes. RabbitGUI still uses Unity GUI Layout but _only_ to reserve editor space to draw in, otherwise things would overlap and wouldn't look good, bad for the soul. RabbitGUI also avoids the issues you have with [Editor]GUILayout where if you exit out inside a BeginHorizontal/Vertical before calling its corresponding End function, or when your code flow changes between repaint/layout (more GUI calls in layout or repaint than the other), etc you don't have to worry about any of that.
     
  40. KhaledM

    KhaledM

    Joined:
    Sep 29, 2013
    Posts:
    41
    @vexe Thank you for your response, sorry I didn't explain what UniMerge do, it is a tool to compare two objects or scenes and can apply modification from one to another for example I have two GameObjects that has MonoBehavior component
    Code (CSharp):
    1. public class Weapon : MonoBehavior
    2. {
    3.     public float Power;
    4. }
    UniMerge will help me to sync Power value between two objects, now, it works perfectly with MonoBehaviors but in BetterBehaviors it cannot apply modification because of the backend serialization
    Please have a look at UniMerge here:
    https://www.assetstore.unity3d.com/en/#!/content/9733
    Screenshot of comparing two objects


    P.S: I Prefer BetterBehavior because I heavily depending on them before there was a BaseBehavior and I use Static, Delegates, Dictionaries...) I afraid if I switch to BaseBehavior now something could break in my project and I can not take the risk for now
     
  41. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    I feel guilty. BetterBehaviour was a mistake. If you're using stuff only BetterBehaviour can serialize, they probably won't work well with UniMerge my guess because they're relying on the fact that you're using default serialization.

    I said BetterBehaviour was a mistake not because it is bad, but it encouraged bad habits. It embraces the actual bad guy which Unity's .scene file by encouraging you to input complex stuff in the inspector and promises you that they will persist and stay there for you (it does its best at doing so). But the problem is Unity's .scene files can never be trusted in my experience to store and persist your data. It could F* you in many ways. It could easily get corrupted, things could change from one Unity update to another, data layout of your structures change from one game version to the other, etc If you're storing your data in the .scene file and it gets corrupted, you'd have to reconnect and reassign everything from the editor again...

    I would like to suggest a better workflow, a better approach to storing your data. You don't even have to think of serialization. Let's see how the 'recommended' approach of working in Unity looks like (Apologies for my drawing skills):



    I hope you can see the problem. All your data is going to this .scene file which you have _zero_ control over. All you can do is tell Unity that you want a piece of data to go into that file (by marking it with SerializeField etc) but you don't have control over the actual serialization process, don't know exactly how many failure points you have in this pipeline, it's hard to reason about the cost of changing your data layout without it affecting the rest of your stuff, if something bad happens to the .scene file you lose your data etc. It's a very poor engineering decision IMHO.

    After days and months of hair pulling, frustration and tons of experimentation with other alternatives, I came to this solution; which worked extremely well for my personal game.
    There's many good side-effects to this approach as you will see. Here's how it looks:



    So, we basically have 'data', a 'provider' and 'customers':
    - Data could be 'user data' (will talk about it in a moment), prefabs, sounds, etc.
    - The provider just loads the data
    - The editor just reads the loaded data and displays it to the user to _read_
    - The game also reads the data to do its thing

    The main things I'd like to focus on:
    - The editor is _not_ used to create data, it acts as a _window_ for the user to look at the data
    - It's a data-driven approach. User creates their own data in their preferred tool or format
    - The only way the user could get F*ed, is if he F*s himself (Unity won't)

    Let's get more concrete. Let's say we're making a Resident Evil-like game.
    So we have items: Weapons, Ammo, Health and KeyItems. Health items are consumed and maybe combined with each other to get stronger health items, KeyItems are used (keys etc) and also combined, Weapons are equipped, Ammo is combined with weapons.

    The naive, fresh college CS graduate/developer, in-experienced way (which is what I first did) of designing the data structures for this system is to go object-oriented:

    Item: base
    --- Weapon : IEquippable
    --- KeyItem : IUsable
    --- Health : IConsumable
    --- Ammo : IBullshit

    I said naive because most likely if you come up with this design then you probably have no idea what the usage code looks like which is a common mistake a lot of people make. They _assume_ they know their end goal and start designing their API towards a goal that lives somewhere in the clouds. But If you take it the other way around, and write the actual usage code of your API _before_ you design it, things will come to the surface more naturally and you immediately know for sure what you need in your API/how to design your data structures.

    In-experienced because it will cause you many headaches _during_ development and _after_ shipping the game. Mainly issues with versioning and serialization. Change the order/layout of fields, insert a field in the middle of a class, and you're back to being F*ed. Good luck trying not to screw up previous user saved data when updating to a new version of your game.

    Here's a better approach, which would only come to one's mind after carefully studying the input data. If you think about it, what data do items have? Maybe a Name, Id, Damage, IsStackable/Combinable/Usable, ConsumeAmount, AmmoType, CombineWith, etc. These are all simple data types, ints, floats, bools and strings. We can thus represent an item in this format (sample items from my 'unfinished' game):

    Code (csharp):
    1.  
    2. a_HGStandard 1x2
    3. {
    4.   CurrentStack = 30,
    5.   MaxStack = 180,
    6.   RemoveWhenEmpty = true
    7. }
    8. k_Lighter 2x2
    9. {
    10.   IsDiscardable = false,
    11.   UseLocation0 = "LodgeF2",
    12.   UseLocation1 = "RopeShutDoor"
    13. }
    14. w_Shotgun 2x5
    15. {
    16.   CurrentStack = 6, MaxStack = 6,
    17.   FireRate0 = 1, FireRate1 = 1.5,
    18.   FireDistance = 10,
    19.   AmmoType0 = ShotgunShellsA, AmmoType1 = ShotgunShellsB,
    20.   AmmoDamageMin0 = 25, AmmoDamageMax0 = 50,
    21.   AmmoDamageMin1 = 35, AmmoDamageMax1 = 65,
    22. }
    23. h_GreyHerb 2x2
    24. {
    25.   IsCombinable = false,
    26.   ConsumeAmount = 100,
    27.   CurePoison = true,
    28.   ConsumeResult = Positive,
    29.   TargetStats = Health,
    30. }
    31.  
    In editor (this is the 'data provider'):


    Explanation:
    - The prefixes denote the item type: Health, Weapon, etc
    - The format is very simple, (key-value pairs) and flexible (you can add/remove/reorder things however you like)
    - Designers could modify the data without having to interact with Unity (e.g. you could have them enter the data to an excel sheet, and write a simple excel parser and spit the data in the previous format in a preprocessor step)
    - I hope you can see what data structure is most suitable for this type of stroage: Dictionary<string, X>
    - What goes into X? Always refer to the data, we're dealing with ints, floats, bools and strings. We can write:

    Code (csharp):
    1.  
    2. public struct DataValue
    3. {
    4.   readonly float ValueFloat;
    5.   readonly string ValueString;
    6.  
    7.   const float NotUsed = -999; // or something else
    8.  
    9.   public DataValue(int value) { ValueFloat = (float)value; ValueString = null; }
    10.   public DataValue(bool value) { ValueFloat = value ? 1 : 0; ValueString = null; }
    11.   public DataValue(float value) { ValueFloat = value; ValueString = null; }
    12.   public DataValue(string value) { ValueFloat = NotUsed; ValueString = value; }
    13.  
    14.   // implicit conversion operators to convert back and forth between DataValue and int, string, float, bool
    15. }
    16.  
    Not going into much details but reasons for choosing struct over class is obvious (memory allocation), why not 'object' instead of a float/string? because of boxing/unboxing. Yes we always end up with 4 bytes extra storage but who cares. We wouldn't have that if the designers of C# left us unions.

    Now we can say:
    Code (csharp):
    1.  
    2. var lighter = new Dictionary<string, DataValue>();
    3. lighter["IsDiscardable"] = false;
    4. lighter["UseLocation0"] = "LodgeF2";
    5. lighter["UseLocation1"] = "RopeShutDoor";
    6.  
    Hope you see how nice that is. Of course you're not going to do that ^ in the game. You will have your Data/Resource provider parse your data files and load them into the dictionary automatically. You will probably also want to avoid string literals and use constants in a static class.

    Code (csharp):
    1.  
    2. public static class k
    3. {
    4.   public const string IsDiscardable = "IsDiscardable";
    5.   etc...
    6. }
    7.  
    And probably have an Item class:
    Code (csharp):
    1.  
    2. public class Item : Dictionary<string, DataValue>
    3. {
    4.   // Properties for convenience
    5.   public bool IsDiscardable { get { return this[k.IsDiscardable]; } set { this[k.IsDiscardable] = value; } }
    6. }
    7.  
    This is what is meant by 'user data' back in the second diagram.
    Some nice things about this:
    - Could be used to represent the majority of structures in your game (Player, Item, Enemy, etc)
    - Easy to serialize. You're just serializing _one_ thing which is the dictionary.
    - No need to worry about versioning, you could add/remove/replace fields however you like, it won't change the fact that it's a dictionary
    - Keeps inheritance flat. Lets you represent _all_ items with a single class yet still maintain the uniqueness of items. You could go super generic and call the structure DataDef or DataContainer or whatever and stick whatever properties you want, use partial class if you want to split to files if you're working with a team & source control. e.g.

    Code (csharp):
    1.  
    2. public class DataContainer : Dictionary<string, DataValue>
    3. {
    4.   // item properties...
    5.   // player properties...
    6.   // enemy properties...
    7. }
    8.  
    You might ask:
    - How the heck can I inspect this thing in the editor if it's not serializable?
    1- This is where VFW comes in super handy with its [Show]. Just mark the dictionary with that and it gets shown even if it's not serializable (this is one major difference between Unity and VFW editor systems, in Unity: inspection/visibility comes as a side effect to something being serializable, in VFW both things are separate, you could have both, or either alone)
    2- You could use SerializableDictionary instead of Dictionary if you want to load your data in editor, tweak the loaded values and see the results after you run the game.

    - How can I store arrays and not just simple single values?
    Simple, look at the Lighter item example ^. It has UseLocation0 and UseLocation1, which is basically an array of two values. You could then write a custom iterator on your dictionary and iterate over the UseLocations easily in a foreach loop! foreach(string location in item.Iterate("UseLocations")) (I can share the code for that if anybody's interested)

    - What about references to other transforms, gameobjects, sprites, etc?
    1- Let's take sprites for instance, since we're dealing with items, each item has an icon. You could make things a lot easier on yourself if you follow a consistent naming convention. So the icon for "Lighter", would be "Icon_Lighter". Store icons in a known folder that your ResourceLoader/DataProvider knows about, at edit time it would just load all the icons first and then load the items. When loading the items you just reference the right icon: "Icon_" + ItemName
    2- gameobjects, transforms, etc: Just don't bother saving or loading those. Always set them up in Awake/Start.

    This is how your ResourceLoader/DataProvider could look like:
    Code (csharp):
    1.  
    2. public class DataProvider : BaseScriptableObject
    3. {
    4.   public DataContainer[] ItemsData;
    5.   public DataContainer PlayerData;
    6.   public DataContainer[] EnemyData;
    7.   // etc...
    8.  
    9.   [Show] void LoadAllData()
    10.   {
    11.   LoadItemsData();
    12.   // etc...
    13.   }
    14.   void LoadItemsData()
    15.   {
    16.   // load the file that contains the items data in the form mentioned above
    17.   // you could store the file in Resources folder and use Resources.Load for example
    18.   }
    19. }
    20.  
    - What about the rest of the "resources" category: prefabs, sound effects, etc?
    1- Depends on your game and how you want things setup. Any solution is good as long as it doesn't involve dragging/dropping stuff. For example, you could just have arrays of Prefabs and sound effects pre-loaded in the editor before you run the game, you only need to load this once in editor and make use of the fact that Unity knows how to serialize its types, just store arrays of GameObjects and load them via (editor-only code) Directory.GetFiles and AssetDatabase.LoadAssetAtPath.
    2- In the game, how do you reference your sounds/prefabs? I prefer to use metaprogramming for this, and generate IDs for my assets. Let's say I have a 'Jump' and 'Walk' sounds, in my loader code, besides loading the resources I have a button to generate IDs for my assets, so I could generate:
    Code (csharp):
    1.  
    2. public static partial class IDs
    3. {
    4.   public const int SFX_Jump = 1;
    5.   public const int SFX_Walk = 2;
    6.   // etc..
    7. }
    8.  
    In its own file ^
    Same thing for prefabs, you could generate them in a different file and make use of partial classes.
    3- When your loader loads the data, you could for example build a lookup table so it instantly knows what AudioClip SFX_Jump is mapped to, or linear search it, whatever works for your needs.

    The beauty of all this, if anything gets screwed in your editor view, you just press a button and everything's loaded back automagically! Your data is stored in formats you _trust_ and have _total control_ over. No shady Unity business.

    - Inspectable delegates, polymorphic types, etc?
    Just forget about it, just forget about it...

    - Hot-reload?
    Lol. One of things I thought was really cool about Unity is that it lets you modify your scripts while the game is running and reloads them for you and everything still works. The idea is very cool and very useful, it's one of the coolest things I learned from Handmade Hero and implemented immediately in my WIP engine. But the way it's implemented in Unity is very poor. You have to make sure your stuff play nice with Unity's serialization (which is what we're trying to avoid in all the previous mumbling), and you don't have a legitimate callback when Unity does the reload (e.g. an OnReload) so you can rewire your things back up. The only callback you could use is OnEnable, you end up with duplicate code in Awake/Start and OnEnable making sure your stuff is initialized, this makes OnEnable a callback that gets called in two places; when reload happens, and when you enable your object. But what if you want the code in it to _only_ run on reload or on enabling? you're out of luck... It's a huge hassle, not worth it in Unity IMHO.

    Connecting all the dots, I like for VFW to act as the window to your data in that diagram via its BaseBehaviour and advanced editor features, no more, no less.

    I wrote The serialization features of BetterBehaviour back when I was just getting started, back when I was an object-oriented guy, I thought it was useful at the time, it was tempting to add all those seemingly cool stuff, there was no way I could have foreseen what's to come.

    I'm working full time now so I can't keep the same attention and focus I used to give to the framework. But if I had time, the next VFW update would deprecate most of the runtime and serialization stuff, and remove them all together in another future update. I would still leave a Legacy package for users still interested. It's a tough choice because a lot of people are using it, but at some point you have to admit you made a mistake and correct your path and let others know about it.

    No idea if any of that was helpful to anybody, but that's just my 2 cents on the situation.
     
    Last edited: Jan 23, 2016
  42. dtaralla

    dtaralla

    Joined:
    Jan 1, 2016
    Posts:
    16
    Hello everyone!
    First thanks for the awesome framework, thumbs up! :)

    I just wanted to ask if anyone had troubles running the "DrawersInEditorWindowExample", from "VFW Examples/Editor" ? When I click on the circle next to the interface field, the selection window shows an empty list of types instead of showing the two implementers. Plus, same thing with the "guiType" field: the combobox (or "popup menu" as some call it) is empty, I'm not able to select anything. Of course the whole VFW package is imported in my project and there are no compiling errors.

    Am I missing something? I ask because I mimicked this manner of coding for one of my EditorWindow subclass, and again I can't select my implementers when clicking on the circle next to the field... :(

    Thank you in advance :)
     
  43. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @dtaralla Sorry about that. As a temporary fix, go to ShowTypeDrawer.cs and change the code in the else statement to:

    Code (csharp):
    1.  
    2. availableTypes = AppDomain.CurrentDomain.GetAssemblies()
    3.   .SelectMany(x => x.GetTypes())
    4.   .Where(x => x.IsA(attribute.baseType) && !x.IsAbstract)
    5.   .ToArray();
    6.  
    Fixed in next update.
     
  44. KhaledM

    KhaledM

    Joined:
    Sep 29, 2013
    Posts:
    41
    @vexe Thank you very much for your detailed explanation and your effort in this beautiful framework, of course I use excel sheets for some of my input data and I already made a simple parser to fill my data into objects, scriptable objects,
    I will take into account your suggestions, but of course for the next project, for now I will try to eliminate the usage of BetterBehavior as much as I can.

    Thanks again.
     
  45. dtaralla

    dtaralla

    Joined:
    Jan 1, 2016
    Posts:
    16
    Hey @vexe,
    Thank you alot for your fast answer! I implemented your fix and it solves the issue of the popup menu for the GUI in the example, but it does not solve the other bug which is being able to select an implementer of an abstract class/interface... :(

    Thank you :)
     
  46. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @dtaralla ah so you want to choose an abstract type. Just remove the !x.IsAbstract check in the where filter.
     
  47. dtaralla

    dtaralla

    Joined:
    Jan 1, 2016
    Posts:
    16
    @vexe I think you misunderstood me; I meant when I click the circle next to the "Itface >" field of your example editor window, I cannot select any implementer class like "TestClass1" and "TestClass2" of the Itface "ITestInterface" type. Is it normal ?
     
  48. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Oh you're talking about the selection window, hmmm. I might have to check that out when I get back home. If you're a Ninja see if you can fix it yourself. The code of interest is in RecursiveDrawer.cs, it's what draws structs, classes, interfaces etc. I'm probably doing something stupid there. In your editor search for 'SelectionWindow' and it should bring you to the right spot in the code.
     
  49. dtaralla

    dtaralla

    Joined:
    Jan 1, 2016
    Posts:
    16
    Yeah I figured out it was in those files I had to look for, but I'm not enough familiar with this level of C# in the types etc :) Notably I think you should look into lines 91 to 106 of RecursiveDrawer.cs. Line 103 it seems strange to constraints ourselves to interface implementers that inherit from MonoBehaviour? Also you do not seem to cover the case for abstract classes, only for interfaces?
     
  50. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    The problem with exposing 'abstract' types in this context is that it doesn't make sense because you can't instantiate them so the drawer has got nothing to work with. In ShowType it makes sense cause we're just assigning a type value to our Type field. Does that make sense?