Best Practices: How to handle inspector's lack of interface support?

Hey guys,

I’m struggling with the “best” way to handle simple communication between two of my objects, given the fact that the Unity inspector doesn’t really support interfaces. Here are my three pieces (simplified):

  1. SceneManager class that can actually load designs into the scene.
  2. FileDialog class that will allow the user to choose a file to load.
  3. DesignLoader interface, which SceneManager implements, and which FileDialog uses to notify when a file has been chosen by the user.

Here are the definitions in their simplest form (note that SceneManager is actually in a separate file):

public class SceneManager : MonoBehaviour, DesignLoader
{
	public void LoadDesign(string path)
	{
		// Load a design from the specified path
	}
}

public class FileDialog : MonoBehaviour
{
	public DesignLoader m_designLoader;
	
	void OnFileChosen(string path)
	{
		m_designLoader.LoadDesign(path);
	}
}

public interface DesignLoader
{
	void LoadDesign(string path);
}

Pretty standard stuff. The interface acts as an abstraction layer, allowing the FileDialog class to be used in any environment, without knowing anything about the specifics of the class that will actually load the design.

Now the crux: what’s the best way to assign the “m_designLoader” member? Since it’s an interface, it won’t show up in the Unity inspector. I’ve created a custom inspector (as in this post: http://forum.unity3d.com/threads/49524-Exposing-fields-with-Interface-type-(C-)-SOLVED); however, even when this is set up, Unity won’t let me drag the GameObject that has SceneManager as a component onto the inspector item. I’m guessing this is because Unity only sees the first class in a given script (in this case, “Scene Manager”), and ignores all of the interfaces after that.

Rejected Solutions

I’ve considered and rejected the following workarounds:

  • SendMessage/BroadcastMessage: while I could send a message to all game objects when a file is picked (or, even worse, search for the specific game object and send it alone a message), this feels sloppy. It also would be less performant, although I do acknowledge that performance is secondary in this type of situation.
  • Move the file loading into FileDialog: this won’t work in my specific case, as the scene manager is in charge of file loading (including from other sources, such as e-mail attachments). Besides, I really want FileDialog to do one thing, and one thing only: allow the user to quickly and easily choose a file. I want FileDialog to be agnostic about what its consumer actually chooses to do with this file.
  • Add code to SceneManager::Start() to walk the GameObject tree and hunt down FileDialog. This would work, but it’s not performant, and saddles the SceneManager with work (and extra clutter) that should be done at compile time. Also it adds more complexity to other users of FileDialog, should any ever exist.
  • Aggregation: while SceneManager could own a FileDialog object, this breaks the basic model/view separation.
  • Inheritance: one solution (proposed by Muuskii) would be to make FileLoader an abstract class, and have SceneManager derive from it. The only problem with this approach is that it ties the SceneManager into only being able to support a single “interface” behavior, whereas ideally you would be able to have a class implement multiple interfaces. If I went with this approach, I would have the same problem the next time I needed to add another behavior of this nature. Another way to think of it is this: with the inheritance solution, the SceneManager becomes a DesignLoader, with some extra things tacked on. In reality, though, the SceneManager manages my scene, and loading a design is just one of the many things it can do.

If I can’t come up with anything else, I will go ahead and use one of the rejected solutions, while shedding a single, small tear.

Any and all suggestions are welcome. Also it would be great if anyone has specific knowledge about Unity 4 implementing full support for interfaces.

Thanks for your help, guys!

-Kevin

Sounds like you should be passing a delegate to a singleton instance of FileDialog to me, then you would call the delegate with the chosen file.

public class FileDialog : MonoBehaviour
{
    public static FileDialog Instance;

    void Awake()
    {
      Instance = this; // or some cleverer code to ensure that "there can be only one"
    } 

    Action<string> onChosen = delegate {};
    public void ShowDialog(Action<string> whenChosen)
    {
       onChosen = whenChosen;
       //Show the dialog
    }

    void OnFileChosen(string path)
    {
       onChosen(path);
       onChosen = delegate {};
    }
}