Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. 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. Inok

    Inok

    Joined:
    Feb 5, 2014
    Posts:
    16
    I not know because not use your extenstion yet. Just say, your extenstion can manage this option?
     
  2. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Well, yes. I don't know if you watched this demo, it shows a saving system similar to whydoidoit's UnitySerializer but much more simplified. With it, you should be able do exactly what you asked (save a scene, save a specific object hierarchy with its components etc) - Unfortunately I didn't have the time to try it on a few example projects so that I can include it with VFW, I might just put a few example scripts but I'm not sure yet, I'd rather have some example project dedicated. Maybe I'll put it as a separate package so you get a regular VFW and VFW + SaveSystem.

    If you're interested in trying it, let me know I'll hit you back with a PM.

    You can still do it in the current version of VFW but it would take a little bit more work.

    I suggest you have a look at the current version, mess with the serializer, it can do a lot! You might be able to solve your problem without the SaveSystem.

    Let me know if you need any help on anything along the way.
     
  3. Inok

    Inok

    Joined:
    Feb 5, 2014
    Posts:
    16
    Thanks for the reply, I will try your extension anyway.
     
  4. bigdaddy

    bigdaddy

    Joined:
    May 24, 2011
    Posts:
    153
    While this package looks great (and works great in the editor), unfortunately it doesn't work on iOS (using version 1.2.6).

    I'm bummed.
     
  5. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hi @bigdaddy no worries, it 'should' work with a little fix. I was waiting for someone to say it didn't work for him on iOS so I can detect what parts are causing the issues and fix them. Most probably it's due to Fasterflect's delgeate API and the usage of dynamic code to generate delegates. Unfortunately I don't have any ios device so I can't test. If you can give me the errors you're getting I should be able to wrap those dynamic parts with #ifs and just use regular reflection. I assume the errors are coming from RuntimeMember?
     
    Last edited: Jan 15, 2015
  6. bigdaddy

    bigdaddy

    Joined:
    May 24, 2011
    Posts:
    153
    @vexe: Sent you the errors (as you know since you responded :p )
    My ios devices are at your convenience
     
  7. bigdaddy

    bigdaddy

    Joined:
    May 24, 2011
    Posts:
    153
    Great working with you Vexe - looks like the next version (1.2.7) will be iOS compatible!
     
  8. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @bigdaddy Thanks! glad I helped :) - 1.2.7 is out with a bunch of editor fixes/improvements and like you said, iOS support!
     
    sGlorz likes this.
  9. Rickshaw

    Rickshaw

    Joined:
    Dec 3, 2013
    Posts:
    5
    This looks amazing. Why did I have to google around for weeks, starting and stopping implementations of this sort of thing before I found this? Why is this functionality not a standard part of unity? The documentation even states that one ought to be able to serialize a gameobject, and over on this post (http://answers.unity3d.com/questions/185005/unity-types-unable-to-be-serialized.html) we read from a dev:

    This is an example of usage. http://msdn.microsoft.com/en-us/library/system.runtime.serialization.surrogateselector.aspx

    You just have to write a surrogate for every Unity type that you want to serialize. GameObject, Transform, etc. Our implementation for a GameObject surrogate sucked. Need to rethink that one. :)

    Hope this helps

    Aug 21, 2012 at 02:18 AM​

    Anyway, when I imported the package 1.2.7 into a blank project, I get the error:
    Assets/Plugins/Vexe/Runtime/Types/Core/BetterBehaviour.cs(84,24): error CS0246: The type or namespace name `RectTransform' could not be found. Are you missing a using directive or an assembly reference?

    This is problem with my version of Unity. I'm in the process of downloading 4.6 and in the meantime I'm gonna go get a couple beers.
     
    Last edited: Jan 20, 2015
  10. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    @Rickshaw thanks for dropping by.

    You're right, surrogates/converters are key when it comes to serializing foreign objects. That's basically how the SaveSystem works (which I haven't released yet, but will eventually...) - It's really easy to implement as long as your serializer is solid and supports custom converters/surrogates.

    As for the import error, RectTransform is available in Unity 4.6. You're the second user that had to get this error, I should probably wrap it with #if UNITY_4_6
     
  11. Rickshaw

    Rickshaw

    Joined:
    Dec 3, 2013
    Posts:
    5
    What gets me is that they've clearly thought about this issue / feature, and it sounds like they even maybe had a broken implementation of it at one point, and the documentation still incorrectly states that a gameobject is serializable... It's not like this is some obscure thing, it is a fairly common feature of more advanced games (anything with a persistent world, really). I think Unity is dropping the ball here.

    Yes, the package works fine with 4.6 and I'm poking around now. Very impressive piece of work!
     
  12. Rickshaw

    Rickshaw

    Joined:
    Dec 3, 2013
    Posts:
    5
    Hi Vexe,

    So after a bit of poking around I've gotten your SaveManager to successfully save and load a few gameobjects and it works as advertised.

    I know your save system is not yet 100% complete, from poking around in the documentation and files and specifically looking at the converters the manager references. I've also seen your posts elsewhere about the difficulty of serializing e.g. an entire texture when just serializing the reference to the asset would be a lot simpler. I'm working specifically with Unity's 2D system here, and noticed that the save / load does not include 2D sprite renderer (I haven't done a full test of everything else I would need yet)

    I can think of a few workarounds here, or could perhaps even write my own sprite / texture / etc converters which would not have to be perfect since they would only have to work with my project. But I also don't know how far along you are on all this so I wanted to ask you how you think I should proceed ( hack / fork / wait? ). This package has already done 99% of the work, and 100% of all the extremely hard work, so I'm incredibly blown away and thankful that it is free.
     
  13. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Wait waaaa....? o_O Sigh. I must have forgotten to uncheck the SaveSystem files, oh well, lol. I just wanted to include some test scenes and some proper doc about it, maybe make a tutorial before I release it. I really didn't mean to include it haha.

    But hey if it's useful to you as-is I'm cool with that! :D

    The way you'd go about it is, yes like you said, just write your own converters or whatever you'd like to support. It's pretty straight-forward, in fact you could just copy-paste the 3D stuff and adapt them in 2D (the Rigidbody, Collider etc pretty much the same codes) - except I highly recommend that (if it's possible) you write these somewhere in your own project space, not inside VFW. Reason being, if you update to a newer version changes high I will not have your additions and so they might get overwritten. So best is to extend the system in your space. Or if you want, you could just write the codes in the plugin and copy/paste your stuff before updating. Just letting you know about this so you don't lose anything.

    A couple of words about the system:
    1- You don't need to give unique names to your gameObjects, but you do need unique names on your saved assets (textures, audio files etc)
    2- You must *not* have components of similar types on a saved gameObject (i.e. two Rigidbodies etc)
    3- Destroying gameObjects that are added after a save point when loading, is done externally by DestroyAlienGameObjectsOnLoad (there's a similar script for components on a gameObject-level)
    4- Use the EventManager to subscribe to when saving/loading starts/ends (see the previous mentioned script as an example)
    5- Make sure you have a StoreManager object in your scene, and a Store created to capture whatever assets referenced in your scene. You 'capture' them by just saving the scene once. I usually do it before I run the scene. But if there's assets that get instantiated later in your scene and not available initially, you should find a way to let the Store know of it. Either by running the scene and capturing at the point where the asset appears, or by manually adding it to the Store's items dictionary at edit time - I think I left the _items readonly for editing, you might want to remove that constraint)

    Weaknesses: 1- Dealing with animations (currently can't get an animation back to the exact frame it was at when saving) 2- Saving/Loading dynamically allocated objects, it's just not fully tested yet.

    I was intending to add: SaveComponent, SaveGameObject, SaveScene with versions that return a string (so you can easily save to Player/BetterPrefs or what-have-you), versions that take a Stream (so you can save to file, network etc)

    There's probably more things I'd like to add. You got me off-guard here :oops:

    Feel free to ask me any questions.
     
    Last edited: Jan 22, 2015
  14. Rickshaw

    Rickshaw

    Joined:
    Dec 3, 2013
    Posts:
    5
    Wow! Well for me at least this has been a happy accident... I must admit, I was wondering to myself as I poked around "why did he keep saying the save system wasn't released yet?" etc.

    I appreciate that you're okay with this and thanks for the tips. This post is great, I think that gives me everything I need to take a crack at this stuff. I wanted to get my hands a little dirty anyway!

    Of course the extra functionality you're working on should be very useful, but I think for my purposes what I have right here should be more than sufficient.

    I'll let you know how it goes!
     
  15. nporaMep

    nporaMep

    Joined:
    Oct 31, 2014
    Posts:
    33
    Hi Vexe,

    I'm just starting to apply your plugin on my project and got a question - you have Reset method in BetterBehaviour which is default method in Unity to user's action "Reset" on component. And so I'm getting warning if I don't use "new void Reset()" for my implementation.
    I can add new for method, but does it break something that I'm hiding yours Reset implementation. I see it does something with reflection but I don't understand it yet.

    And also you have those Runtime Extensions e.g. Disinclude - there is no source code for them?
     
    Last edited: Jan 22, 2015
  16. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hey @nporaMep, thanks for dropping by.

    In my Reset, I see if any member has [DefaultAttribute] applied on it, if so I take whatever default value is set and assign it to the member. Initially in VFW I had issues with giving fields default values, so I opted with having a custom attribute to do that. See the example in the Attributes scene. DefaultsExample.cs I think. Then, I supported initializing fields to default values. But I kept the attribute cause it also works for properties, and I do like the ability of giving my properties default values.

    Now that you've come across it, I think will mark it as virtual in next update so you can override and call base if you want that behavior.

    In the meantime, you can either use the new keyword like you mentioned, or make the reset in BetterBehaviour virtual and override your own.

    Hiding doesn't 'break' anything. You get a warning because the hiding could be unintentional, so it's just letting you know that there's already a Reset method out there. Adding 'new' just says, "hey, I know what I'm doing, I know there's a Reset method already, but I want you to stop bitching and use this one instead, forget about the other one."
     
    Last edited: Jan 22, 2015
  17. rahuxx

    rahuxx

    Joined:
    May 8, 2009
    Posts:
    537
    Hey M8 can you start some new tutorial series on using your tool too?
     
  18. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hey @rahuxx, I will do eventually, just gotta find some free time. What topics/things you're curious to know about, or not clear enough?
     
  19. nporaMep

    nporaMep

    Joined:
    Oct 31, 2014
    Posts:
    33
    So to breakdown:
    1) If I want default Unity behaviour for Reset I just:
    add void Reset() and get warning OR
    new void Reset() and get no warning
    2) If I want to use DefaultAttribute without any specific logic in Reset() I just decorate code with [Default] on fields/properties
    3) If I want to use DefaultAttribute and have specific logic in Reset I mark Reset as virtual in BetterBehaviour and override it with calling base.Reset() first.

    Right?

    To add to rahuxx question: I'm most interested in information on:
    1) Which platforms may have problems to run if I use your framework
    2) Is there performance impact for release build in runtime if I use your framework.
    3) Comparison of VFW and uFAction frameworks.
     
    Last edited: Jan 22, 2015
  20. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Let's step back here for a minute. Reset is used to reset whatever variables you have to their initial values. So if you had:
    Code (csharp):
    1.  
    2. public class Test : MonoBehaviour
    3. {
    4.     public int x;
    5.     public int y = 10;
    6. }
    7.  
    Clicking on the gear icon in the inspector and hitting Reset, will reset 'x' to 0 (since it's not initialized to a certain value and 0 is the default value to integers) and 'y' to 10 (since that's what we specified in our initialization of y)

    But, you can intercept this callback and define how exactly to reset your behaviour by writing your own Reset method:

    Code (csharp):
    1.  
    2. public class Test : MonoBehaviour
    3. {
    4.     public int x;
    5.     public int y = 10;
    6.  
    7.     void Reset() { x = 100; y = 200; }
    8. }
    9.  
    Calling reset now will set x to 100 and y to 200, instead of 0 and 10.

    Now, in BetterBehaviour it acts pretty much the same thing, except I added something extra to Reset so it takes into consideration the DefaultAttribute. For ex:
    Code (csharp):
    1.  
    2. public class Test : BetterBehaviour
    3. {
    4.    public int x;
    5.    public int y = 10;
    6.  
    7.    [Default("Unnamed")]
    8.    public string Name { get; set; }
    9.  
    10.    [Default(Instantiate = true)]
    11.    public uAction action { get; set; }
    12.  
    13.    [Default(Enum = (int)KeyCode.Space)]
    14.    public KeyCode jumpKey { get; set; }
    15. }
    16.  
    Modify these values however you want in the inspector then call Reset. 'x' will be set to 0, 'y' to 10, 'Name' to "Unnamed", 'action' will get a new instance, 'jumpKey' will be Space. You do not need to use Default with fields, you can just give them default values immediately during initialization, but it's useful for properties since you can't initialize them like you do with fields. i.e. you can't write int x { get; set; } = 10;

    Say you don't like that behaviour, you can write your own Reset:
    Code (csharp):
    1.  
    2. public class Test : BetterBehaviour
    3. {
    4.    // .. same fields and properties
    5.    void Reset() { // do whatever ... }
    6. }
    7.  
    Now when you go Reset from the inspector, your custom Reset will be called and not the one in BetterBehaviour. But, here you will get a warning saying that there's already a Reset in BetterBehaviour and your custom Reset is hiding it. So if you meant to have a new Reset, just add 'new' before it. So it becomes "new void Reset() { ... }" - You can still get the base Rest behaviour by calling base.Reset()

    Another way to go about this is mark BetterBehaviour.Reset as virtual, and override it in Test.

    However; new and virtual/override aren't quite exactly the same thing. See here.

    For our purposes, any approach here would work.

    If you want the default Unity MonoBehaviour Reset functionality, you don't need to do anything. The Reset in BetterBehaviour already does it.

    Hope that helps.
     
    Last edited: Jan 22, 2015
  21. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    1) Currently, standalone and iOS don't seem to have any problems. I assume the same for Android. Other platforms, such as WP, consoles, BlackBerry etc I'm not sure about because I don't have those devices to see if things work. You might run into reflection problems in some platforms such as WP, but there's always solutions for them using platform dependent compilation.

    2- Shouldn't be anything of high impact. The only runtime overhead BetterBehaviour leaves is when serializing/deserializing (or saving/loading if you're using the SaveSystem). You have to profile and see for yourself. See this and this to read about when serialization callbacks get called at runtime.

    3- uFAction is just a package that offers serializable/inspectable delegates. It's kind of like the delegates in NGUI and the new UI system, but on steroids. VFW is a full fledged runtime and editor framework with many features and abilities such as a custom/advanced serialization system, a better drawing API, and a faster GUI Layout system.. VFW has serializable/inspectable delegates too, only difference being is that they're not as sophisticated as the ones in uFAction. But they also have pros over the ones in uFAction.

    - uFaction delegates
    - Pros; 1- Extension methods support. 2- A variety of delegates including a non-strict untyped delegate (KickassDelegate) that you could hook any method with any signature to it. 3- Multiple editor choices, there's a compact and more verbose views. 4- The ability to target private methods. 5- The ability to selectively choose values to invoke your delegates with from the editor either by means of a direct value or from a source. 6- Customization
    - Cons: 1- Can't have sequences (arrays/lists) of delegates. 2- You must subclass to create new types of delegates (due to how Unity's inability to serialize generics).

    - VFW delegates:
    Pros: 1- Opposite of uFAction con #1. 2- Opposite of uFAction con #2. 3- The implementation is 'very' simple, which makes it more robust and much easier to maintain.
    Cons: 1- Opposite of uFAction pro #1. 2- Doesn't have a KickassDelegate equivalent. 3- A very simple editor. Not that this is a bad thing, but compared to uFaction's editors it's more limited. 4- Only public methods. 5- Can't selectively choose what values to pass from the editor.

    From experience though, I found that the delegates in VFW are more than enough for most cases. Don't really need the sophistication in uFAction. I was going for a 100% solution there, not the brightest idea. Most the times, 85% solutions are better.
     
    Last edited: Jan 22, 2015
  22. rahuxx

    rahuxx

    Joined:
    May 8, 2009
    Posts:
    537
    A good beginner style tuts first in which we develop editor tool that interact with scene objects, like modifying transforms, materials, and other stuff.
    This will be super cool to start buddy.
     
  23. MaDDoX

    MaDDoX

    Joined:
    Nov 10, 2009
    Posts:
    764
    First and foremost, congratz and thanks for an amazing extension. I'm working on a simple yet feature-complete FSM using VFW editor features, it's working nicely so far but I've stumbled on an issue. I don't really know how to instantiate a new contained class when creating a new list member, it just points the new entry to the former "Add pair" class value. I've tried making the custom class in the list a BetterBehavior but that has hid all its fields. I've read you mentioning the Reset method but I'm not sure it would apply here. Please watch this short video, should make things clearer:



    I'm also curious on how to use the 'Kickass delegate' instead of the fixed-parameters Action, does that require ufAction or something? I might resort to interfaces instead, they seem to do what I want but they're a bit slower to use imo. Thanks for any pointers and please keep up the amazing work!
     
  24. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hey there MaDDox, thanks for dropping by!

    The issue you're describing looks very similar to one I've solved in the past. What VFW version are you using?

    I don't have a 'Kickass delegate' type of delegate at the moment. I wanted to keep the delegates implementation as simple as possible. I've come to find that in each instance where I thought I needed such a delegate, I could make a very simple redesign such that I don't really need it. A finite parameter delegate works in most cases. KD is a sacrifice of performance and complexity. I'll see if I can think of a simple way to add that functionality. Unity UI delegates seem compact (although buggy sometimes), I might go for something similar in terms of interface/functionality. - As for uFAction, I'm not developing it anymore.

    Not sure what you're referring to in "they seem to do what I want but they're a bit slower to use imo", the uAction delegates or, interfaces? (how do you plan on replacing delegates with interfaces?)

    On the side: (Something I've been wanting to talk about for a while, will definitely make a video about it)
    * If you can help it, try to reduce the friction with Unity's serialization system as best as you can. What do I mean by that? Well for example, if you have a non-public field and you want to serialize it, don't use [SerializeField] because then, you're adding extra redundant overhead by having to serialize the field twice now: by Unity and VFW. Instead, just use the VFW custom attributes [Serialzie] or [Save] - it's not that things will break if you use SerializeField, but it's just an extra overhead that you can live without. - Same thing said about custom classes, you don't need to add [Serializable] for VFW to serialize them. So just don't add it. That way it's only serialized by VFW and not Unity. How about public fields? Well, If they're in a BetterBehaviour, having a public field means that's serialized twice. You could do many things: 1- use a public auto-property, 2- use a readonly public field. This is not applied to custom System.Objects that are *not* marked with Serializable, there, you could just have public fields and not worry about it, because the whole class is not serialized by Unity. Hope that makes sense :)
    * You don't need [Show] if the member is serialized

    Btw cool VS color scheme, what's its name?
     
    Last edited: Jan 22, 2015
  25. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Oh and btw, unless it's an implementation detail, but isn't Dictionary<int, X> practically the equivalent of using a List<X>? (in both cases you're using an int to look things up)
     
  26. nporaMep

    nporaMep

    Joined:
    Oct 31, 2014
    Posts:
    33
    Did you have a chance to work on Struct editing in Editor? Any clues what is the problem and how to solve it?
     
  27. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    It's very tricky indeed. The problem is that structs are value types, when you pass them around functions, you're basically passing copies of the original struct, and so the editor is basically modifying a different copy of the struct. I've made a couple of attempts of fixing it, one of them was to change my functions signature to take a ref object target so I pass the structs by reference, caused more problems. I tried boxing/unboxing it, but the problem is there's many points in my code base where I pass 'object target' around, I need an intense debug session (logs before/after calls to these functions) to identify where exactly the struct is being copied. There are some special keywords such as __makeref but last time I checked it's not available in Unity's Mono. Pretty tedious task. - I'll see if I can find time to give it another shot, I'm pretty sure at one point in my development I will reach a point where I *must* have this fixed.

    Just to be clear to others reading this: Structs are serialized successfully, they're visible but unmodifiable from the inspector.
     
  28. MaDDoX

    MaDDoX

    Joined:
    Nov 10, 2009
    Posts:
    764
    Hey Vexe, I've double-checked, it's the latest version - and really a bug, or there's some dark way around it which I couldn't find. Lists of custom classes with an uAction as one of its fields work fine, it's what I'm using for now - and you're right about the int not being needed, it just annoyed me that your "line number" attribute shows 1 as the first item, instead of 0. Maybe a new "index" SeqOpt could come in handy?

    Back to the point the problem is in dictionaries of such custom classes. Since there's no implementation of them in the project, the first time you add a dict item it does instantiate it properly, after that all new items just point to the first one instantiated - obviously making the whole thing useless.

    As for how to replace the uActions with Interfaces, I basically have a required "Execute" method in the interface, so all the iAction derivates have it. The advantage is that I can use any properties or files and they'll show up nicely in the inspector, plus the implementations hold their own data - very handy for transitional states. The bad side is that this kind of "subclassing" (I know that's not the right term) totally ignores the target object and its data, making the actual coding of the methods trickier/stiffer - that's why I requested the kickass delegate, that'd be much more elegant than having a, say uAction<Megadata>, with Megadata being a class with a list of each main type.

    Right now I'm stuck to a list of uActions, I'd love to get a little bit more whenever you get the chance. One last thing I'd love to do is to have an "OnChanged new entry run method" attribute, and also a reference to that new entry data. This way we could preprocess new items when applicable, and manually initialize more complicated fields.

    As for the VS theme, I'm pretty sure those new colors you see are from Visual Assist X (I use it alongside R#, most kickass combo ever :)), I've just replaced the method's default blue color by orange. In my opinion the coolest theme colors out there is from Appcode, really great on the eyes and super readable. I should replicate it to VS sometime ^_^
     
    Last edited: Jan 23, 2015
  29. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    The sequence index is just for visuals. Didn't think it would matter to anyone it starting from 1. Starting from 0 next version.

    Bug confirmed. Will fix asap.

    I already have an OnChanged(Call = "Method") or you mean something different?

    I try to keep the framework attributes as general as possible, if there's something custom needed for a certain situation, it's best to write a custom attribute and keep it at your own user codebase. But OnChanged is pretty darn useful indeed.
     
    Last edited: Jan 23, 2015
  30. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    So the problem was that when you're adding pairs from the temporary adding area you're adding the same value object to your pairs. And since your value is a reference type, every pair you add will point to the same object (which is the object created in the temp adding area) - I've redesigned the dictionary drawer to be much simpler thus more robust without needing that temp area but with a constraint on the key type: it must be something 'new'ble, so no UnityEngine.Objects or abstract types can be used as keys. I imagine it's not that big of a deal anyway as most key types are strings, enums, or some hash integer. Sent you a PM with the patch as I can't make an official release atm.
     
  31. MaDDoX

    MaDDoX

    Joined:
    Nov 10, 2009
    Posts:
    764
    Thanks Vexe, it works! As for the 'OnChanged' attribute I knew about it, I was talking exactly about the temporary adding area. There are probably more elegant ways to do that, I just thought it could come in handy as a new system event - or even the same, supposing we could access and change those temporary values it wouldn't make a difference. The temp values were (they seem to be gone in your new/beta version) in a black box so it wasn't so flexible.

    The key restriction you added is fine btw, no worries about it.
     
  32. MaDDoX

    MaDDoX

    Joined:
    Nov 10, 2009
    Posts:
    764
    I've been thinking, a very common need when creating custom editors is to show some fields / buttons only when a certain condition (usually a bool == true) is met, just like with your "Advanced" toggle. Is there any way to replicate that behaviour using VFW attributes?
     
  33. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    You mean something like:
    Code (csharp):
    1.  
    2. [Show, VisibleWhen("NotPlaying")] // or ActiveWhen for enabled/disabled state
    3. void SomeMethod() { }
    4. bool NotPlaying() { return !Application.isPlaying; }
    5.  
    ?

    It's possible yes. In fact I've had a similar feature before (but took it out) where you could activate a member only at runtime. But the tricky part is where is the best place to place the code that processes this attribute? It would be awesome if this could be implemented without having to modify the drawing system internals. Haven't thought about it too much but I should be able to come up with something flexible and feasable.
     
  34. MaDDoX

    MaDDoX

    Joined:
    Nov 10, 2009
    Posts:
    764
    Yes, that :) Not sure what you mean by "where to put the code", I was thinking about a regular method, just like what "OnChanged" does. Hope you get around to it sometime. BTW I know, I flood you with requests, sorry lol. I guess I'm a bit too excited about VFW after years of custom editor pain :)

    While we're on it, I'm guilty for not watching your entire video about property grouping and ordering of the fields and properties in the inspector (which means I can't do ordering properly) but it did look a bit complicated. Make you could shed some light on it?
     
  35. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Maybe I made it sound complicated in my video. But it's really simple. The idea is that you could categorize members in many ways, by data type, by regex pattern, by member type, etc. You just specify how you want to categorize things when you define your category, ex:
    Code (csharp):
    1.  
    2. [DefineCategory("Ints", DataType = typeof(int))]
    3. [DefineCategory("Editor", Pattern = "^ed")]
    4. public class Test : BetterBehaviour
    5. {
    6.     public int count, id, items, whatever;
    7.  
    8. #if UNITY_EDITOR
    9.     public Color edGizmos;
    10.     [Show] void edPrintCount() { ... }
    11. #endif
    12. }
    13.  
    You're gonna get two categories, one called "Ints" that will have all ints grouped together. And another called "Editor" which will have any member whose name start with "ed" - There's more things you could do, like explicitly adding members to certain categories, specifying how you want to group the results of your filters (default is intersection), etc. I suggest you watch the video if you want to know about all these things.
     
  36. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    I've just released a new version 1.2.8c. Here's some of the changes:

    I've added a Kickass-like delegate called uDelegate. Users @Jlpeebles and @MaDDoX asked me to do so, so I did.
    The idea is to have a delegate that you could use to hook up methods with arbitrary parameter count and type. You have to set the delegate from the editor, and choose the invocation arguments for the handlers. The arguments are serialized so they make it to the runtime. From there you just call myDel.Invoke() which uses the value you setup in the editor as arguments to the handlers.It has the limitation currently of not supporting overloaded methods, so you won't see 'Translate' in Transform for ex.




    PopupAttribute is now more powerful. You can populate the values from fields/properties and not just methods. The values must either be string[] or List<string> (no int/float support anymore) - you could also populate from the unity target if you're in a System.Object, or from a static member in a type (see PopupsExample.cs for more details)



    I've redesigned the dictionary drawer to be simpler and a little bit more joyful to work with. I've removed that temporary adding area, and instead, now you can add as many pairs as you want, set them up and hit a 'w' (write) button to write those values to the actual dictionary. The button glows orange letting you know you did some modifications and you should write, and red if you have duplicate keys.



    Made a very simple change that enabled applying attributes to method parameters! which is very useful in editor (say you have an inventory and a [Show] AddItem(string) method that takes the item name to create/add - the item names are populated from a database or something, you could just use a Popup on the string parameter and get a list of all the items instead of manually typing it)

     
    Last edited: Jan 30, 2015
  37. sk8terboy4

    sk8terboy4

    Joined:
    Mar 29, 2013
    Posts:
    29
    How can I serialize a generic class?
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine.UI;
    5.  
    6. public class Recipes : MonoBehaviour {
    7.     public Inventory inventory;
    8.     public CraftingSlot craftingSlot;
    9.     public CraftingSlot input1, input2;
    10.  
    11.     public List<Recipe> recipes = new List<Recipe>();
    12.     /*{
    13.         new Recipe(){
    14.             Name = "Wood Sword",
    15.             RecipeItems = new List<RecipeItem>(){
    16.                 new RecipeItem(){
    17.                     Ingredients = Item.ItemType.Stick,
    18.                     Amount = 1
    19.                 },
    20.                 new RecipeItem(){
    21.                     Ingredients = Item.ItemType.Stone,
    22.                     Amount = 5
    23.                 }
    24.                
    25.             }
    26.         },
    27.         new Recipe(){
    28.             Name = "Plank",
    29.             RecipeItems = new List<RecipeItem>(){
    30.                 new RecipeItem(){
    31.                     Ingredients = Item.ItemType.Wood,
    32.                     Amount = 5
    33.                 }
    34.                
    35.             }
    36.         },
    37.         new Recipe(){
    38.             Name = "Bow",
    39.             RecipeItems = new List<RecipeItem>(){
    40.                 new RecipeItem() {
    41.                     Ingredients = Item.ItemType.String,
    42.                     Amount = 3
    43.                 },
    44.                 new RecipeItem(){
    45.                     Ingredients = Item.ItemType.Stick,
    46.                     Amount = 1,
    47.                 }
    48.             }
    49.         },
    50.         // More recipes
    51.     };*/
    52. }
    53. public class RecipeItem
    54. {
    55.     public Item.ItemType Ingredients{ get; set; }
    56.     public int Amount {get; set;}
    57.     public string Name { get; set; }
    58. }
    59. public class Recipe
    60. {
    61.     public string Name {get; set;}
    62.     public string Description{ get; set; }
    63.     public int ID{ get; set; }
    64.     public Sprite Icon{ get; set; }
    65.     public GameObject Model{ get; set; }
    66.     public int Value{ get; set; }
    67.     public int Amount{get; set;}
    68.     public bool Placeable{ get; set; }
    69.     public Item.ItemType Type{ get; set; }
    70.  
    71.     public List<RecipeItem> RecipeItems {get;set;}
    72. }
    73.  
    I am trying to view the List<Recipe> in the inspector and also the List<RecipeItem> in the Recipe class under the List<Recipe> list. So It should look like: recipes->Recipe->(showing name, description, etc)->RecipeItem->(showing Ingredients, Amount, and Name).
     
  38. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hi there! - You just inherit BetterBehaviour instead of MonoBehaviour in Recipes. Don't forget to add using Vexe.Runtime.Types;
     
  39. sk8terboy4

    sk8terboy4

    Joined:
    Mar 29, 2013
    Posts:
    29
    Thanks! This helped a lot.
     
  40. salladin

    salladin

    Joined:
    Apr 18, 2013
    Posts:
    2
    Last edited: Feb 2, 2015
  41. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Order doesn't matter in a dictionary, Why would you reorder it? - For Lists/Arrays, you could annotate with [Seq(SeqOpt.Advanced)] to get a checkbox besides your list/array header from which you can click to enable advance mode, that will show arrow buttons beside each element, click to move up/down. You can also shift, shuffle, etc.. I have no interest atm in implementing fancy drag-drop reordering of elements, I want to keep the default drawer as simple and fast as possible. If you want, you could easily modify the sequence drawer or create your own.
     
    Last edited: Feb 2, 2015
  42. salladin

    salladin

    Joined:
    Apr 18, 2013
    Posts:
    2
    Got it!. Thanks for your reply and awesome Vexe library.
     
  43. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    So I got some good news, some bad news, and some other good news.

    The good news is, I've finally addressed and fixed struct editing in the inspector!
    The bad news is, it currently only works if I use regular reflection in my internal RuntimeMember, and not Fasterflect (which means it's slower)
    The other good news is:
    1- It didn't seem to me it's adding any huge editor performance penalty
    2- There are ways to create fast open instance delegates without having to use Fasterflect. But it's quite tricky, especially when dealing with structs. I managed to get a basic delegate for call method (equivalent to MethodInvoker in Fasterflect) to work with both structs and classes. (in .NET) (see) - But there seems to be a bug in mono that's preventing this from working with structs. I might be able to cook something with Expressions, haven't messed with them before though.

    If anyone knows of a way to create an open instance delegate to a method to call and to a field/property to set/get I'd be happy to hear it.
     
    Last edited: Feb 4, 2015
  44. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    1.2.9 is here. We can finally inspect structs \m/ (@nporaMep)

    Removed some DLLs. There's now vRuntime and vEditor instead of RuntimeHelpers/Extensions and EditorHelpers/Extensions (no change on namespaces)

    The examples are now a separate package.

    And implemented a user-requested feature (@MaDDoX) VisibleWhen (which I also have been wanting to do anyways)

     
  45. kiriri

    kiriri

    Joined:
    Jan 14, 2011
    Posts:
    107
    Hey,
    Thank you for this great extention! I was going to release an open source project soon and while I 'm not yet sure if I absolutely need your plugin per se, I'll include it anyways because I think it has the potential to become a great standart. I'm developing for Unity 5 and there are some runtime editor exceptions in some example scripts.
    More specifically, there's this null exception:

    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. Vexe.Editor.Helpers.GUIHelper.<get_HelpBox>m__99 () (at Assets/Plugins/Editor/Vexe/Others/GUIHelper.cs:325)
    3. Vexe.Runtime.Helpers.RTHelper.LazyValue[GUIStyle] (System.Func`1 getCurrent, Vexe.Runtime.Helpers.Predicate isNull, System.Action`1 setValue, System.Func`1 getInstance)
    4. Vexe.Runtime.Helpers.RTHelper.LazyValue[GUIStyle] (System.Func`1 getCurrent, System.Action`1 setValue, System.Func`1 getInstance)
    5. Vexe.Editor.Helpers.GUIHelper.get_HelpBox () (at Assets/Plugins/Editor/Vexe/Others/GUIHelper.cs:322)
    6. Vexe.Editor.GUIs.RabbitGUI.HelpBox (System.String message, MessageType type) (at Assets/Plugins/Editor/Vexe/GUIs/RabbitGUI/RabbitGUI.cs:415)
    7. Vexe.Editor.Drawers.CommentAttributeDrawer.OnUpperGUI () (at Assets/Plugins/Editor/Vexe/Drawers/User/Decorates/CommentAttributeDrawer.cs:10)
    8. Vexe.Editor.GUIs.BaseGUI.<Member>m__8C (Vexe.Editor.Drawers.BaseDrawer d) (at Assets/Plugins/Editor/Vexe/GUIs/BaseGUI/Controls/Members.cs:99)
    9. Vexe.Editor.GUIs.BaseGUI.<Member>m__8B (System.Collections.Generic.List`1 drawers, System.Action`1 section) (at Assets/Plugins/Editor/Vexe/GUIs/BaseGUI/Controls/Members.cs:91)
    10. Vexe.Editor.GUIs.BaseGUI.Member (Vexe.Editor.EditorMember member, System.Attribute[] attributes, Vexe.Editor.Drawers.BaseDrawer memberDrawer, Boolean ignoreComposition) (at Assets/Plugins/Editor/Vexe/GUIs/BaseGUI/Controls/Members.cs:99)
    11. Vexe.Editor.GUIs.BaseGUI.Member (Vexe.Editor.EditorMember member, System.Attribute[] attributes, Boolean ignoreComposition) (at Assets/Plugins/Editor/Vexe/GUIs/BaseGUI/Controls/Members.cs:52)
    12. Vexe.Editor.GUIs.BaseGUI.Member (Vexe.Editor.EditorMember member, Boolean ignoreComposition) (at Assets/Plugins/Editor/Vexe/GUIs/BaseGUI/Controls/Members.cs:44)
    13. Vexe.Editor.GUIs.BaseGUI.Member (System.Reflection.MemberInfo info, System.Object rawTarget, UnityEngine.Object unityTarget, System.String key, Boolean ignoreComposition) (at Assets/Plugins/Editor/Vexe/GUIs/BaseGUI/Controls/Members.cs:33)
    14. Vexe.Editor.Editors.BaseEditor.OnGUI () (at Assets/Plugins/Editor/Vexe/Editors/BaseEditor.cs:347)
    15. Vexe.Editor.GUIs.RabbitGUI.OnGUI (System.Action guiCode, Vector2 padding) (at Assets/Plugins/Editor/Vexe/GUIs/RabbitGUI/RabbitGUI.cs:125)
    16. Vexe.Editor.Editors.BaseEditor.OnInspectorGUI () (at Assets/Plugins/Editor/Vexe/Editors/BaseEditor.cs:120)
    17. UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor editor, Int32 editorIndex, Boolean forceDirty, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect, Boolean eyeDropperDirty) (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1137)
    18. UnityEditor.DockArea:OnGUI()
    That's Readonly, SceneTransition, Draggable,Path, Sequence,Constraints.

    Furthermore there's

    in AnimVarExample .

    Hope that helps :)

    EDIT: Furthermore GUIStyles do not draw any of their properties, expanding them will cause a

    and another

     
    Last edited: Feb 7, 2015
  46. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hi there! Can't seem to trigger the errors here in 4.6.2 unfortunately. So it's most probably a Unity 5 thing.

    The first error relates to getting the GUIStyle for the helpbox control. I assume they changed where it's located, or its name. The second error, well here's what I'm doing in that function:

    Code (csharp):
    1.  
    2.      public static string[] GetAnimatorVariableNames(Animator animator)
    3.      {
    4.        var controller  = animator.runtimeAnimatorController as AnimatorController;
    5.        int count  = controller.parameterCount;
    6.        var variables  = new AnimatorControllerParameter[count];
    7.        variables.For(i => variables[i] = controller.GetParameter(i));
    8.        return variables.Select(v => v.name).ToArray();
    9.      }
    10.  
    I'm guessing from the error they made 'parameterCount' non-public.

    So it seems there's some adjustments to be made for my fw to support Unity 5 - but hey, that's what you get for relying on the internals :p - I'm sticking to 4.6.x for now. If you want, you could give it a stab: Decompile the editor dll in ILSpy, and find out where that helpbox GUIStyle is, and modify the code getting it in GUIHelper (it was previously in EditorStyles, non public, static named "helpBox") - For the parameterCount, pretty sure it should still be there but just non public. Should be able to access it with reflection. Copy paste that function above, access the count via reflection, replace the previous call (EditorHelper.GetAnimatorVariableNames) with your own modified version of the function. Should be very straight-forward - Let me know if you need more help :)
     
  47. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Looking back at that previous snippet, I don't know what was I thinking with that AnimatorControllerParameter array... all I need is a string[]:
    Code (csharp):
    1.  
    2.        var variables  = new string[count];
    3.        variables.For(i => variables[i] = controller.GetParameter(i).name);
    4.        return variables;
    5.  
    smh....
     
  48. kiriri

    kiriri

    Joined:
    Jan 14, 2011
    Posts:
    107
    Good news : Unity 5 features direct getters for all your favorite GUIStyles. That means all you need is EditorStyles.helpBox :)

    EDIT: I know how you feel :D
     
  49. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Nice! Could have avoided the problem altogether had I used BindingFlags.Public with the flags that are currently there (non-public | static) - What about parameterCount?

    [Edit]: is it "had I" or "if I", does anyone know? wish I remember a single useful thing from school lol
     
    Last edited: Feb 7, 2015
  50. kiriri

    kiriri

    Joined:
    Jan 14, 2011
    Posts:
    107
    still a public list but the getter function for the count has been removed. the count itself still exists still, but it's obsolete and returns 0 . So parameters.Length needs to be used, but since it's an array there's no issues there, right?

    Edit : "Had I" sounds fine, but I'm anything but a linguist so my endorsement means rather little ;)
     
    Last edited: Feb 7, 2015