For my game, I plan to have variables that many difference game objects would reference, like base health or damage multiplier, and they would be changed as the game progresses.
ex. a baseHealth variable:
Fast enemies would have baseHealth * 0.5, bosses would have baseHealth * 5.
If I want to store these variables in one place for easy access, should they be stored in a script, or a scriptable object, or something else?
There are a lot of options for this. Probably the most common are.
Singletons: I don’t really like using these as they cause tight coupling. e.g. GameState.Instance.baseHealth
MonoBehaviours: just pass the monobehaviour to everything that requires it. This can be a bit of a nightmare to manage though e.g. if a bullet requires the data then the path that builds it also requires it (player>weapon>bullet).
Dependency injection frameworks e.g. VContainer. Most game programmers find this overkill but it’s great for Unit testing. If you don’t need Unit testing then it’s not really worth looking at.
ScriptableObjects: if there is only going to be a single instance then this can work well. You can inject it directly into the classes that require it. It also works between scenes.
One nice thing about MonoBehaviours or ScriptableObjects is that you can easily change the data for debugging.
MonoBehaviours or dependency injection will also allow you to easily have multiple instances e.g. split screen with two baseHealths.
If you’re just starting then I’d probably stick with Singletons for now.
If by “global variables” you mean global constants, as your example suggests, for instance, a constant like baseHealth that is used in calculations such as baseHealth * 5, baseHealth * 10, and so on, then using a ScriptableObject or a JSON file, as suggested by @StCost, is preferable.
However, if by “global values” you mean variables that change as the state of your game evolves—such as enemy speed that varies during gameplay or values affected by buffs and debuffs—then the solutions proposed by @CodeKiwi are more suitable. In such cases, adding a static class with static fields can be a another option.
That said, dependency injection (with or without a framework) is generally a better approach than relying on singletons or static classes in this scenario.