How to deal with large amounts of cacheable data for components-disabler

Hi! I need some advice from experienced developers on code design.
My game has a number of game mechanics that I want to disable under different conditions. For example: player is in a dialog, opened inventory or is playing a some mini-game.
I decided to create a class-disabler (I access it through the GameManager singleton) which has two methods - one takes bool as arguments for those mechanics that I want to disable, the second one - resets.

Here’s the code below, which I wrote (I think it’s terrible)

using UnityEngine;

public class ComponentsDisabler : MonoBehaviour
{
    [SerializeField] private ProximitySelector selector;
    private bool resetSelector;
    [SerializeField] private CapsuleCollider2D marcusCollider;
    private bool resetPlayerCollider;
    [SerializeField] private CursorManager cursorManager;
    private bool resetCursorManager;
    [SerializeField] private PauseController pause;
    private bool resetPause;
    [SerializeField] private PlayerController controller;
    private bool resetController;
    [SerializeField] private DTRManager dTRManager;
    private bool resetDTRManager;

    public void DisableComponents(bool playerSelector, bool playerCollider, bool hideCursor, bool pauseController, bool playerController, bool dtr)
    {
        if (playerSelector) { selector.enabled = false; resetSelector = playerSelector; }
        if (playerCollider) { marcusCollider.enabled = false; resetPlayerCollider = playerCollider; }
        if (hideCursor) { cursorManager.SetActiveCursorType(CursorManager.CursorType.Empty); resetCursorManager = hideCursor; }
        if (pauseController) { pause.enabled = false; resetPause = pauseController; }
        if (playerController) { controller.enabled = false; resetController = playerController; }
        if (dtr) { dTRManager.enabled = false; resetDTRManager = dtr; }
    }

    public void EnableComponents()
    {
        if (resetSelector) { selector.enabled = true; resetSelector = false; }
        if (resetPlayerCollider) { marcusCollider.enabled = true; resetSelector = false; }
        if (resetCursorManager) { cursorManager.SetActiveCursorType(CursorManager.CursorType.Arrow); resetCursorManager = false; }
        if (resetPause) { pause.enabled = true; resetPause = false; }
        if (resetController) { controller.enabled = true; resetController = false; }
        if (resetDTRManager) { dTRManager.enabled = true; resetDTRManager = false; }
    }
}

as you can see I cache data about which mechanics I have disabled in additional reset bools, and then use those bools to restore the values
I don’t like this code for 2 reasons:

  1. It’s not “elegant”.
  2. It’s unlikely, but it could be that the disabler will call 2 different methods. And when it will be necessary to restore game state - EnableComponents() will enable only those mechanics, which were disabled by last call.

I personally see 2 solutions

  1. Use dictionaries, or create a nested class for Enabler. I haven’t used dictionaries and am afraid of pitfalls, and the nested class will only make the code even larger. But I’d really like to pass all received by it arguments at the end of DisableComponents() method without additional reset bool.
  2. The simplest solution is to enable all components regardless of conditions in the EnableComponent() method. This solution fits my project, but it doesn’t seem the most flexible.

And the mini question: for example in class PlayerController in method OnDisable() I set rigitbody.velocity = 0; So I just need to disable PlayerController instance and don’t need to create a separate method. Is this a good solution? I’m afraid of using OnDisable()
Thanks!

This looks like a case for a super rudimentary state machine

using UnityEngine;

public class GameStateController : MonoBehaviour
{
    [SerializeField] ProximitySelector selector;
    [SerializeField] CapsuleCollider2D marcusCollider;
    [SerializeField] CursorManager cursorManager;
    [SerializeField] PauseController pause;
    [SerializeField] PlayerController controller;
    [SerializeField] DTRManager dTRManager;

	EState _state = EState.undefined;

	public void Trigger ( EState newState )
	{
		switch( newState )
		{
			case EState.undefined:		throw new System.NotImplementedException($"{newState} should never be called");
			case EState.GameMenu:		OnGameMenu(); break;
			case EState.Gameplay:		OnGameplay(); break;
			case EState.DialogScene:	OnDialogScene(); break;
			case EState.DeathScreen:	OnDeathScreen(); break;
			default:					throw new System.NotImplementedException($"{newState} is not implemented here");
		}
		_state = newState;
	}

	void OnGameMenu ()
	{
		/* code that enables some components, disables other ones*/
	}
	void OnGameplay ()
	{
		/* code that enables some components, disables other ones*/
	}
	void OnDialogScene ()
	{
		/* code that enables some components, disables other ones*/
	}
	void OnDeathScreen ()
	{
		/* code that enables some components, disables other ones*/
	}

	public enum EState { undefined = 0 , GameMenu , Gameplay , DialogScene , DeathScreen }

}

Answering as the OP has asked for a more flexible design : It seems like the issue here is more deep-seeded issue due to bad design choices , you have a problem that shouldn’t occur.

The following reply is for cases in which you have 1 mono-behaviour that is responsible for everything (Which should be the case)

in theory, when you have multiple states, let’s say
"Game logic active " state
and "UI Logic active " state

When you press a button to toggle some UI element, the game logic routines should become “Unreachable” , aka not get evaluated till you release control from the UI back to the game (When you have a single mono-behaviour empty that is in-charge of everything, you become the boss of what to update each frame).

By doing as such , you don’t have to worry about components running or what not, you can just “ignore the problem” as it won’t be reachable anyway; and then you don’t need to push and pop states from before. that is the solution, let the components exist and active, they won’t get evaluated nor rendered nor anything until you exit your UI elements as we are preventing them from being updated.

(The only element you should keep track of, is time and frame-counter so you won’t have sudden hiccups in game logic when you resume the game and exit your UI elements).