Search code examples
c#design-patternsunity-game-engineorganizationcode-organization

Unity - Accessing other scripts' variables - put properly


I know how to access another script's variable, but I can do only via function calls or delegates. The first one is easy, but makes the code fragile becuase once I edit the original, I have to edit again. The second one is better but as I have a lot of functions, with different kinds of return values and parameters, it would complicate things a lot. Say I want to do some stuff at the beginning of the game. So far I created a function named OnGameStart() in the appropriate script and called everything I will need from there, and that OnGameStart() was made public and was called from another script.

I will need to play sounds, check save data, play UI and other animations and so on at the beginning but I don't want to make my code a disaster. I looked for this online but found only the simplest "how to communicate between scripts" stuff, which goes with the basic function call, sometimes events. Could someone skilled guide me to resources on how to make compact, segregated classes that hold up Demeter's law?


Solution

  • There are definitely a lot of possibilities to address such problem, you could for instance take some inspiration from the Hollywood principle.

    Instead of your Player searching for something, provide it to him at initialization.

    Here's a really quick example:

    Definition of a game manager interface:

    using UnityEngine;
    
    namespace Assets.Scripts
    {
        public interface IGameManager
        {
            void PlayAudioClip(AudioClip audioClip);
        }
    }
    

    Definition of a game manager:

    using UnityEngine;
    
    namespace Assets.Scripts
    {
        public class GameManager : MonoBehaviour, IGameManager
        {
            #region IGameManager Members
    
            public void PlayAudioClip(AudioClip audioClip)
            {
                // TODO
            }
    
            #endregion
        }
    }
    

    An example:

    using System;
    using UnityEngine;
    
    namespace Assets.Scripts
    {
        public class Player : MonoBehaviour
        {
            public GameManager GameManager; // TODO assign this in Inspector
    
            public void Start()
            {
                if (GameManager == null)
                    throw new InvalidOperationException("TODO");
            }
    
            public void Update()
            {
                // demo
                var wounded = true;
                var woundedAudioClip = new AudioClip();
                if (wounded)
                {
                    GameManager.PlayAudioClip(woundedAudioClip);
                }
            }
        }
    }
    

    You could also have a used a sort of Singleton in Unity, (or whatever appropriate).

    Notes:

    The example above is really just an example to give a you hint on how to think, you haven't provided all the details and even if you did, we could hardly help you any further (only you will find over time what's really appropriate to your current problem).

    When advancing in your game project you will see that there are no hard rules to follow, obviously patterns are important but you will probably find yourself ending up with your own (i.e. a finely-grained combined usage of multiple patterns).

    Maybe 'Who wrote this programing saying? "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."' will give you some inspiration too.

    Conclusion is, discover/try patterns, adapt them, and over time you will find what's working the best for you.