2016-11-29

A UI thread dispatcher for Unity3D

I've recently been working on implementing an IHttpService that can work outside of a MonoBehaviour and call out to web services asynchronously.

Rather than providing my IHttpService.JsonPost method with callbacks I decided to have it return a Promise, the code for which I took from this Real Serious Games GitHub repository.

The problem is that when you use WebClient's async methods they call back the Complete events on the worker thread, so code like this won't work because you cannot manipulate UI from any thread other than the main one.

httpService.JsonPost<MyInfo>(url)
  .Then(myInfo => someTextUiObject.text = myInfo.Name);
And there seems to be no kind of thread Dispatcher in Unity3D for UI updates as there is in Windows.Forms - so I wrote my own.

using System.Collections;
using System;
using System.Threading;
using System.Collections.Generic;
using UnityEngine;
public class UiThreadDispatcher : Singleton<MonoBehaviour>
{
    static volatile int lockValue = 0;
    static Queue<Action> actionQueue = new Queue<Action>();
    void Awake()
    {
        StartCoroutine(CheckForDispatchedActions());
    }
    public static void Dispatch(Action action)
    {
        Lock();
        actionQueue.Enqueue(action);
        Unlock();
    }
    static void Lock()
    {
        while (Interlocked.Exchange(ref lockValue, 1) != 0) { }
    }
    static void Unlock()
    {
        lockValue = 0;
    }
    private IEnumerator CheckForDispatchedActions()
    {
        while (true)
        {
            Action action = null;
            Lock();
            try
            {
                while (actionQueue.Count > 0)
                {
                    action = actionQueue.Dequeue();
                    action();
                }
            }
            catch (Exception unexpectedException)
            {
                Debug.LogException(unexpectedException);
            }
            finally
            {
                Unlock();
            }
            yield return null;
        }
    }
}

The idea is that any piece of code can call the static Dispatch method, for example

//Instead of this
httpService.JsonPost<MyInfo>(url)
  .Then(myInfo => someTextUiObject.text = myInfo.Name);
//You would do this
httpService.JsonPost<MyInfo>(url)
  .Then(myInfo => UiThreadDispatcher.Dispatch(() => > someTextUiObject.text = myInfo.Name));
The Queue object needs to be locked to Enqueue/Dequeue items, but I use a simple SpinLock pattern because clashes will be very rare. Then I just have the MonoBehaviour instance execute the queued actions.

*Note to self: Must test on iOS.