Blog

Unity's ScriptableObject and Threading

May 20, 2011 in , ,
I found a pleasant solution to a problem I had in Unity that I thought I'd share. This post is technical in nature and won't be of interest to people who don't use Unity.

In my procedural game I'm doing a lot of lengthy calculations and I've been looking into doing them in a separate thread in order to easily spread out the calculations over multiple frames. Using co-routines for this was getting overly convoluted, with yield statements and StartCoroutine() calls sprinkled all over the code-base.

Threading is supported in Unity using the normal .Net (or more specifically Mono) APIs for threading. However, the Unity API can't be touched in anything else than the main thread, or errors will occur. Luckily the lengthy calculations are mostly self-contained and are not touching the Unity API.

However, the calculations do use some data structures that are stored in the form of ScriptableObject. Using ScriptableObject is the simplest way to store some data in Unity with automatically handled serialization. Also see Bas' explanation here.

The problem?
  • Querying the name of a ScriptableObject can't be done outside of the main thread.
  • Doing equality comparisons between ScriptableObjects can't be done outside of the main thread. This includes checking if a ScriptableObject is null.
These are things I can't avoid in my code. So instead I came up with this derived class that I now use for all my ScriptableObject needs:
using UnityEngine;

public class ThreadFriendlyScriptableObject : ScriptableObject {
    
    // Hide the name property with a public variable of the same name.
    // (Also hide this in the Inspector.)
    [HideInInspector]
    public new string name;
    
    // Set it to be the same as the property was.
    // (OnEnable will be called from the main thread so it's ok here.)
    void OnEnable () {
        name = base.name;
    }
    
    // Override equality operators to avoid calls into
    // the Unity API when doing comparisons.
    
    public static bool operator == (
        ThreadFriendlyScriptableObject a,
        ThreadFriendlyScriptableObject b
    ) {
        return object.ReferenceEquals (a, b);
    }
    
    public static bool operator != (
        ThreadFriendlyScriptableObject a,
        ThreadFriendlyScriptableObject b
    ) {
        return !object.ReferenceEquals (a, b);
    }
    
    public override bool Equals (System.Object other) {
        return object.ReferenceEquals (this, other);
    }
    
    // We get a compile warning if we don't override this one too
    public override int GetHashCode () {
        return base.GetHashCode ();
    }
}
Everything works great for me using this class. There's a few limitations of course:
  • You won't be able to access or modify the actual name of the ScriptableObject. This is not a problem unless you need the name to change.
  • You won't get Unity's nice behavior of making a reference to an Object look like it's null if the Object was destroyed. This is only a problem if you plan on destroying the ScriptableObjects at runtime. Since the main function of ScriptableObjects is to be persistent assets, you're not likely to want or need this.
Use at your own risk. :)

2 comments:

thpetsen said...

Thanks for sharing, you *could* kick someone at Unity to allow read access to member variables, this worked well in 2.6, but someone added code to prevent this in 3.x
Cheers,
/Thomas

Rune Skovbo Johansen said...

@thpetsen: In 2.6 you could access member properties from other threads and sometimes it would seem to work fine and other times it would cause a crash, and there would be no consistency of when one or the other would happen. So for 3.0 all access from other threads which might cause a crash was prevented. Even though it makes things seem more difficult, it really is a good thing.