SmartData for Unity
A designer-friendly, editor-driven framework for connecting data and events.
Need a flexible way to connect everything in your game without singletons, hard-coding or overly-complex architecture? SmartData might be what you're looking for. It is based on Ryan Hipple's (twitter | github) amazing talk from Unite Austin 2017, which you are strongly recommended to watch before proceeding.
- Grab SmartData.unitypackage from the Releases page for everything you need!
- OR, to use the git repository, you'll need our events implementation, Relay. github.com/SixWays/Relay
- Requires Unity 2018.2 or above.
What is SmartData?
Firstly, SmartData is in beta. With that out of the way, SmartData is a framework that uses
ScriptableObjects to pass data around a game at the global level. Let's take a common example - showing the player's HP on the HUD. Let's assume the Player and the HUD are prefabs, dynamically instantiated at runtime, rather than placing them in each scene at edit-time and manually dragging references.
Note that the following pros and cons are entirely subjective. And there are many more methods we could use!
Traditional method A - Singletons
Player class is a singleton. We instantiate the Player prefab, then the HUD prefab, and the HUD uses
Pros: Easy to code.
Cons: Encourages spaghetti code. Rigid coupling between classes. HUD depends on the Player instance existing. Not exposed to designers.
Traditional method B - Find reference at runtime
We don't like singletons, so instead the HUD searches for the Player using
Pros: Coupling is slightly less rigid.
Cons: Player must be called the right thing. HUD depends on the Player instance existing. Not exposed to designers.
We create a
FloatVar asset called PlayerHp. In
Player we declare a
FloatWriter and in
In-editor, each script now has a field in which we can drag-and-drop our PlayerHp asset. Now they both refer to the same data.
HUD now has three ways to get the player's health:
playerHp.valuein an update loop - the PlayerHp asset value will stay up-to-date with
- Use the exposed
UnityEventin the editor and check Auto Listen - the event will fire when
Playerupdates the value
playerHp.BindListener()to register for the event in code when
Playerupdates the value
Pros: No code coupling. HUD can exist without Player (PlayerHp asset always exists). Exposed to designers.
Cons: More complex.
While SmartData takes a little more setup, it's far more flexible, powerful, safe and designer-friendly - and after a couple of uses, that setup is easy.
Now imagine a real codebase. You have dozens of singletons, your code is spaghetti, you need a coder every time a designer wants to get any of that data, and unless you instantiate everything in your scene - in the right order! - your game explodes, making low-level testing and iteration hellish.
With SmartData, global data is just a set of assets. Designers can access it at will with a set of Components, modify its behaviour and select at edit-time what references point to what data. Coders still retain control of their code, specifying read-only or read/write access from a given reference, with attributes to control what options a designer sees in the editor.
SmartData supports any underlying data type via its code generator GUI. It comes with a selection of basic SmartTypes pre-built such as
string amongst others.
Note that any single SmartType only has one underlying data type. The multi-type editor above is simply for generating multiple SmartTypes at once for convenience.
EventVars, accessible via
EventDispatcher references, simply raise events without any data payload. These are particularly useful for game-level events, such as starting, pausing the game, a player dying etc.
Designers can use components which give code-free access to SmartObjects. Read/Listen components give a list of SmartRefs with UnityEvents. Write/Dispatch components give one SmartRef and the ability to set values and/or dispatch events from UnityEvents or other third-party design tools.
SmartData includes a visual debugger graph which shows all the current connections between
FloatWriter etc) and
FloatVar etc). At runtime, it will also animate to show updates and events.
SmartObjects will also show a list of connections in the inspector, and at runtime a list of objects listening to events from code.
NOTE: Plain references to SmartObjects will not show up in the node graph. You must use SmartRefs - e.g.
The above is just scratching the surface. SmartData has many other features which will gradually be documented in the wiki.
- SmartSet - List of data with OnAdd and OnRemove events
- e.g. a list of all AIs - AIs add/remove themselves and the AI manager (and anything else) listens.
- SmartMulti - List of SmartVars created on demand
- e.g. for local multiplayer games - PlayerHp[0-3]
- Decorators - Like components for SmartObject assets, these modify behaviour
- e.g. for clamped/ranged values or even automatic network replication
- Code generation templates
- Add your own templates to the Generate Types editor to generate custom code for each type.
What is SmartData not?
- A visual scripting language / editor
- A coding-free solution
- Particularly good at runtime-instantiated SmartObjects - yet (see What's Next)
- The answer to all your problems
The first focus will be more documentation and examples, since there's a lot here.
The next major planned feature is SmartGroups - a comprehensive solution for dynamically instantiating SmartObjects. Currently SmartData is all about edit-time data - make assets, link them by hand, profit. At runtime, you can create SmartObjects in code, and link to them in code, and destroy them in code, and unlink from them in code. You may have noticed a pattern.
For example, if you want an OnHit
EventVar for every AI spawned, there's no easy edit-time way to specify what will be interested in these events, or how they will be indexed. SmartGroups aim to fix that. More later.
Note that SmartMultis are not intended to supply this functionality - they're a convenience for cases when there will be multiple long-lived instances of something. For example, with local multiplayer, your HUD elements can all reference a single "PlayerHp"
FloatMulti asset by index. There's no good way to destroy one of the underlying
FloatVars and no way to automatically stop listening to it.
- Namespaces are generated for each underlying type. For example,
- As mentioned, you'll need
Relayin your project to use
- If you use Odin Inspector, you must disable it for the SmartData namespace.
- Userspace code (generated classes etc) is nice and neat. The underlying hierarchy though is crazy complex, largely due to Unity's serialization quirks. It looks horrible, but it's all there for a reason. Sorry in advance.
SmartData, as previously mentioned, owes its existence and inspiration to Ryan Hipple (see above)
Development assistance, feedback and ideas provided by Eric Provencher (@prvncher) and Dustin Chertoff (@dbchertoff)
 Relay is not linked as a submodule for three main reasons. Firstly, you're fairly likely to be using SmartData as a submodule itself, and sub-submodules tend to confuse lots of git clients. Secondly, if you're already using Relay (you should be, but we're biased) it could get messy. Finally, Relay is pretty stable now so you should be able to grab a zip and forget about it.
 The graph is not editable!
 but only because nothing ever can be