How can I simultaneously invoke all instances of a function without it being a static function?
You can’t invoke “all instance methods”. The memory addresses of those functions are not known until runtime, so the CLR wouldn’t know what to invoke where. The whole reason that you can link against a static function is that its memory address is known when the compiler generates the code.
With that said, you can use events and delegation to create a broadcasting class that all your objects subscribe to. When the broadcaster invokes its event, all the listener get the message, which sounds exactly like what you want to do. Here’s an example:
- Create an empty scene.
- Add one gameobject called “BroadcastObject”.
- Add a few listener objects, their name doesn’t matter.
Attach the following script to the BroadcastObject:
using UnityEngine;
using System;
public class Broadcaster : MonoBehaviour
{
// We use this to broadcast
public event BroadcastHandler Tick;
// These are the arguments that are broadcast (in our case they're empty)
public EventArgs e = null;
// Instantiate the EventHandler
public delegate void BroadcastHandler(Broadcaster b, EventArgs e);
// Let's broadcast a message
void OnGUI()
{
if (GUI.Button(new Rect(20, 20, 110, 20), "Call all listeners"))
{
// Check if we have subscribers
if (Tick != null)
{
Tick(this, e);
}
}
}
}
Attach this script to the Listeners:
using UnityEngine;
using System;
public class Listener : MonoBehaviour
{
void Start ()
{
// Find the broadcaster's GameObject and retrieve the script
GameObject obj = GameObject.Find ("BroadcastObject");
if (obj != null)
{
Broadcaster b = obj.GetComponent<Broadcaster>();
if (b != null)
{
// Subscribe to the broadcaster. Wire up the MessageReceived
// function to be called when a message is received.
// (Use -= to unsubscribe later, if desired)
b.Tick += new Broadcaster.BroadcastHandler(MessageReceived);
}
}
}
private void MessageReceived(Broadcaster b, EventArgs e)
{
// Invoke your actual method here
Debug.Log(this.ToString() + "Message received!");
}
}
That should do it. When you press the GUI button, all listeners subscribed to the broadcaster receive the event. In this case, all write “Message received!” to the console. If you want to send actual arguments to each listener, create a custom EventArgs class that carries those parameters. A use case might be an event-driven NPC scheduler for an RPG game. Rather than having each NPC check the time every frame, you instead have them subscribe to the time-of-day broadcaster, which sends an event whenever the the time has meaningfully changed (“Time to go to bed!”) via the EventArgs.
The standard event pattern in C# can take a while to grok. Try it out in an empty project first, then adapt it for your project step by step. I can post the example project if needed.
There is one way though. It’s a pattern I call the InstanceList
pattern.
I’ve written about the military pattern which is a special use case for the InstanceList
pattern. It enables all instances to be called through one static function.
The basic idea is to have a list of all instances, then through a static function; call a method on all instances.
Here is an example:
public abstract class Fireable : IDisposable {
public static void Fire() {
foreach (var fireable in instanceList) {
fireable.OnFire();
}
}
public void Dispose() { instanceList.Remove(this); }
protected abstract void OnFire();
protected Fireable() { instanceList.Add(this); }
private static List<Fireable> instanceList = new List<Fireable>();
}
public class Gunner : Fireable {
public Gunner(string name) { Name = name; }
protected override void OnFire() { Console.WriteLine(" Gunner "+Name+" firing..."); }
public string Name { get; set; }
}
Given:
Gunner g1 = new Gunner("Rambo"),
g2 = new Gunner("James"),
g3 = new Gunner("Lara"),
g4 = new Gunner("Robocop");
Then:
Fireable.Fire();
Would cause:
Gunner Rambo firing...
Gunner James firing...
Gunner Lara firing...
Gunner Robocop firing...
I have made the InstanceList pattern available for download through NuGet InstanceListPattern