What's the proper way to queue and space function calls?

I do a bunch of function calls to display stuff, but I would like to be able to make it so that all of them don’t display at the same time but:

  • That there is a spacing between subsequent displays
  • That I can put something in before each call (like a throbber)

What would be a good way to do this in Unity? Something with Coroutines might work, but those are fairly annoying to call with multiple parameters, right?

Update:
To be clearer, how would this work for a sequence of calls such as:

chat.addMessage("Bla");
chat.addMessage("Blabla");

I don’t want to have to space stuff out on this side, so keep the interface to chat as simple as it is now. How would I lay out the chat object internally so that it doesn’t display both messages directly after each other but puts them in a timed-release queue?

If you need a coroutine queue you can use something like this:

using UnityEngine;
using System.Collections.Generic;

public class CoroutineQueue
{
    MonoBehaviour m_Owner = null;
    Coroutine m_InternalCoroutine = null;
    Queue<IEnumerator> actions = new Queue<IEnumerator>();
    public CoroutineQueue(MonoBehaviour aCoroutineOwner)
    {
        m_Owner = aCoroutineOwner;
    }
    public void StartLoop()
    {
        m_InternalCoroutine = m_Owner.StartCoroutine(Process());
    }
    public void StopLoop()
    {
        m_Owner.StopCoroutine(m_InternalCoroutine);
        m_InternalCoroutine = null;
    }
    public void EnqueueAction(IEnumerator aAction)
    {
        actions.Enqueue(aAction);
    }

    private IEnumerator Process()
    {
        while (true)
        {
            if (actions.Count > 0)
                yield return m_Owner.StartCoroutine(actions.Dequeue());
            else
                yield return null;
        }
    }
}

This helper will run a coroutine which simply “idles” if no actions are in the queue. As soon as a coroutine gets enqueued it’s started on the owner MonoBehaviour and once it’s finished it will grab the next queued item. I deliberately wanted to keed it simple, so i did not add any waiting for each element. To wait in between two coroutines you can simply queue another coroutine which does nothing but waiting.

You could add those two methods to the class above:

public void EnqueueWait(float aWaitTime)
{
    actions.Enqueue(Wait(aWaitTime));
}

private IEnumerator Wait(float aWaitTime)
{
    yield return new WaitForSeconds(aWaitTime);
}

When you call EnqueueWait you simply add another item to the queue which does just wait for the specified time.

Example:

private CoroutineQueue queue;

void Start()
{
    // use "this" monobehaviour as the coroutine owner
    queue = new CoroutineQueue( this );
    queue.StartLoop();
}

// [...]
queue.EnqueueAction(YourCoroutine(blubb, blah));
queue.EnqueueWait(2f);
queue.EnqueueAction(YourSecondCoroutine(blah, blubb));
queue.EnqueueWait(2f);

In this case when the 4 Enqueue calls are made your first coroutine will start the next frame. When it’s finished the internal “Wait” coroutine will run for 2 seconds and then your second coroutine will be started. Finally another 2 sec Wait is started once the second coroutine has finised. If you enqueue something while the Wait (or any other coroutine) is still executing it will be executed once everything before that item has finished.

You can create more simple utility coroutines which you can enqueue in the order you like. You can create a coroutine that waits for some input or for an object reaching a certain point.

I’ve made the CoroutineHelper class on the wiki which has a different aim in mind. It doesn’t support queueing but has some other useful concepts.

Coroutines are actually pretty easy to call with parameters and would do what you are looking for I think. Here’s an example of how they work:

void Start()
{
    StartCoroutine(DelayedFunction(2f, "Foo"));
}

public IEnumerator DelayedFunction(float delayTime, string param1)
{
    yield return new WaitForSeconds(delayTime);
    Debug.Log(param1);
}

EDIT:

A Coroutine queue system is a bit more complicated and requires your own implementation. You have to account for the delay and add to the queue manually, although it’s not overly complex. Here’s what it might look like:

List<Message> messages;
public Button button;

void Start()
{
    messages = new List<Message>();
    button.onClick.AddListener(() => AddMessage("Foo", 2f));
}

public void AddMessage(string param1, float delayTime = 2f)
{
    messages.Add(new Message { param = param1, time = delayTime });
    if (messages.Count == 1) StartCoroutine(NextMessage());
}

public IEnumerator NextMessage()
{
    yield return new WaitForSeconds(messages[0].time);
    Debug.Log(messages[0].param);
    messages.RemoveAt(0);
    if (messages.Count != 0) StartCoroutine(NextMessage());
}

struct Message
{
    public string param;
    public float time;
}

The top answer is way too complicated in my opinion; it is much simple to hold a variable and only free it after each corroutine; making it work just like a queue.

public static class ExampleClass{
    static bool onProgress = false;
    public static IEnumerator ExampleMethod(float argument){
         while (onProgress) {
             yield return null;
         }
         onProgress = true;
         // Change next couple of line for your actual yield statements (e.g an animation)  
         yield new WaitForSeconds(argument);
         // Any further logic to may be added right here (e.g. an extra wait)
         onProgress = false;
    }
}

Of course you would call it like this:

StartCoroutine(ExampleClass.ExampleMethod(1f));

Coroutines can do this easily. Don’t use multiple parameters if you find them annoying.