04 Jul 2017

CoreObject

I once built a talk with a collection of new hacks. This talk went through a couple of almost-delivered scenarios and with the new side projects I have since accumulated, scheduling gets difficult.

So I would rather share this stuff in post form now than stall for a possible eventuality. I have no idea how frequent I will be able to post, but here is one at least.

Challenge

Most Unity projects get spread across multiple scenes very quickly. This is advantageous not only for clearly segmented production, but also for tweaking of load times, streaming, and much more.

An unfortunate side effect of this is an increase of challenges in referencing objects in a live session, across those scenes. Desired targets include objects baked into specific scenes and specific instances created at runtime.

Common approaches to solving this include dependency injection and directory singletons – both of which work really great. Unfortunately they both introduce a level of indirection in the Unity serialisation system, making it impossible to track cross-scene dependencies with generic tools. This also usually adds a programming- or finicky book-keeping step to the process of introducing new conceptual instances.

If we could solve this challenge, we would end up with a clear dependency chain in the serialisation system, which would allow us to fully utilise the tracking and tooling benefits this yields, while greatly reducing the barrier to add new concepts and interact with existing ones.

Detour

With each scene a serialised island, there is one more (ignoring bundles) serialisation hub which all scenes can access: Assets.

Both the instance and its consumer can reference the same asset, which we can then use to facilitate a connection between the two. Further, with both referencing the same asset, we now have that dependency information in the serialisation system. Also, adding new conceptual instances is just a matter of creating a new asset, assigning it to either end.

Another bonus is easy listing of all conceptual instances by simply finding all assets of the type. You can demonstrate this via the object picker. Should you prefer a drop-down selector to the object picker, such would be a fairly straight forward piece of property drawer code. You could even utilise asset folder structure to define drop-down nesting.

Implementation

The base (5.3 tested) implementation of this needs three pieces:

  • A ScriptableObject type representing the conceptual instance as an asset.
  • One MonoBehaviour registering a source.
  • A serialized reference to the ScriptableObject type or a MonoBehaviour acting as a gateway to the instance – similar to a MeshFilter component.

In the given example, the instances being passed around are just GameObjects. However in a real world scenario you may also want to create CoreObject<T> derivatives passing references to specific components. Not only does this facilitate quicker access to the components you need – it also naturally separates CoreObjects into categories.

A corresponding instance component could look a little something like this.

Variants

While this base functionality has plenty of utility, I have on occasion played around with variants on the concept. For one, the base idea passes a single-source instance producer to multiple consumers, but there is no reason why you could not use this setup to register/unregister to a list of instances in stead.

A personal favourite variant is more fundamentally different. In stead of facilitating references, the core objects wrap UnityEvents – turning them into global “game events”. This allows you to completely decouple event producers and consumers, while maintaining dependency tracking through the serialisation system.

The pattern I mostly use such events in start with the producers invoking the event via a serialised UnityEvent. Say you expose a “fire” UnityEvent to which you add a particle effect invocation, audio playback, and finally the invocation of the CoreGameEvent “gunshot”.

This “gunshot” event would then be referenced by CoreGameEventHandler components which also exposes a “handle” UnityEvent. The action list of this event could then include stuff like switching the soundscape, putting NPCs into an alert state, trigger an auto save, and whatever else might be useful.

Not only do you get decoupling of produces and consumers – you also get to centralise handling details around the individual consumers as opposed to leaving them all jumbled up at the producer.

So what do you think? Do you use something similar? What cool variants and tooling do you employ?

CoreObject
Unity Protocol Buffers
Behave 2.7
Learn
Behave 2.6
Trusted Gear
Mad Mash Versioning
Behave 2.5
Behave 2.4
Co-very-routine
Construct
The Engine Wars: Numbers
GDC 14: The Quest For Fun
Moving in Unity
Behave 2.3
Unity and .net assemblies
Behave 2.2
ReView
Behave 2.1
Behave 2.0
Unity Hacks: Dual sticks
Unity Hacks: Cameras
Unity Hacks: Touch gestures
OnRenderTextureGUI
Unite 13 video "Unity Hacks" available
The implicit local network interface
Talks and progress
Five years of Unity expertise looking for contracts
Automagic Unity Android Java gadget OF DOOM!
Invading Planet from your couch
Mountain Lion and laggy bluetooth and duct-tape
Unite 12 video and new videos section available
Asia Bootcamp videos now available
Path is now MIT licensed
Behave 1.4 released
So I've been a bit busy lately
Behave 1.3 released
IGDA Unity SIG slides
Second Unity IGDA SIG this evening: Scene construction and AI
First IGDA Unity SIG this evening
Alternative licensing available
Pathfinding in two lines
Path 2 released
Assembling and assimilating
Path 2 intro screencast
Path 2 beta release for GGJ
AIgameDev master class video now online
Expanding beta
Behave AIgameDev master class public stream
Behave master class on open AIgameDev stream tomorrow
Interview with AIGameDev
New video: From tree to code
Issue tracking on github Behave release project
IT University Copenhagen Unity course completed
IT University Copenhagen Unity course files Thursday
CPH IT University Unity course files
Behave 1.2 released
Video: Behave - starting from scratch
Behave runtime documentation updated
Behave 1.1 released
FAFF cleanup: Sketch
Building a menu of delegates and enums
Pick me! Pick me!
Optimising coroutine yielding in C#
Downloading the hydra
New license of Path: GPL
CopyInspector
Magnetic
GUI drag-drop
Logging an entire GameObject
I bet you can't type an A!
Where did that component go?
UnitySteer
New and improved: Behave 1.0 released
Behave 0.3b and unity 2.5
Behave 0.3b hotfix
Path tutorial video available
Path 1.0 launched!
Continued community tutorials
Community tutorial
New tutorial
First tutorial available
Behave 0.3b
unite '08 open-mic session
Behave 0.2b
Behave 0.1b
Behave pre-release
Path beta 0.3b
Path beta 0.2b
Path beta 0.1b
Path pre-release