Home

Awesome

Vexe Framework (VFW)

Custom Serialization is no longer supported: https://github.com/vexe/VFW/issues/88

Contents

What:

Usage:

using Vexe.Runtime.Types;

public class TestBehaviour : BaseBehaviour
{
    [PerItem, Tags, OnChanged("Log")]
		public string[] enemyTags;
		
		[Inline]
		public GameObject go;
		
		[Display(Seq.LineNumbers | Seq.Filter), PerItem("Whitespace"), Whitespace(Left = 5f)]
    public ItemsLookup[] ComplexArray;
}

Here's a bird's eye-view for some of the features:

1- <a name="feature-drawing-api">A new drawing API that has the following features</a>:

  1. You can write drawers for pretty much 'any' System.Type!
  2. Can be applied on any 'member' (method, property or field) You can write a single drawer and apply it on fields or properties! DRY!
  3. The API is strongly typed (uses generics). You get a strong reference to attribute you're writing your drawer for
  4. Fields/properties are dealt with equally the same. This is possible via a 'EditorMember' wrapper around them. That wrapper is strongly-typed, there's also a weak-typed version of it. No more dealing with SerializedProperties!
  5. You can expose 'any' member even if it wasn't serializable! (inspector exposure is independent of serialization).
  6. Fields, properties, methods, generics, interfaces, abstract objects, dictionaries, arrays, lists, etc. Could all be exposed.
  7. Attribute composition works very well, as long as you compose correctly (just like Unity's 'order' attribute, you get an 'id' that you could use to sort your composite attributes).
  8. You can re-use your drawers everywhere! Drawers target a System.Object as opposed to Unity's targeting UnityEngine.Object. So you could easily use your drawers in an EditorWindow for instance.
  9. The drawing API is so flexible, that you can use drawers on members that don't have any attributes on them! pseudo: MemberField(someMember, someAttributes);
  10. When dealing with collections, you have the option whether you want to apply attributes on the collection itself, or its individual elements.
  11. You can use GUILayout, GUI and a custom fast layout system: RabbitGUI!
  12. You can have your composite drawers target specific areas of your target field. So you get a OnLeftGUI, OnRightGUI, OnBottomGUI and OnUpperGUI (for the left, right, bottom, and upper sides of your field) - Composition level: Korean!
  13. Custom/shorter attributes to show/hide things: [Show], [Hide]
  14. Focus on writing your drawers' logic without having to write any type-checks/validation to make sure your attributes are applied on the right field/property type. Apply a Popup on a GameObject field, it just gets ignored.

3- <a name="feature-helper-types">Helper types</a>:

  1. SelectionMemorizer: memorizes your objects selections. Ctrl.Shift.- to go back, Ctrl.Shift.+ to go forward.
  2. BetterUndo: a simple undo system implemented via the command pattern that lets you take direct control of what is it to be done, and how it is to be undone
  3. RabbitGUI: A much faster and cleaner GUI layouting system than Unity's GUILayout/EditorGUILayout with similar API and some extra features.

4- <a name="feature-property-attributes">Tons of property attributes</a>:

  1. Constraints: Min, Max, Regex, IP, NumericClamp, StringClamp
  2. Decorates: Comment, WhiteSpace
  3. Enums: EnumMask (one that works! - credits to Bunny83), SelectEnum (gives you the ability to right-click an enum to show the values in a selection window - useful for enums with large values)
  4. Popups: AnimVar (shows all the variables available in the attached Animator component), Popup (your usual popup - could take a method name to generate the values at runtime), Tags (displays all available tags in a popup), InputAxis (gives you all the available input axes like Horizontal etc)
  5. Randoms: Rand (sets a float/int to a random value with a randomize button)
  6. Vectors: BetterVector (gives you the ability to zero, normalize and copy/paste vector values)
  7. Others: Draggable (allows you to drag/drop the field itself), Path (allows you to drag/drop objects to a string field to take their string value - useful if you have a path field for a folder/file instead of manually writing its path you just drag-drop it, works on GameObjects too). OnChanged (my fav! lets you choose a callback method or property/field to set when your field value changes!), Assignable (lets you assign your fields/properties from a source object)
  8. Selections: SelectEnum, SelectScene: give you a selection window to select objects (from children, parents, etc), enums and scenes in that order.
  9. Filters: FilterEnum, FilterTags: give you a small text field to quickly filter out values to quickly assign your enums/tags
  10. Categories: DefineCategory: defines a member category and lets you categorize your behavior members according to custom rules and filters. Category: annotate members with this, to include them in a certain category. IgnoreCategories: Ignore (hide) previously defined categories.
  11. Collections: Seq: lets you customize the way your sequence (array/list) looks. PerItem: indicated that you want your composite attributes to be drawn on each element of a sequence (array/list). PerKey/PerValue: indicates that you want to apply your custom attributes on each key/value of a dictionary.

Screenshots:

[OLD] Tutorials/Videos (read the video description to see what it covers):

  1. Intro / API Overview
  2. Attributes 1
  3. Attributes 2
  4. Member Categories
  5. Misc Features, and some minor issues

Known Issues:

  1. Undo isn't 100% perfect still, (although it has been improved in 1.3)
  2. Not everything is documented yet
  3. No multi-object editing support yet

Acknowledgments:

  1. Please understand that this is a one-man job, I try my best to make everything work, but I can't make everything perfect. I try to do my best isolating my code and writing unit tests for it to make everything's working OK, but a bug or two could easily slip in... If you want to write a program without bugs, then don't write it.
  2. Apologies in advance if you come across a frustrating bug or any inconvenience due to using this framework, if you do, make sure you let met know.
  3. For help, improvements/suggestions, bug reports: you could post here, pm me or email me at askvexe@gmail.com. I will try to reply ASAP and help as best as I can.
  4. I always use the latest version of Unity for development. Before you report an issue, please make sure you're using the latest version too. If the issue is still there, report it.

FAQ:

  1. <a name="faq-license">Why did you take out the custom serialization features?</a>
  1. <a name="faq-license">License?</a>
  1. <a name="faq-needs-pro">Does this require Unity Pro?
  1. <a name="faq-support-aot">Does this support AOT platforms?</a>
  1. <a name="faq-free">Why free?</a>
  2. Because I believe I can build a base "standard" framework that anyone could use in his own personal projects to benefit from and build upon. The current state in almost all asset developers is that they all come up with 'their' own solutions to the same problems over and over again. This makes it difficult for assets from different publishers to play nice with each other because their backbone is different. For ex say you purchased a property drawers asset that you'd like to use in say, Node Canvas. Well you can't because NC and the drawers asset use a different base for their codes thus making them incompatible.
  3. This stuff should be available to us out-of-the-box let alone free... don't let me start uRanting.
  4. Open source is beautiful. Everyone could contribute to the improvement of the software, fixing bugs, etc.
  5. <a name="faq-drawing-system">How does the drawing system work? And how do I write custom drawers?</a>
  1. ObjectDrawers define how a member looks like in the inspector. Use an ObjectDrawer when you want your drawer to be applied on your type wherever it occurs. Examples of that are IntDrawer, StringDrawer, ListDrawer<T>, ArrayDrawer<T> etc. So whenever an int, string, List<T>, T[] occur, the corresponding drawer will be used to draw them. In your ObjectDrawer you override OnGUI and do all your gui stuff. You will get a strongly-typed 'memberValue' that you could use to modify the value of the member the drawer is targetting. Use the 'gui' property to do all your drawings. You will also get a strongly typed property 'attribute' referencing the attribute your drawer targets.
  2. AttributeDrawer<T, A> where A is a DrawnAttribute. Just like ObjectDrawers, AttributeDrawers also define how a member looks like. Use an AttributeDrawer<T, A> if you want your drawer to only be used when you annotate your members with a certain Attribute. Examples of that are PopupDrawer, AnimVarDrawer and ShowTypeDrawer. PoupDrawer is only used on string if they're annotated with PopupAttribute. So PopupDrawer is a AttributeDrawer<string, PopupAttribute>. Again, you get OnGUI and a memberValue property. It does not make sense to annotate with multiple AttributeDrawers, ex annotate a string with both AnimVar and Popup as there will be a conflict and only one of them will be used.
  3. CompsoiteDrawer<T, A> where A : CompsoiteAttribute. CompositeDrawers are a bit different. They're not used to define a how a member should look like, instead they 'decorate' areas around the member. You don't get OnGUI, but OnLeftGUI, OnRightGUI, OnUpperGUI, OnLowerGUI and OnMemberDrawn(Rect) callbacks. The last callback gets called when a member is drawn, the rectangle of where the member is drawn gets passed. The rest of the callbacks are called in this order: OnUpperGUI, OnLeftGUI, OnRightGUI, OnBottomGUI. You could override any of those callbacks to decorate certain areas of your member. See WhiteSpaceDrawer as an example, it uses those callbacks to add space to the left/right/top/bottom of a member. See PathDrawer as an example using OnMemberDrawn callback. Just like AttributeDrawer, you also get 'memberValue', 'attribute' and 'gui'. Examples of Composites are CommentDrawer, WhiteSpaceDrawer, InlineDrawer, PathDrawer and many more. Unlike AttributeDrawer, you can apply multiple composite drawers on a member. You can define the order of which those drawers are applied/drawn via the 'id' property in CompositeAttribute. Lower comes first.
  4. Applying an attribute on a member that it can't handle will output an error, and then fallback to whatever the appropriate drawer is for that member. For example, applying Popup on a Transform, this doesn't make sense so you will get an error saying that Popup can't handle Trasnform, and your member will be drawn using UnityObjectDrawer.
  5. After you write your drawer, you must register/map it using the TypeDrawerMapper. See RegisterCustomDrawerExample.cs to see how it's done.
  6. <a name="faq-member-order">How are my class members ordered in the inspector?</a>
  1. <a name="faq-doesnt-serialization">What doesn't the serialization system support?</a>
  1. <a name="faq-exact-serialization">What are the exact serialization rules?</a>
  2. Public fields are serialized by default
  3. Auto-properties with at least a public getter or setter are serialized by default
  4. Properties with side-effects (have bodies for their getter/setter) are never serialized
  5. Any non-public field or auto-property marked with [SerializeField] or [Serialize] are serialized
  6. Public fields/auto-properties are not serialized if marked with [DontSerialize] or [NonSerialized]. Note it's better to use [DontSerialize] because it can be used on Properties unlike [NonSerialized]
  7. All the previous applies on readonly fields
  8. For static fields/auto-properties, all the previous is applied if they're in a BetterBehaviour. They might not be serialized in System.Objects depending on the serializer of use. For ex, FullSerializer doesn't support serialization of static fields/auto-properties, but FastSerializer does (will be released soon)
  9. No need to mark types with any attribute to make them [Serializable] (in fact, it's recommended that you don't mark types with [Serializable], see note below)
  10. <a name="faq-serialize-or-serialize-field">So should I use [Serialize], or [SerializeField]? any reason to prefer one over the other?</a>
  1. <a name="faq-converters">How to write converters/surrogates to customize serialization for/add support to certain types?</a>
  1. <a name="faq-view-state">How to view the serialize state of a BetterBehaviour?</a>
  1. <a name="faq-how-to-serialize">How do you serialize a BetterBehaviour to file?</a>