Question about how to achieving high cohesion / loose coupling

What if you were attempting to create a GameOver script that resets the majority of the script upon game finish/over? It will communicate with numerous scripts and objects. Reset script elements like Health, Player Controller, Stats, UI, Remove Enemies, Weapon, and others (like in the example above).

This script will have tight coupling and low cohesion. Do you have any ideas on how to design such a function? I’m also curious about how a professional will write them.

There are about 50+ ways to do everything. Which way will be a combination based upon the circumstances, tools available, policies of the company and a random number generator.

What gets you in more problems is looking for a best solution that fits everything. It doesn’t exist.

Very quickly off-the-cuff, I personally would make an interface called IResetable with a single Reset() method. Implement this on everything that needs to be reset and then, in the GameOver script find all the scripts that implement the interface and call Reset() on each one

public interface IResetable
{
    void Reset();
}

public class GameOver : MonoBehaviour
{
    public void ResetGame()
    {
        foreach(var resetable in FindObjectsOfType<IResetable>())
        {
            resetable.Reset();
        }
    }
}

But, like @MichaelP says, there are many, many ways to do something like this. My quick example may not be sufficient on large games.

In fact, this works great in smaller games. However, since I’m only creating a small game, this is a fantastic idea that I can implement. I sincerely appreciate it.

You are correct, yet there are situations when a game needs the GameOver method or alternative solutions. Suppose you are developing a 2D platform with endless map. Every stat is reset to start stat if you win or die. How would you handle that situation. Do you have any example? Anyway, thanks for the reply.

Load the scene again. This seems like the simplest way to reset everything.

This sort of situation is one of those instances where I would likely break one rule to make another easier to implement…

In my GameOver script, I would have the following:

public static event System.Action OnGameOverReset;

and when it’s time to reset all of the scripts, call

OnGameOverReset?.Invoke();

any script that can be reset simply adds it’s reset method to the event

GameOver.OnGameOverReset+=MyResetMethod;

Because the method is static, there is no searching for a Singleton, no Singleton setup at all, actually… You could technically argue that the event list is global state, but I find this one to be fairly harmless.

Alternatively, I would judge @bixarrio’s solution to be an excellent solution.

I have tried this, but it throws an error. It seems like unity cannot use FindObjectsOfType<>() to look for interface. I found on web that they look for monobehaviour instead. Store the monobehaviours in an array then use foreach loop to search for interface. Wouldn’t that be performance costly?

Unity is often unfriendly to Interface.

Sorry about that. As far as I remember FindObjectOfType can’t do interfaces but GetComponents can and so I get them mixed up.

Since the reset won’t happen very often you could use Linq

foreach(var resetable in FindObjectsOfType<MonoBehaviour>().OfType<IResetable>())
{
    resetable.Reset();
}

It gets all the MonoBehaviours and then filters them on the interface. May be a little expensive but like I said, it doesn’t happen every frame so it should be fine.

That’s pretty much what’s happening in the code above. Yes, it may be performance heavy (especially in a scene with lots and lots of components) but it doesn’t happen often so it shouldn’t be an issue. The - probably better - alternative is to use the events @Brian_Trotter mentioned. With that, only the components that care (which would be the same ones you would’ve implemented the interface on) will respond and there’s no need to look at any components that have nothing to do with resetting.


If you insist on using the code above and it does cause a noticeable drop in frames you could run a coroutine that would fade to black, run the resets over multiple frames and then fade in again. Something like

private IEnumerator ResetSceneRoutine()
{
    yield return FadeToBlack(); // Assumption that we have a Coroutine to fade

    var resetCount = 0; // count resets so we don't yield after every single reset
    var maxResetsPerFrame = 10; // Magic number - should be configurable
    foreach(var resetable in FindObjectsOfType<MonoBehaviour>().OfType<IResetable>())
    {
        resetable.Reset();
        resetCount++;
        if (resetCount == maxResetsPerFrame)
        {
            yield return null;
            resetCount = 0;
        }
    }

    yield return FadeIn();      // Assumption that we have a Coroutine to fade
}

Again, I don’t know if this will work. I write code in-post all the time and make mistakes

That’s a really bad habit, @bixarrio. I should know, I do it all the time. :stuck_out_tongue:

That’s correct, and I forgot about that as well when I read your solution.

I think if I were to implement it with a FindObjectsOfType solution, I’d mirror something closer to the RPG ISaveable Model…

Each GameObject that would have things that can be reset would have a ResetableEntity : MonoBehaviour, and this is what the Reset would search for.

Then the ResetableEntity would run a foreach on the GetComponents of ResetableEntity.

This would be faster than the Linq expression, but still quite a bit slower than the Observer Pattern method.

1 Like

Yup, I also thought of applying a fade while performing the trick, but it would require a lot of scripting to do so. I have tried @Brian Trotter’s solution and it works perfectly fine and requires less coding. I appreciate all the responses.They have helped me learn more. :grin:

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms