Search Unity

Best way to get variable from another script C# unity

Discussion in 'Scripting' started by xeonheart, Nov 6, 2018.

  1. xeonheart

    xeonheart

    Joined:
    Jul 24, 2018
    Posts:
    219
    Hello,

    I have been reading on the forums, and i see there are 2 ways of doing it...:

    the one i think many recommend is using the GetComponent correct?

    need help trying to implement it, i am confused on the naming, so here is my 2 script names below...

    Code (CSharp):
    1.  
    2. ///this has the variable i want to pass to Ant_Eat script
    3. public class Smash : MonoBehaviour
    4. {
    5.  
    6.     bool Smashed_Ant = false;
    7.  
    8. void Start()
    9. {
    10. }
    11.  
    12. void Update()
    13. {
    14.  
    15. }
    16.  
    17.  
    18. }
    19.  
    20.  
    21. ///this is the script i want to have the bool character appear
    22. public class Smash : MonoBehaviour
    23. {
    24.  
    25.     GameObject Ant_Smashed = GameObject.Find("Smash");
    26.  
    27. void Start()
    28. {
    29. Smash smash = Ant_Smashed.GetComponent<Smash>();
    30. }
    31.  
    32. void Update()
    33. {
    34.  
    35. }
    36.  
    37.  
    38. }
    39.  
    again, trying to understand but let me know, the other trick was to put the bool variable as a static, which i believe jlb said not to do, i want to learn the right way and good habits :)
     
    unity_uCAh1YGgjxXPOw likes this.
  2. Tarball

    Tarball

    Joined:
    Oct 16, 2016
    Posts:
    166
    You want to use Find very sparingly. Probably don't use it in this case. In most cases, you will do the following:

    Code (CSharp):
    1. public class Bool_Script_To_Access : MonoBehaviour
    2. {
    3.     public bool Smashed_Ant = false;
    4. }
    Code (CSharp):
    1. public class Script_To_Do_Stuff : MonoBehaviour
    2. {
    3.     public GameObject ant;
    4.     private Bool_Script_To_Access bool_script;
    5.     private void Start()
    6.     {
    7.         bool_script = ant.GetComponent<Bool_Script_To_Access>();
    8.     }
    9.     private void Update()
    10.     {
    11.         bool_script.Smashed_Ant = true;
    12.     }
    13. }
    This is assuming that the two scripts are attached to two separate game objects. Now, in the inspector, select the game object which is attached to Script_To_Do_Stuff. Then, drag the game object which is attached to Bool_Script_To_Access to where it says "ant." Done. This will work for most cases you'll have. If you need more general reference to a game object it gets more difficult, and you might want to look into singleton approach.

    Edit: You might check out the first 15 minutes or so of https://unity3d.com/learn/tutorials/topics/scripting/persistence-saving-and-loading-data
    where he creates a GameControl which stores variables that can be accessed from any script. It is a great video.
     
    Last edited: Nov 6, 2018
  3. xeonheart

    xeonheart

    Joined:
    Jul 24, 2018
    Posts:
    219
    ok really trying but not getting anywhere :(

    i tried using this tutorial on youtube.


    so here is the code so far:
    Code (CSharp):
    1.  
    2. public class Smash : MonoBehaviour
    3. {
    4.     public bool Smashed_Ant = false;
    5.     void FixedUpdate ()
    6.     {
    7.          void_Smashed_Ant(); // this will change the bool Smashed_Ant to true;
    8.     }
    9.  
    10. }
    11.  
    the script that needs to access is below, so the above script has a touch function, and when the user touches the ant, he smashed it, so with the bool variable now changed from false to true, i want the below script to see it and stop a timer below, or in this example, just use the log to register it smashed.

    Code (CSharp):
    1.  
    2. public class Ant_Eat : MonoBehaviour
    3. {
    4.  
    5.     public GameObject Ant;
    6.     void Start()
    7.     {
    8.         Ant = GameObject.FindGameObjectWithTag("Ant");
    9.     }
    10.  
    11.     void Update()
    12.     {
    13.         if (Ant.GetComponent<Smash>().Smashed_Ant == true)
    14.         {
    15.              Debug.Log("Ant Smashed");
    16.          }
    17.    }
    18.  
    19. }
    20.  
     
    Rahat_Hassan likes this.
  4. xeonheart

    xeonheart

    Joined:
    Jul 24, 2018
    Posts:
    219
    Hey Tarball, i tried your example but now i am getting error message:
    Code (CSharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. Ant_Eat.Update () (at Assets/Scripts/Ant_Eat.cs:68)
    any idea why?
     
  5. xeonheart

    xeonheart

    Joined:
    Jul 24, 2018
    Posts:
    219
    sorry the error message is pointing to this part of the code:
    Code (CSharp):
    1.         if (smash.Smashed_Ant == true)
    2.         {
    3.             Ant_Timer = 10.0f;
    4.         }
     
  6. Tarball

    Tarball

    Joined:
    Oct 16, 2016
    Posts:
    166
    I haven't watched the video, but I will say using GetComponent every single frame is a very bad idea -- very expensive. The 2 ways I told you both work great, however.
     
    Arkknight38 likes this.
  7. Tarball

    Tarball

    Joined:
    Oct 16, 2016
    Posts:
    166
    The object reference error is because you have to drag the ant game object to the field "ant" in the other script in the inspector. I mention that above.
     
  8. xeonheart

    xeonheart

    Joined:
    Jul 24, 2018
    Posts:
    219
    k cool, i will try them out, also i tried using inheritance from one script to another, as shown below:
    Code (CSharp):
    1. public class Ant_Eat : Smash
    2. {
    3.  
    4. }
    and able to access the variables no problem, the issue still resides, that capturing the change of the variable.

    But do you agree with the method i tried? or the other 2 you suggested?
     
  9. xeonheart

    xeonheart

    Joined:
    Jul 24, 2018
    Posts:
    219
    ugh frustrated with something simple...

    i tried on Ant_Eat script:

    Code (CSharp):
    1. if(ant.GetComponent<Smash>.Smashed_Ant == true)
    2. {
    3.       Debug.Log("ant smashed");
    4. }
    of course its a method and not a variable... not sure what to do :(
     
  10. xeonheart

    xeonheart

    Joined:
    Jul 24, 2018
    Posts:
    219
    ok let me just give the objective of what i want maybe this will help.

    if the ant collides with an object, then start a timer... if its smashed even when its collided or not, stop or reset a timer back to 10 seconds. i have a timer that goes off when the ant collider hits another object collider, which then has a count down timer... if the ant is smashed before the timer is 0, then reset timer to 10 seconds...

    so far, i just have it where the ant object is destroyed... and it has a script called smash on it... and the other object has the "Ant_Eat" script... :(
     
  11. NicBischoff

    NicBischoff

    Joined:
    Mar 19, 2014
    Posts:
    204
    You need to either Get the value or Set the value. When the bug is squished you would invoke a method or call a method on the bug to update its state. It’s possible to have the object monitor a variable but that is not ideal for performance. Rather think about it this way, the input script checks if you clicked on a bug. Once you click on a bug you call the method on a bug script that resides on the bug called ‘Clicked()’. I think your question is more along the line of logic than process.
     
    MachinatioVitae likes this.
  12. Tarball

    Tarball

    Joined:
    Oct 16, 2016
    Posts:
    166
    Really, my first post above is all you need to know. Just modify it, so instead of
    Code (CSharp):
    1.     private void Update()
    2.     {
    3.         bool_script.Smashed_Ant = true;
    4.     }
    use

    Code (CSharp):
    1. private void Update()
    2.     {
    3.         if(Input.GetKey(KeyCode.J)
    4.             bool_script.Smashed_Ant = true;
    5.         if(Input.GetKeyUp(KeyCode.J)
    6.             bool_script.Smashed_Ant = false;
    7.         if(bool_script.Smashed_Ant == true)
    8.             Debug.Log("ant smashed");
    9.     }
    You should very rarely (only if you absolutely have to) use GetComponent in Update. Notice how I cache the GetComponent in a variable in Start above. Otherwise, it could lead to garbage that needs to be collected. Good Luck
     
  13. xeonheart

    xeonheart

    Joined:
    Jul 24, 2018
    Posts:
    219
    Hey Tarball,

    thank you for taking the time, however i am getting this error message:
    UnassignedReferenceException: The variable ant of Ant_Eat has not been assigned.
    You probably need to assign the ant variable of the Ant_Eat script in the inspector.
    UnityEngine.GameObject.GetComponent[Smash] () (at C:/buildslave/unity/build/Runtime/Export/GameObject.bindings.cs:28)
    Ant_Eat.Start () (at Assets/Scripts/Ant_Eat.cs:27)


    at this part of the code:
    Code (CSharp):
    1.    
    2. public GameObject ant;
    3.     private Smash smash;
    4.  
    5.     // Use this for initialization
    6.     void Start()
    7.     {
    8.         smash = ant.GetComponent<Smash>(); /* <- right here is where its saying the error message is raised*/
    9.     }
    not sure why. I dragged my ant prefab to the public Gameobject on my unity, but this error still shows up.
     
  14. Tarball

    Tarball

    Joined:
    Oct 16, 2016
    Posts:
    166
    That's the thing -- use the ant in the scene and not the prefab. It's a pain to get your instantiated objects to play well with your game objects that are always in the scene, because of the way Unity works. Sometimes you have to get creative, depending on what you want to do. If it's not in the scene before you hit play, then you may want to "find" it at Start(). I would use "find" sparingly though, as I said before, and try to only use it at Start() or Awake().

    The GameControl method works pretty well for me, because the player is always in the scene. I try to design everything so that the ant needs to access player information and rarely the other way around, if that makes sense. Because, there may be many ants, but in my case, there is only one player. Then, I store player information in the GameControl. If you need even more access, then a more advanced singleton would perhaps be better.

    For an unrelated example, just to give you an idea of how to manage many ants in a scene, in the game I'm making right now, I use "tags" often. I'll have a collider on the ant. Then in OnColliderEnter(), if (collider.gameObject.tag == "player"), then he gets squashed. That keeps things simple, and then each individual ant has their own squashed boolean variable, so every ant in the scene is not affected -- only the one that was collided with. That's just one way to handle things, but I hope you get the idea.
     
    Helbino likes this.
  15. Helbino

    Helbino

    Joined:
    Jan 14, 2021
    Posts:
    3
    Thanks for the extremely detailed answers, Tarball! I was able to get script referencing to work after about 3 hours because of this.

    Some key lessons I learnt:

    • Public variables show up in the Inspector under the script. (I believe serialize field private variables do too, but haven't experimented on that).
    • When you are just trying to read or reference a variable in the initial script, the only thing that needs to change is whether it is public (or serialized private).
    • In your accessing script:
    • Since you are going to be referring to the script to be accessed through a game object, you must create a public Gameobject. What this does is it creates a variable on the inspector that allows for a Gamobject to be dropped in.
    • You drag and drop (from the hierarchy) the gameobject (with the script to be accessed attached to it) through the inspector into that Gameobject variable space.

    And it works like a charm!
     
  16. TromJH230

    TromJH230

    Joined:
    Feb 19, 2020
    Posts:
    2
    I've found that you can create a static Instance of an object if you are referencing a script in multiple other scripts. Be aware that this will only work if there is only a single instance of the script.

    So here is the script that you want to get variables from:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class ObjectPooler : MonoBehaviour
    6. {
    7.     public static ObjectPooler Instance;
    8.  
    9.     private void Awake()
    10.     {
    11.         Instance = this;
    12.     }
    13.     // Rest of your code goes here
    14. }
    15.  
    You can then reference the script in other scripts like this:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Example : MonoBehaviour
    6. {
    7.     ObjectPooler objectPooler;
    8.     void Start()
    9.     {
    10.         objecetPooler = ObjectPooler.Instance;
    11.     }
    12.     //Rest of your code below
    13. }
     
    thiyageshvenkat and SmithySFC like this.
  17. SmithySFC

    SmithySFC

    Joined:
    Jan 15, 2021
    Posts:
    11
    Superb explanation with sample code - great job!
     
  18. VoidBoltGamesLTD

    VoidBoltGamesLTD

    Joined:
    Nov 10, 2023
    Posts:
    2

    will this work on prefabs where the referenced game object is outside the prefab