Getting message from dialogue trigger to dialogue manager

I am currently writing a dialogue system for our game. I am still learning Unity, our game up until this point has been written in the old GameMaker Studio 1 and the project has definitely outgrown that engine. I would like this system to be as generic as possible so it could possibly be reused in a different project.

The problem: I am currently struggling to think of an elegant way to send messages from my Usable components to the DialogueSystemController which lives on a Dialogue Manager game object in the scene.

The easy solution would be a Singleton class with a public method called StartConversation or something similar, but having read the criticisms of that pattern, I would like to avoid those. Currently, the DialogueSystemController uses FindObjectsOfType() on Start to get a list of all usables in the scene and stores them in a list. Then, it iterates through that list and subscribes to each Usable’s UsableUsed event.

public class DialogueSystemController : MonoBehaviour
{
    private List<Usable> usables;

    private void Start()
    {
        usables = new List<Usable>();
        Usable[] usablesInScene = FindObjectsOfType<Usable>();
        foreach (Usable usable in usablesInScene)
        {
            usables.Add(usable);
            usable.UsableUsed += OnUsableUsed;
        }
    }
 
    // [irrelevant code omitted]
}

public class Usable : MonoBehaviour
{
    [SerializeField]
    private Conversation initialConversation = null;

    public event EventHandler<UsableUsedArgs> UsableUsed;

    [ContextMenu("Use Usable")]
    public void OnUse()
    {
        Debug.Log($"Usable {name} was used.");
        UsableUsedArgs args = new UsableUsedArgs();
        args.Conversation = initialConversation;
        UsableUsed?.Invoke(this, args);
    }
}

This works, but it kind of feels like a hack. And I can imagine the number of references I have to keep track of and/or pass around will only grow as I add more components/features to the system. And also, eventually I would like to write a component that will expose some UnityEvents relating to the dialogue system so outside systems can hook into it.

I have considered [this] solution by Game Dev Guide. I would make an events class purely for “internal” use by the dialogue system, but this would also use a Singleton.

Any thoughts on whether my current solution would continue to work? Are singletons okay to use as long as they are only for internal use of a separate system?

The system you have right now has most of the drawbacks of the Singleton, plus the costly expense of calling FindObjectsOfType, plus the extra problem that you are only getting Usables in Start(). What happens if new Usables are added to the scene later? Wouldn’t it be easier if the Usables could all register themselves with the Dialogue system when they are enabled?

My suggestion is to just use the tried & true singleton pattern.

Another alternative would be to keep a static list defined in the Usables class that Usables add themselves to and remove themselves from when enabled/disabled. FindObjectsOfType is too slow to call repeatedly and doesn’t handle new/removed objects well.

1 Like

You make a good point! I am making the assumption that Usables are only added outside runtime in the editor, which felt reasonable until I just thought about it. And also, Usables that are disabled at start won’t be actually be returned by FindObjectsOfType, will they.

Your solution does sound cleaner and it doesn’t introduce any more coupling than my solution already has, it just inverts the responsibility of registering the usables, correct? Perhaps I’m simply trying too hard to avoid singletons, given the poor reputation they seem to get, haha.