Let’s say some component called A needs to use a function Foo in some other component, B.
More specifically, say A is used in a number of different objects, and Foo can be implemented differently in the other component. In other words, multiple objects use A, but the component that implements Foo is different across those objects.
I’ve read that the best way to deal with this dependency is essentially this:
// A.cs
public class A : MonoBehaviour
{
public ISomeInterface SomeInterface { get; set; }
void Bar()
{
SomeInterface.Foo();
}
// other stuff A does
}
// ISomeInterface.cs
public interface ISomeInterface
{
void Foo();
}
// B.cs
public class B : MonoBehaviour, ISomeInterface
{
public void Foo()
{
// do something
}
// other stuff B does
}
But that’s where all the tutorials end. I don’t understand where you’re supposed to assign B as A’s SomeInterface.
Do you make a “setup” script for each object and resolve the dependencies in the Start() function?
That’s a good question, specially in Unity, where the entry point of your application is not clearly identified (it’s the scene itself BTW).
I would create a GameObject with a, let’s call it, GameManager script attached to it as a component. This GameManager instance is the one responsible for all the wiring you are talking about. You can make it singleton if you want, to avoid having more than one instance of it. I don’t consider that a good practice, though. If you want only one instance, be careful and create just one.
Then in the Awake() callback of GameManager I would either:
Find the game objects containing the A instances components (You can use GameObject.Find() for it or, if they are more than one, asign a tag to them in the inspector and use GameObject.FindGameObjectsWithTag())
Instantiate GameObjects from prefabs containing the A class and the class implementing ISomeInterface.
Either way, you get references to the participating objects and now you can asign things properly. Here is an example of the GameManager class:
using UnityEngine;
using System.Collections;
public class GameManager : MonoBehaviour
{
void Awake ()
{
// Asume the GO containing the instance implementing ISomeInterface is named 'FooContainer'
GameObject fooContainer = GameObject.Find("FooContainer");
// This part is a little tricky, the generics version of GetComponent() doesn't work here (see later)
ISomeInterface fooFighter = fooContainer.GetComponent(typeof(ISomeInterface)) as ISomeInterface;
// Asume GOs containing the A script have 'Atag' asigned in the inspector
GameObject[] AContainers = GameObject.FindGameObjectsWithTag ("Atag");
foreach(GameObject AContainer in AContainers)
{
A Ainstance = AContainer.GetComponent<A>();
if (Ainstance != null)
{
Ainstance.SomeInterface = fooFighter;
}
}
}
}
Unity doesn’t make easy to work with MonoBehaviours implementing your own interfaces, but this works.
The only concern is that the code used to get the instance of the class implementing the interface feels like a hack to me.
I did post a question about it here which hopefully will get answered by someone more experienced than myself.