Search Unity

  1. We are migrating the Unity Forums to Unity Discussions. On July 12, the Unity Forums will become read-only. On July 15, Unity Discussions will become read-only until July 18, when the new design and the migrated forum contents will go live. Read our full announcement for more information and let us know if you have any questions.

How to change a variable from another script?

Discussion in 'Scripting' started by jleven22, Jun 27, 2019.

  1. jleven22

    jleven22

    Joined:
    Mar 26, 2019
    Posts:
    436
    I am new to C# and Unity, but slowly learning. That said, please explain to me like I'm a 5 year old.

    I have a script on my player game object that is tied to the UI. My "int health" relates to how many hearts are shown in the UI.

    I would like to access this from my GameController script, wherein enemies can damage and items can heal the player. How can I access "health" from my GameController script?
     
  2. Optrix

    Optrix

    Joined:
    May 30, 2017
    Posts:
    17
    You can point the classes to one-another.

    Let's say you start with the classes below...

    Code (CSharp):
    1. class Player : MonoBehaviour
    2. {
    3.    public int Health;
    4. }
    5.  
    6. class GameController : MonoBehaviour
    7. {
    8.    void Update()
    9.    {
    10.        //Ding the Health
    11.    }
    12. }
    And you want to change or view health from GameController.

    Simply add a public variable in GameController.

    Code (csharp):
    1. class GameController : MonoBehaviour
    2. {
    3.    public Player MyPlayer;
    4.  
    5.    void Update()
    6.    {
    7.        //Ding the Health
    8.        MyPlayer.Heath--;
    9.    }
    10. }
    Now go to the editor, click on the GameObject with the 'GameController' class, and you'll see in the Inspector that there's now a property called 'MyPlayer'.

    Drag-and-drop your GameObject with the Player component into this MyPlayer box.

    There - GameController can now directly interact with your player health - or anything else you want, including the Animator class (through MyPlayer.GetComponent<Animator>())
     
    Bunny83, LeeHearn, jimisv52 and 5 others like this.
  3. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    You access it by having health be either a public variable or through a public method on the player script. Examples:

    Code (csharp):
    1. public int Health;
    or

    Code (csharp):
    1. private int health = 5;
    2.  
    3. public void CauseDamage(int damageAmount)
    4. {
    5.     health -= damageAmount;
    6. }
    7.  
    8. public int GetHealth()
    9. {
    10.     return health;
    11. }
    Another way is to use getters and setters, but some new users get confused by them and they don't seem as popular today as when I first was learning C# back in the day.

    Now how to actually access this player script from the GameController script is done through a reference to the player script. How to get that reference can be a little tricky, depending on if you are instantiating the player object or if it is part of the scene. If part of the scene you can just establish the reference by dragging it to a public field on the GameController script. Otherwise you can use a singleton, or one of the find methods.

    For example, lets say the player object is an instantiated prefab, and the GameController is a part of the scene (pretty common way of doing things). The GameController script is on a GameObject which has been tagged "GameControllerObject". You can do something like below:

    Player script:
    Code (csharp):
    1. private int health = 5;
    2.  
    3. void Start()
    4. {
    5.     GameObject gameControllerObject = GameObject.FindWithTag("GameControllerObject");
    6.     if (gameControllerObject != null)
    7.     {
    8.         GameController gameControllerScript = gameControllerObject.GetComponent<GameController>();
    9.         if (gameControllerScript != null)
    10.         {
    11.             gameControllerScript.PlayerScript = this;
    12.         }
    13.         else
    14.         {
    15.             Debug.Log("GameControllerObject is missing the GameController component");
    16.         }
    17.     }
    18.     else
    19.     {
    20.         Debug.Log("Unable to find GameControllerObject in scene");
    21.     }
    22. }
    23.  
    24. public void CauseDamage(int damageAmount)
    25. {
    26.     health -= damageAmount;
    27. }
    28.  
    29. public int GetHealth()
    30. {
    31.     return health;
    32. }
    33.  
    34.  
    GameController script:
    Code (csharp):
    1. public Player PlayerScript;
    2.  
    3. //Damage the player
    4. public void DamagePlayer(int amountDamage)
    5. {
    6.     if (PlayerScript != null)
    7.     {
    8.         PlayerScript.CauseDamage(amountDamage);
    9.     }
    10. }
    11.  
    12. //Get the player's health, returns -1 if we are missing a reference to the player
    13. public int GetPlayerHealth()
    14. {
    15.     int returnValue = -1;
    16.     if (PlayerScript != null)
    17.     {
    18.         returnValue = PlayerScript.GetHealth();
    19.     }
    20.     return returnValue;
    21. }
     
    MysleyMakers likes this.
  4. jleven22

    jleven22

    Joined:
    Mar 26, 2019
    Posts:
    436
    So after reading through this, I think it makes more sense that I build my health into my player script, then use the Game Controller object to cause damage or healing.

    So here's what I'm looking to do:
    1) Have a player int health = 3
    2) Have the Health script (tied to UI) applied to Player game object, then get the int health from the Player script
    3) Manipulate the player int health using Game Controller object & script.

    Here's what I've got so far:

    Player Script:
    Code (CSharp):
    1. public class BitchFaceController : MonoBehaviour
    2. {
    3.     //Combat System
    4.     public static int health;
    5.  
    6.     private void Start()
    7.     {
    8.         health = 3;
    9.     }
    10.  
    Health Script:
    Code (CSharp):
    1. public class Health : MonoBehaviour
    2. {
    3.     public BitchFaceController healthScript;
    4.  
    5.     private int health;
    6.     public int numOfHearts;
    7.  
    8.     public Image[] hearts;
    9.     public Sprite fullHeart;
    10.     public Sprite emptyHeart;
    11.  
    12.     // Start is called before the first frame update
    13.     void Start()
    14.     {
    15.         healthScript.Health.health = 3;
    16.     }
    17.  
    18.     // Update is called once per frame
    19.     void Update()
    20.     {
    21.  
    22.         if(health > numOfHearts)
    23.         {
    24.             health = numOfHearts;
    25.         }
    26.  
    27.         for (int i = 0; i < hearts.Length; i++)
    28.         {
    29.             if (i < health)
    30.             {
    31.                 hearts[i].sprite = fullHeart;
    32.             }
    33.             else
    34.             {
    35.                 hearts[i].sprite = emptyHeart;
    36.             }
    37.             {
    38.  
    39.             }
    40.             if(i < numOfHearts)
    41.             {
    42.                 hearts[i].enabled = true;
    43.             }
    44.             else
    45.             {
    46.                 hearts[i].enabled = false;
    47.             }
    48.         }
    49.     }
    50. }
    51.  
    I'm getting this error:
    Assets\Scripts\Health.cs(20,9): error CS0176: Member 'BitchFaceController.health' cannot be accessed with an instance reference; qualify it with a type name instead

    I'm so out of my league here, and I really feel like it shouldn't be this complicated right?
     
    ZoeWheat likes this.
  5. WallaceT_MFM

    WallaceT_MFM

    Joined:
    Sep 25, 2017
    Posts:
    394
    The issue is that you've made the health variable static. A static variable is a variable that belongs to the type, not to instances of the type. An analogy here is that the number of corners a rectangle has is not a property of any particular rectangle, but something that all rectangles have in common. A static variable says that all instances of that type share the same value and the value cannot be different from instance to instance. You can't have a rectangle with 3 corners.
    So, change
    public static int health
    to
    public int health


    However, that change won't solve all of your problems here. Maybe you left out some code, but these two class don't look like they do anything together. Line 15 of the Health script looks like it is trying to assign a value but then that value is never referenced anywhere else. Also, I think that line should read
    healthScript.health = 3;
    , you have an extra Health in there that will cause a compiler error.

    tbh, you probably should delete the first class altogether and handle it through the Health script.
    Code (csharp):
    1.  
    2. public class Health : MonoBehaviour
    3. {
    4.     // If you are new to C#, you may not have seen these before. This is called an auto-property. A property is
    5.     // a shortcut for making a variable and a pair of methods that get and set the variable's value. In this case,
    6.     // we let anyone get Health's value, but only we are allowed to set it.
    7.     public int Health {get; private set;}
    8.     public int numOfHearts;
    9.  
    10.     public Image[] hearts;
    11.     public Sprite fullHeart;
    12.     public Sprite emptyHeart;
    13.  
    14.     public void TakeDamage(int damage)
    15.     {
    16.         Health -= damage; // We make this a different method so that you could do other effects in here if you want to, like play and 'oof' sound or blood animation
    17.     }
    18.  
    19.     // Start is called before the first frame update
    20.     void Start()
    21.     {
    22.          Health = numOfHearts;
    23.     }
    24.  
    25.     // Update is called once per frame
    26.     void Update()
    27.     {
    28.         if(Health > numOfHearts)
    29.         {
    30.             Health = numOfHearts;
    31.         }
    32.  
    33.         for (int i = 0; i < hearts.Length; i++)
    34.         {
    35.             if (i < Health)
    36.             {
    37.                 hearts[i].sprite = fullHeart;
    38.             }
    39.             else
    40.             {
    41.                 hearts[i].sprite = emptyHeart;
    42.             }
    43.             if(i < numOfHearts)
    44.             {
    45.                 hearts[i].enabled = true;
    46.             }
    47.             else
    48.             {
    49.                 hearts[i].enabled = false;
    50.             }
    51.         }
    52.     }
    53. }
    54.  
    55. // This is in a different file, and is an example for a GameManager
    56. public class GameManager : MonoBehaviour
    57. {
    58.     private Health playerHealth; // This is not visible in the inspector, or to other scripts because it is private
    59.  
    60.     void Start()
    61.     {
    62.         // This searches the scene for a Health object, which should be your player's health script
    63.         // If you add more than one health script to the scene, this will stop working correctly and might
    64.         // grab a different health script, which is almost definitely not what you want
    65.          playerHealth = FindObjectOfType<Health>();
    66.     }
    67.  
    68.     // This method lets any other script that knows about the GameManager damage the player if it wants to by calling gameManager.DamagePlayer(1);
    69.     public void DamagePlayer(int damage)
    70.     {
    71.          playerHealth.TakeDamage(damage);
    72.     }
    73. }
    74.  
     
  6. nathanhawthorne12

    nathanhawthorne12

    Joined:
    Jan 2, 2019
    Posts:
    29
    So the way I do it is a bit different, found on another page. No idea if its the right way to do it or not but it works, so I cant complain.

    In one of my games we have ManagerScripts, Health,Scoring,Spawning,AdsManager etc etc.

    In my health script I have


    Code (CSharp):
    1. private static PlayerHealthManager _Instance;
    2.     public static PlayerHealthManager Instance
    3.     {
    4.         get
    5.         {
    6.             if (_Instance == null)
    7.             {
    8.                 GameObject go = new GameObject("PlayerHealthManager");
    9.                 go.AddComponent<PlayerHealthManager>();
    10.             }
    11.             return _Instance;
    12.         }
    13.     }  
    14.  
    15. private void Awake()
    16.     {
    17.         _Instance = this;
    18.     }
    19.  
    20. [Header("Health")]
    21. public float CurrentHealth;  
    22.  
    23. public void Start()
    24.     {      
    25.         CurrentHealth = 100;      
    26.     }
    27.  
    28.     public void Update()
    29.     {
    30.         if (Time.frameCount % IntervalTime == 0)
    31.         {
    32.             SliderInformation();
    33.         }      
    34.     }
    35.  
    36.     public void AddHealth(int FHP)
    37.     {
    38.         CurrentHealth += FHP;
    39.      
    40.     }
    41.  
    42.     public void SubtractHealth(int FHP)
    43.     {
    44.         CurrentHealth -= FHP;
    45.     }
    46.  
    47.     public void SliderInformation()
    48.     {
    49.         HealthBarSlider.value = CurrentHealth;      
    50.     }
    51. }
    This is attached to a blank game object on the main scene.
    If I am in another script and i need to update health all i do is

    PlayerHealthManager.instance.AddHealth(how much i want to add it by)
    or
    PlayerHealthManager.instance.SubtractHealth(How much i remove health by)

    As I said unsure if this is the correct way to do it but it works for me.
     
  7. Optrix

    Optrix

    Joined:
    May 30, 2017
    Posts:
    17
    Nothing wrong with this as such, but you should only use statics like this if you only have one instance of whatever it is (ie. player) in a scene. If you decided to support two or more players, you'd need to rebuild everything, and it would be awkward for enemy health as you'd have plenty of enemies on screen at once.

    You should use a static instance like this for anything in the scene that is purely management - scripts that find out if the level has been completed, spawn enemies etc. They are fantastic for that, since a number of different scene objects will need to interact with them.

    Code (CSharp):
    1. private static PlayerHealthManager _Instance;
    2.     public static PlayerHealthManager Instance
    3.     {
    4.         get
    5.         {
    6.             if (_Instance == null)
    7.             {
    8.                 GameObject go = new GameObject("PlayerHealthManager");
    9.                 go.AddComponent<PlayerHealthManager>();
    10.             }
    11.             return _Instance;
    12.         }
    13.     }
    14.  
    Quick comment about this though...

    While this is a cute little "Oops, I forgot to add the behaviour to a GameObject!" trick, there's also no specific need to create a new game object at runtime - I prefer to keep all of these sorts of 'management' behaviours on a single GameObject, rather than dozens of different GameObjects, which your solution would most likely lead to if you forgot to put the PlayerHealthManager on something in your scene. Worse, there's a chance that your script execution order might mean that PlayerHealthManager ended up being in the scene twice in certain scenarios, and you won't be able to tell which one is 'active'. However, there's almost no performance difference, it's just neater :p.
     
  8. nathanhawthorne12

    nathanhawthorne12

    Joined:
    Jan 2, 2019
    Posts:
    29
    Morning Optrix

    Yup your right, I fell into the trap when i first started Unity and c# a few months ago "what is the easiest way to manipulate variables from any script with ease" sort of thing and found the above. Knowing what I know now, yeah i'd much rather call scripts via "public MyScript _myscript" and getcomponent etc. But I must admit it is nice and easy to manipulate variables whilst running when all of the Managers are visible on one GameObject :D

    The game I am working on only has 3 scenes and 2 of them are pre-initialisation and main menu so all the little Gamemanagers scripts are attached to one gameobject so I have no trouble now, but I can see it happening soon if I decide to change current game or do a new one :D
     
  9. chkashifali355

    chkashifali355

    Joined:
    Nov 18, 2021
    Posts:
    3
    Do you want to change the speed for just one platform, or change for all platforms?
    If you want the same speed for all platforms, you should make speed static.

    in class Car declare a variable as static

    public static float speed;
    and class from you want to update value
    Car.speed = 15.0f;


    If you want to change the speed for a specific platform, get the Platform component from a game object, and modify speed.
    // assuming "platform" is of type gameObject
    platform.GetComponent<Platform>().speed = 15.0f; // replace 15.0f with your desired speed.
     
  10. kokiad

    kokiad

    Joined:
    Mar 11, 2022
    Posts:
    1

    This way I can only put one object in the inspector (in the MyPlayer) what if I want to put multiple objects.