Search Unity

Question Static variables?

Discussion in 'Scripting' started by FahimFuad, Sep 12, 2020.

  1. FahimFuad

    FahimFuad

    Joined:
    Jul 27, 2020
    Posts:
    26
    Hello!......I am making a platformer shooter game....and it has PlayTimeGameManager script and a GameManager script....they are given below:
    Code (CSharp):
    1. using UnityEngine;
    2. public class PlayTimeGameManager : MonoBehaviour
    3. {
    4.     [SerializeField] private Transform spawnPos;
    5.     [SerializeField] private GameObject Square;
    6.     [SerializeField] private GameObject Junkare;
    7.     [SerializeField] private GameObject defeatUI;
    8.     [SerializeField] private GameObject victoryUI;
    9.     [SerializeField] private Transform enemies;
    10.     [HideInInspector] public int currentNumberOfEnemies;
    11.     private GameObject player;
    12.     public static bool square { get; set; }
    13.     public static bool junkare { get; set; }
    14.  
    15.     private void Awake()
    16.     {
    17.         if(square) Instantiate(Square, spawnPos.position, Quaternion.identity);
    18.         if (junkare) Instantiate(Junkare, spawnPos.position, Quaternion.identity);
    19.     }
    20.  
    21.     private void Start()
    22.     {
    23.         player = GameObject.FindGameObjectWithTag("Player");
    24.         currentNumberOfEnemies = enemies.childCount;
    25.         defeatUI.SetActive(false);
    26.         victoryUI.SetActive(false);
    27.     }
    28.     public void GameOver() =>  defeatUI.SetActive(true);
    29.     private void Update()
    30.     {
    31.         if (currentNumberOfEnemies <= 0 )
    32.         {
    33.             victoryUI.SetActive(true);
    34.             player.SetActive(false);
    35.         }
    36.     }
    37.     public void Retry()
    38.     {
    39.         defeatUI.SetActive(false);
    40.         FindObjectOfType<GameManager>().Retry();
    41.     }
    42.    
    43. }
    44.  
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SceneManagement;
    3. using System.Collections;
    4. public class GameManager : MonoBehaviour
    5. {
    6.  
    7.     public Animator transitionAnimation;
    8.     public void LoadGame() => StartCoroutine(LoadLevel(1));
    9.     public void LoadMainMenu() => StartCoroutine(LoadLevel(0));
    10.     public void LoadChooseSquares() => StartCoroutine(LoadLevel(2));
    11.     public void Retry() => StartCoroutine(LoadLevel(SceneManager.GetActiveScene().buildIndex));
    12.     public void QuitGame() => Application.Quit();
    13.     private IEnumerator LoadLevel(int levelIndex)
    14.     {
    15.         transitionAnimation.SetTrigger("Start");
    16.         yield return new WaitForSeconds(2);
    17.         SceneManager.LoadScene(levelIndex);
    18.     }
    19.     public void LoadSquare()
    20.     {
    21.         PlayTimeGameManager.square = true;
    22.         PlayTimeGameManager.junkare = false;
    23.     }
    24.  
    25.     public void LoadJunkare()
    26.     {
    27.         PlayTimeGameManager.junkare = true;
    28.         PlayTimeGameManager.square = false;
    29.     }
    30. }
    31.  
    Problem: I want the player to select caracters between Square and Junkare, these are in separate scenes, when player selects square or junkare and hits play button, the square or the junkare is instantiated....now my question is...I have heared it is bad to use static variables....in my code two boolean for setting instantiating the caracter is used in PlayTimeGameManager script....how could I replace them?
     
  2. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    It's not bad to use static variables.

    The way to look at it is like this.....

    Classes are objects. And objects have buttons you push. These buttons are called 'methods'. That's just the simplest way of looking at it.

    Variables are the ....values the buttons use.

    Static variables are values where you only want one ever to exist.

    Regular variables mean that every time another object is created, it now has that variable on it.

    Regular variables in an RPG:
    HP, MP, because every character has their own set of these.

    Static variables in an RPG:
    Playtime because only one Playtime ever exists ever. Even if you were to create a second character, they share the same 'Playtime' variable.
     
    Randy_Riley and FahimFuad like this.
  3. FahimFuad

    FahimFuad

    Joined:
    Jul 27, 2020
    Posts:
    26
    Is There any way i can replace them....I mean the static variables takes up memory for lifetime...if I have like 100 of caracters like square and junkare it will be a complete mess with booleans...Is there a easy way?
     
  4. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    You need to learn that you don't need to inherit from Monobehaviour here:

    Code (CSharp):
    1. public class GameManager : MonoBehaviour
    2. {
    3.  
    4. }
    For me that was the first most important rule in Unity.

    You only need one single script ever to inherit from MonoBehaviour if you want.

    Well, then, how do you use the values from both classes you might ask. Well, if you have one MonoBehaviour script like this attached to your main camera that works as you main game manager:

    Code (CSharp):
    1.  
    2. //Script A
    3.  
    4. using PlayTime;
    5.  
    6. public namespace PlayTime {
    7. public class GameManager : MonoBehaviour
    8. {
    9. void Start() {
    10. }
    11. void Update() {
    12. }
    13. }

    You can then have your second class in a separate script:

    Code (csharp):
    1.  
    2. //Script B
    3. using PlayTime;
    4.  
    5. public namespace PlayTime {
    6.  
    7. public class Character {
    8.    public string Name;
    9.    public int HP;
    10.    public int MP;
    11. }
    12.  
    13. }
    14.  
    And now in that first script you can create individual characters like this:

    Code (csharp):
    1.  
    2. //Script A again, we change it now that character is an object we can create an 'instance' of:
    3. using PlayTime;
    4.  
    5. public namespace PlayTime {
    6. public class GameManager : MonoBehaviour
    7. {
    8. void Start() {
    9.     Character henry = new Character();
    10. }
    11. void Update() {
    12. }
    13. }
    Now we place buttons on Henry, that's what we call Methods:

    Code (csharp):
    1.  
    2. //Script B
    3. using PlayTime;
    4. using UnityEngine;
    5.  
    6. public namespace PlayTime {
    7.  
    8. public class Character {
    9.    public string Name;
    10.    public int HP;
    11.    public int MP;
    12.  
    13.    public void Punch() {
    14.       Debug.Log("Punch!");
    15.    }
    16. }
    17.  
    18. }
    19.  
    So you get how like, we can make Henry now punch like this, because punch is a public method, or how I look at it like a button on Henry we can push:

    Code (csharp):
    1.  
    2. //Script A again, we change it now that character is an object we can create an 'instance' of:
    3. using PlayTime;
    4.  
    5. public namespace PlayTime {
    6. public class GameManager : MonoBehaviour
    7. {
    8. void Start() {
    9.     Character henry = new Character();
    10.  
    11.     henry.Punch();
    12. }
    13. void Update() {
    14. }
    15. }


    So that's generally the basics of how C# works.

    Now, static methods are where you want to use static variables.

    Say, for example, we want to have a list of characters. At first, for the longest time, I would personally just put it in Script A in this example like so:


    Code (csharp):
    1.  
    2. //Script A again, we change it now that character is an object we can create an 'instance' of:
    3. using PlayTime;
    4.  
    5. public namespace PlayTime {
    6.  
    7. public class GameManager : MonoBehaviour
    8. {
    9.  
    10. public List<Character> CharactersList = new List<Character>();
    11.  
    12. public void AddCharacterToList(Character character) {
    13.      CharactersList.Add(character);
    14. }
    15.  
    16. void Start() {
    17.     Character henry = new Character();
    18.  
    19.     henry.Punch();
    20. }
    21.  
    22. void Update() {
    23. }
    24.  
    25. }


    But what happens if now GameManager ends up getting clogged up with all kinds of lists.

    So, you use a static list with static methods in the character class:

    Code (csharp):
    1.  
    2. //Script B
    3. using PlayTime;
    4. using UnityEngine;
    5.  
    6. public namespace PlayTime {
    7.  
    8. public class Character {
    9.    public string Name;
    10.    public int HP;
    11.    public int MP;
    12.  
    13.  
    14.    public static List<Character> CharactersList;
    15.  
    16.  
    17.    public void Punch() {
    18.       Debug.Log("Punch!");
    19.    }
    20. }
    21.  
    22. }
    23.  


    So now we have a CharactersList that we can access anywhere, right? The thing is, if we create a guy called Henry, and another gal called Karen or whatever, we can't access this list from them!

    Code (csharp):
    1.  
    2. //Script A again, we change it now that character is an object we can create an 'instance' of:
    3. using PlayTime;
    4.  
    5. public namespace PlayTime {
    6.  
    7. public class GameManager : MonoBehaviour
    8. {
    9.  
    10. void Start() {
    11.     //This also works:
    12.     Character henry = new Character();
    13.     Character karen = new Character();
    14.  
    15.     //This works:
    16.     henry.Punch();
    17.     karen.Punch();
    18.  
    19.     //This does NOT work:
    20.     henry.CharactersList.Add(new Character());
    21.  
    22.     //But this DOES work:
    23.     Character.CharactersList.Add(henry);
    24.  
    25. }
    26.  
    27. void Update() {
    28. }
    29.  
    30. }

    So why use a static list like this?

    Well, what I used to do when I first started was this inside of the game manager or whatever script you have attached to a game object:

    void Start() {
    Character henry = new Character();
    henry.HP = 5;
    henry.MP = 6;
    }


    But if we modify the Character class to be like this, using a 'constructor' (it looks odd at first but just see the code below, it works):


    Code (csharp):
    1.  
    2. //Script B
    3. using PlayTime;
    4. using UnityEngine;
    5.  
    6. public namespace PlayTime {
    7.  
    8. public class Character {
    9.  
    10.     //this is a constructor IT'S CONFUSING TO LOOK AT AT FIRST.
    11.      //You use the accessibily 'public' then the name of the class we're inside 'Character'.
    12.       //You can only do this once:
    13.     public Character(int _HP, int _MP, int _Name) {
    14.  
    15.          Debug.Log("Hello I'm a new character, I've been created just now...let's set values:");
    16.  
    17.          Name = _Name;
    18.          HP = _HP;
    19.          MP = _MP;
    20.  
    21.           //NOW every time a character is created, we can add it to the STATIC characters list here:
    22.           CharactersList.Add(this);
    23.  
    24.           //I use 'this' because 'this' is 'this' character object. It calls on itself so it can add itself to
    25.               //the static CharactersList.
    26.     }
    27.  
    28.  
    29.    public string Name;
    30.    public int HP;
    31.    public int MP;
    32.  
    33.  
    34.    public static List<Character> CharactersList;
    35.  
    36.  
    37.    public void Punch() {
    38.       Debug.Log("Punch!");
    39.    }
    40. }
    41.  
    42. }
    43.  


    Now finally back to script A:

    Code (csharp):
    1.  
    2. //Script A again, we change it now that character is an object we can create an 'instance' of:
    3. using PlayTime;
    4.  
    5. public namespace PlayTime {
    6.  
    7. public class GameManager : MonoBehaviour
    8. {
    9.  
    10. void Start() {
    11.     //This also works:
    12.     Character henry = new Character(25, 5, "Henry");
    13.  
    14.     //So we just created a character named "Henry", and we can assume it's been added to our
    15.      //static list, a static list that we can call from any script as long as do 'using Playtime' (because
    16.        //that's the namespace I used above.
    17.  
    18.      foreach (Character character in Character.CharactersList) {
    19.          Debug.Log("Character Name: " + character.Name);
    20.     }
    21.  
    22. }
    23.  
    24. void Update() {
    25. }
    26.  
    27. }

    But generally here now we can now add a static method for getting a character:

    Code (CSharp):
    1.  
    2. using System;
    3. using UnityEngine;
    4. using System.Collections.Generic; //< you need this for lists don't forget
    5.  
    6. public class Character {
    7.     public Character(int _HP, int _MP, int _Name) {
    8.  
    9.          Debug.Log("Hello I'm a new character, I've been created just now...let's set values:");
    10.  
    11.          Name = _Name;
    12.          HP = _HP;
    13.          MP = _MP;
    14.  
    15.          Debug.Log("I'm now adding myself to the global characters list, of which only one will ever exist because it's static:");
    16.          CharactersList.Add(this);
    17.  
    18.         Debug.Log("I've added myself, now you can find me.");
    19.     }
    20.  
    21.    public string Name;
    22.    public int HP;
    23.    public int MP;
    24.    public static List<Character> CharactersList;
    25.  
    26.    public static void GrabCharacter(string characterName) {
    27.       foreach (Character character in CharactersList) {
    28.           if (character.Name == characterName) {
    29.               return character;
    30.           }
    31.  
    32.          Debug.LogError("Couldn't find character with name of: " + characterName + " in global Characters List!");
    33.       }
    34.    }
    35.  
    36.    public void Punch() {
    37.       Debug.Log("Punch!");
    38.    }
    39. }
    40.  
    41. }
    42.  
    And now we can attach JUST this script to a gameobject. Script B above does not have to be attached to any game object.

    Code (CSharp):
    1. [code]
    2. //Script A again
    3. using System;
    4. using UnityEngine;
    5. using System.Collections.Generic;
    6.  
    7. public namespace PlayTime {
    8.  
    9. public class GameManager : MonoBehaviour
    10. {
    11.  
    12. void Start() {
    13.     //This also works:
    14.     Character henry = new Character(25, 5, "Henry");
    15.  
    16.     Debug.Log("Henry now exists, so lets grab him using Character.GrabCharacter("Henry"):
    17.  
    18.     Character henryFound = Character.GrabCharacter("Henry");
    19.  
    20.    Debug.Log("Now lets make Henry punch:");
    21.  
    22.     henryFound.Punch();
    23.  
    24. }
    25.  
    26. void Update() {
    27.  
    28. }
    29.  
    30. }


    Now, you don't NEED to use static values, but they're good for organization. That's why you see the Unity API using 'static methods' all over the place, because it's just a good way of keeping everything insanely clean.

    Static method examples are like:

    Application.dataPath() < - returns the windows /Assets/ root path of your game.


    Just remember:
    classes are 'objects'
    methods are like 'buttons' on these 'objects' that you push
    static variables, used by static methods are a button that everyone shares together, and can push at any time.

    I use static methods mainly for stuff like making a CreateCharacter(); method, or making a ChangeTimeOfDay(); method. Static methods can be called by anything, anytime, anywhere as LONG as they have access to it (it's public, you called the namespace using the 'using' keyword at the top of your script, etc...)
     
    Last edited: Sep 12, 2020
    travlake, Lime_x and FahimFuad like this.
  5. Ardenian

    Ardenian

    Joined:
    Dec 7, 2016
    Posts:
    313
    There are arguments for and against using static variables (and classes) in the Unity development environment. One of the biggest arguments against using them is that there is zero support from Unity in that direction. They work, but there is no enhancement in the work flow.

    I recommend you to look into ScriptableObjects. They are assets that exist at runtime independent from your scenes. In the example by Unity, you can see how ScriptableObjects are used for manager classes instead of using a static class (more or less). Basically, you write your manager definition as a ScriptableObject, create one instance for it and reference that instance everywhere.

    This is an article that highlights some of these advantages over static variables and classes: Three ways to architect your game with ScriptableObjects
     
  6. FahimFuad

    FahimFuad

    Joined:
    Jul 27, 2020
    Posts:
    26
    I have used scriptable objects in handeling player stats(power, hp , jump speed of square and junkare{these are the caracters in my game})....but my problem is...i want to have a player selection UI(I made this).....I have also added buttons in this menu but I dont know that what to do when player clicks on this button..that is how to make my character active...I cant communicate between the main menu scene and the game scene without the static variables.....
     
  7. Ardenian

    Ardenian

    Joined:
    Dec 7, 2016
    Posts:
    313
    You could, for instance, store the character prefab that the player selects in your SO instance and then when loading the game scene instantiate that character prefab When the player clicks the button, you would set the referenced prefab in the SO instance.
     
  8. djweaver

    djweaver

    Joined:
    Jul 4, 2020
    Posts:
    105
    I appreciate your explanations but in the link provided I don't really see any comparisons to a similar static class/variable system versus the ScriptableObjects. Its just demonstrating how ScriptableObjects could be used, and from my limited perspective, I can't see how it offers any advantage over a static class approach. I'm at a confusing stage in my games development where communication between scripts and objects and flow of data in general is becoming more important and so far I'm only using static classes and I'm having a hard time understanding why I should use an event system or say, ScriptableObjects.

    I don't want to hijack OP's thread so here is a link if you're interested: https://forum.unity.com/threads/best-way-to-handle-projectiles-top-down-scrolling-shooter.969069/

    EDIT: found Ryan Hipples scriptable object talk linked at the top of that link you provided. Good stuff, much more in depth than the article:


    Also, Charles from Infallible Code and Jason Storey briefly discuss their typical use-cases for scriptable objects here:
     
    Last edited: Sep 14, 2020
  9. Ardenian

    Ardenian

    Joined:
    Dec 7, 2016
    Posts:
    313
    You are right, it does not draw that comparison directly. However, it shows some cases that use ScriptableObject instances that are similar to cases that would otherwise use static references to object instances.

    The most immediate advantage of SO instances is the ability to reference them in the editor, whereas when using instances of an object with a static reference, you cannot use those instances around the Unity inspector (without writing a wrapper).

    The article that I linked, the Player HP example is perfect. If you used a static variable that references your player HP, you would have to hardcode everything that uses the player healthpoints. Do you have an UI element that displays the player HP? You have to hardcode it to read out the player HP from that static variable in your player script. Additionally, you have to do this every time you interact with your player HP, from anywhere.

    However, with a SO instance, you hardcode it once and then you can reference it everywhere. You no longer need to write an UI element definition for every player stat that you want to display, but you write one generic definition that can display any player stat, which are your SO instances.

    This is just one example, but it might show you how powerful this can get.
     
    FahimFuad and djweaver like this.