Creating An Event Driven System for Ordering Actors in Turn-Based Tactical Play with Unity 3D and C#
Here’s the objective: using Unity 3D and C# we want to build a system that can alert any number of actors to roll a random number, and report back their name and initiative number to be stored in a dictionary as key/value pairs.
For the first part of this, we’ll be using the Func<TResult> Delegate. This is a kind of delegate that can accept any function that returns the type designated by TResult. In this case, we want a delegate method that will return an integer value — the actor’s initiative roll.
The delegate declaration looks like this:
When we signal that the round has started, any function subscribed to this delegate will return an integer. Let’s get some subscribers:
Here I have an abstract Actor class that my various actor types can inherit. Since each kind of actor has a different means of calculating their initiative, I’ll subscribe an abstract RollInitiative() int function to the OnRoundStart delegate. Now whatever way they use to determine the return value will be up to them. Here’s one example:
Next we’ll declare the dictionary:
There are two problems from here.
First problem: we need more than an integer returned to us. We’re putting this information into a Dictionary, which requires two types of data — a GameObject (the actor), and an integer (the actor’s initiative score).
The other problem: our Func delegate is multicast, meaning there are multiple subscribers. If we attempt to store the return value from OnRoundStart we will only get the value for the last subscriber on the invocation list. Obviously we want to store the value for every subscriber, so a simple int roll = OnRoundStart?.Invoke() will not work.
We can solve both problems in one foreach loop. The first problem will be solved by using the Delegate.Target property. If we cast the Target as an Actor, we can store the game object the Actor instance is attached to in a variable and use it as the key half of our dictionary key/value pair.
To solve the second problem, we iterate the foreach loop using the Delegate.GetInvocationList() method. This method keeps a list of all subscribers to the delegate. We Invoke() each subscriber in turn, and store the returned integer into a variable to use as the value half of the dictionary key/value pair.
Here’s the C# method to make it all work:
And from there we get the following output:
The first four entries come from the individual Actor’s RollInitiative() method, and the last four are the details of what is stored in the initiative dictionary. A perfect match.
In the next article we’ll put the dictionary to work by loading each key/value pair in it into a LinkedList<T>() which we can traverse and reorder according to the turn progression of the game and the initiative cost of the actors’ actions.