- Home /
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):
SceneManager class that can actually load designs into the scene.
FileDialog class that will allow the user to choose a file to load.
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
So; FileDialog : $$anonymous$$onoBehaviour is NOT in it's own file called FileDialog?
Are you trying to assign an actual instance of your class to the reference, or just dragging it from the project pane?
Good questions!
FileDialog is in its own file (called FileDialog.cs), and I currently have the FileLoader interface in there too. Scene$$anonymous$$anager has its own file.
There is a GameObject in the scene with Scene$$anonymous$$anager attached, and a separate GameObject with FileDialog attached. I'm trying to drag the Scene$$anonymous$$anager GameObject (from my scene hierarchy) into the FileLoader field in the FileDialog's inspector view.
I've also tried putting the "FileLoader" interface into its own file, but this doesn't solve the problem.
I'm running into an issue while trying to reproduce your project: You say that the inspector is actually showing a field that you can drag into? I'm not seeing such a field, just and empty Inspector.
I may be skim$$anonymous$$g past it but I don't see where the solution is in that link that may allow me to see the field.
Yeah, I wouldn't think moving the Interface to it's own file would make a difference. I typically lump my Interfaces into the same file as the "main" class that implements it and it turns out fine.
I want to throw this solution up here really quick just because it wasn't on your list of rejected ideas and I'm not co$$anonymous$$g to any immediate conclusion the other way:
public class Scene$$anonymous$$anager : DesignLoader
{
public override void LoadDesign(string path)
{
// Load a design from the specified path
}
}
public abstract class DesignLoader : $$anonymous$$onoBehaviour
{
abstract public void LoadDesign(string path);
}
Once I did that, I could drag Scene$$anonymous$$anager onto File Dialog no problem. I would really like to find a solution that involves Interfaces since you seem to know what you're doing and to be dead set on using them. I just wanted to suggest this and see if it works for your design.
Answer by whydoidoit · Oct 11, 2012 at 06:14 PM
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 {};
}
}
Other choices include a static event that is fired on chosing the file - I prefer the delegate approach as it's cleaner and won't lead to memory allocation leaks.
void DoSomethingWithAFileInDesignLoader()
{
FileDialog.Instance.ShowDialog(LoadDesign); //Or a lambda
}
One FileDialog to guide them all?
This singleton/delegate approach is great! Thanks for your help.
Your answer
![](https://koobas.hobune.stream/wayback/20220613082120im_/https://answers.unity.com/themes/thub/images/avi.jpg)