1
\$\begingroup\$

I've been trying to build a "event system" for a project that I'm working on . Here is how I'm doing it : I populate a list with reference to a gameObject and the functions that I need to execute from that gameObject . Then , when the "event" is triggered (in this case , when the player steps into a trigger collider) I just loop over the list and Invoke the functions inside it.

The problem with this is that every single function inside the list gets executed at the same time. This works fine in some cases but if I want to create a more cinematic event I need to have the ability to execute a function after the previous one has finished it's execution .Sadly I have no idea how to do that.

I've been reading a lot of the documentation of both Unity and C# about coroutines and delegates but I can't seem to wrap my head around all those things and find a way to do implement them on code .So I need your help with that :How can I achieve this?

TL;Dr version : I need your help in coming up with a way to execute a function after the previous function has ended it's execution .These functions would be stored in an array or list.

Here is the code of the trigger of the event itself

public class ActivateEvent : MonoBehaviour {

private bool activated;
public Signal[] actionList;
[HideInInspector]
public int actionEventAmount;

// Use this for initialization
void Start()
{
    activated = false;

}
void OnTriggerEnter2D(Collider2D other)
{
    if (other.gameObject.tag.Equals("Player") && !activated)
    {
        Activate();
    }
}

public void Activate()
{
    activated = true;
    for(int i=0;i<actionEventAmount;i++)
    {
        actionList[i].Invoke();
    }
}

}

and here is the code for the Signal Class

public class Signal
{
   public GameObject target;
   public string method;
   public string argType;

   public Signal()
   {

   }
   public Signal(System.Type argType)
   {
       this.argType = argType.FullName;
   }



 public void Invoke()
    {
        if (target != null && !string.IsNullOrEmpty(method))
            target.SendMessage(method, SendMessageOptions.RequireReceiver);
    }
    public void Invoke(object value)
    {
        if (argType != null)
        {
            if (target != null && !string.IsNullOrEmpty(method))
            {
                if (argType.Equals(value.GetType().FullName))
                    target.SendMessage(method, value, SendMessageOptions.RequireReceiver);
                else
                    Debug.LogError("Incorrect parameter type, expected [" + argType + "], got [" + value.GetType().FullName + "].");
            }
        }
        else
            Invoke();
    }
}

public class SignalAttribute : System.Attribute
{
    public string name;

    public SignalAttribute()
    {

    }
    public SignalAttribute(string name)
    {
        this.name = name;
    }
}
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

Here is a simple approach:

public class Manager
{
    private Worker[] workerQueue; //The array that holds the queue
    private int indexHolder;      //The integer that holds the index of most recent executed worker

    private void ExecuteNext()
    {
        workerQueue[++indexHolder].Execute(this); //Executes the next worker in queue.
    }

    public void OnExecutionFinished()
    {
        ExecuteNext();
    }
}

public class Worker
{
    private Manager _manager;

    public void Execute(Manager manager)
    {
        _manager = manager;

        Work();
    }

    private void Work()
    {
        //Do what you want to do here.

        //After your job is done, call this:
        NotifyExecutionFinished();
    }

    private void NotifyExecutionFinished()
    {
        _manager.OnExecutionFinished();
    }
}

Edit:

A better version which allows you to assign jobs to workers on creation:

using System;
using System.Collections.Generic;

public class Manager
{
    private List<Worker> workerQueue; //The list that holds the queue
    private int indexHolder;          //The integer that holds the index of most recent executed worker

    public void CreateWorker(Action job)
    {
        Worker newWorker = Worker(job);
        workerQueue.Add(newWorker);
    }

    public void StartExecution()
    {
        indexHolder = 0;
        workerQueue[0].Execute(this);
    }

    public void ClearQueue()
    {
        workerQueue.Clear();
    }

    private void ExecuteNext()
    {
        workerQueue[++indexHolder].Execute(this); //Executes the next worker in queue.
    }

    public void OnExecutionFinished()
    {
        ExecuteNext();
    }
}

public class Worker
{
    private Manager _manager;
    private Action _job;

    public Worker(Action job)
    {
        _job = job;
    }

    public void Execute(Manager manager)
    {
        _manager = manager;
        Work();
    }

    private void Work()
    {
        _job();                     //Do the job first.
        NotifyExecutionFinished();  //Then inform the manager.
    }

    private void NotifyExecutionFinished()
    {
        _manager.OnExecutionFinished();
    }
}

You can add workers like this:

    managerInstance.CreateWorker(() => 
    {
        //The job you want for this worker.
    });
\$\endgroup\$
5
  • \$\begingroup\$ Wow , thanks !!! I will try this in a few hours (I'm in class right now) . It seems that is going to work in a painless way .Thank you so much !!!! \$\endgroup\$ Mar 4, 2016 at 20:00
  • \$\begingroup\$ @FranciscoM. You are welcome! \$\endgroup\$ Mar 4, 2016 at 20:14
  • \$\begingroup\$ where i should add: managerInstance.CreateWorker(() => { //The job you want for this worker. }); ? \$\endgroup\$
    – Pawel
    Aug 2, 2019 at 10:52
  • \$\begingroup\$ @Pawel Wherever you need to create a worker. \$\endgroup\$ Aug 2, 2019 at 11:56
  • 1
    \$\begingroup\$ @FranciscoM. if the answer solves your problem, don't forget to mark it as accepted \$\endgroup\$ Aug 2, 2019 at 12:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .