Search Unity

Is this a good way of cummunicating between scripts?

Discussion in 'Scripting' started by Jason210, Mar 27, 2018.

  1. Jason210

    Jason210

    Joined:
    Oct 14, 2012
    Posts:
    131
    Hi

    Coming back to this problem again...which I'm studying. Imagine there are two scripts attached to two different game objects in a scene. All I need to do is for script A to get the value of variable from script B (in order to use it for something).

    In this example the variable I need is called "pressure", and the script I'm getting it from is called "myScript". For simplicity, I'm just outputting the value to the console.

    Is this the correct approach?

    Code (CSharp):
    1.  
    2.  
    3. public GameObject myGameObject;
    4. myScript myScriptReference;
    5.  
    6. void Start()
    7. {
    8.     myScriptReference = myGameObject.GetComponent<myScript>();
    9. }
    10.  
    11. void Update()
    12. {
    13.     Debug.Log("Pressure = " + myScriptReference.pressure;);
    14. }
    15.  
    16.  
    It seems a bit verbose. Also, I am choosing the gameObject I need to reference in the inspector...which is tedious for me and could break or easily lead to a null reference error. Is there way to choose the gameObject in the script itself?

    Thanks!
     
    Last edited: Mar 27, 2018
  2. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Here's a couple other options

    Code (CSharp):
    1.  
    2.  
    3. public myScript myScriptReference;
    4.  
    5. void Start()
    6. {
    7. }
    8.  
    9. void Update()
    10. {
    11.     Debug.Log("Pressure = " + myScriptReference.pressure;);
    12. }
    13.  
    14.  
    In the above you just drag that same GameObject into the myScriptReference and it will automatically set the reference to the correct script, assuming you only have one of that script attached to the object. You could also drag the component over, but I think you'd need to set up two inspector windows and lock one of them, which is a hassle if you're not doing that a lot.

    The advantage here is this is slightly better performance wise than your version (start is simply empty) and the code is as simplified as it can be. The disadvantage is you still have to remember to set the reference in the inspector just the same as before.

    Code (CSharp):
    1.  
    2.  
    3. GameObject myGameObject;
    4. myScript myScriptReference;
    5.  
    6. void Start()
    7. {
    8.     myGameObject = GameObject.FindGameObjectWithTag("myGameObject");
    9.     if (myGameObject != null)
    10.     {
    11.         myScriptReference = myGameObject.GetComponent<myScript>();
    12.     }
    13.     else
    14.     {
    15.         Debug.Log("myGameObject does not exist");
    16.     }
    17. }
    18.  
    19. void Update()
    20. {
    21.     Debug.Log("Pressure = " + myScriptReference.pressure;);
    22. }
    23.  
    24.  
    In the above you set your myGameObject with the tag "myGameObject", which is done at the top of the inspector. Then this script will automatically find it during Start. This is useful in cases where either of these two GameObjects is instantiated instead of existing in the scene, or are part of 2 separate scenes, which would prevent creating references by dragging to the inspector.

    The advantage here is you aren't creating any inspector references. The disadvantage is you have to set up a tag, and have the correct spelling and capitalization in the Find call, and there is no autocomplete for these in visual studio. This also is the worst performing solution, as the Find call added to the GetComponent call both take a small amount of time, but that isn't usually a big deal unless you are doing these a lot in your game (don't make the noobie mistake of putting those calls in Update, or instantiating hundreds of objects with these calls in their Start).
     
    Last edited: Mar 27, 2018
    Jason210 and astracat111 like this.
  3. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    You can also do soemthing like this...

    In your hierarchy in the editor have it be like:

    Scene
    -- children of scene gameobject below --
    Object1
    Object2
    OtherObjectsHere

    Attach a scene.cs or whatever script to your Scene game object in your scene.

    Code (CSharp):
    1.  
    2. //Scene cs script
    3. public class Scene_Script : Monobehaviour {
    4.   public static List<GameObject> Scene_GameObjects = new List<GameObject>();
    5.  
    6.   Start() {
    7.     foreach (Transform child in this.gameObject.transform) {
    8.        Scene_GameObjects.add(child.gameObject);
    9.     }
    10.   }
    11.  
    12.     public static GetGameObject(string name) {
    13.        foreach (GameObject gameObject in Scene_GameObjects) {
    14.          if (name == gameObject.name) {
    15.            return gameObject;
    16.          }
    17.        Debug.LogError("Couldn't find gameObject in Scene_GameObjects list with name of ''" + name + "''!");
    18.        return null;
    19.     }
    20.   }
    21. }
    Go into execution order in Unity and make sure this scene.cs script is the first one that initializes in your scene.

    Now in your game object's script:

    Code (CSharp):
    1.  
    2. public class GO_Base : Monobehaviour {
    3.    public Scene_Script scene_Script;
    4.  
    5.    Start() {
    6.       //You can find the gameobject you now need like
    7.       GameObject theGameObjectInTheScene_I_Need = scene_Script.GetGameObject("myGameObjectName"); //You can call the scene scripts method like this.
    8.     }
    9.  
    10.    Update() {
    11.       theGameObjectInTheScene_I_Need.hp = 100; //or whatever you need
    12.    }
    13.  
    14. }
    15.  
    Untested code, but this is generally what I do...not sure if it's the most efficient though, but running through a list is fast.

    You then get your null error which you can instantly read and know what object name was found in your scene object list.
     
  4. Jason210

    Jason210

    Joined:
    Oct 14, 2012
    Posts:
    131
    @Joe-Censored, post: 3439726

    Thanks. I can see the advantage of the first option. It still requires a single drag and drop but is less code and uses fewer references. It seems to be the simplest and fastest alternative performance-wise.
     
    Last edited: Mar 27, 2018
  5. FirstTimeCreator

    FirstTimeCreator

    Joined:
    Sep 28, 2016
    Posts:
    768
    As long as you aren't using GameObject.Find your good :)
     
  6. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    But in the beginning of a scene GameObject.Find is fine, just don't use it in an update. What might be a good method is having, then, a scene script at the beginning of the scene while a loading screen is displayed create the lists for someone's game objects. List<GameObject> Scene_NPC_GameObjects that stores only your game objects tagged with 'npc' or whatever. That way you can have a super fast way of finding an npc.
     
  7. FirstTimeCreator

    FirstTimeCreator

    Joined:
    Sep 28, 2016
    Posts:
    768

    Anywhere there is gameobject.find you should make a global controller to hold those related classes and drop the class in instead of using that in start()... the more you game grows in complexity the more you realize it is a better way of doing things.
     
  8. astracat111

    astracat111

    Joined:
    Sep 21, 2016
    Posts:
    725
    @FristTimeCreator you're probably right, personally I've never dealt with scenes with over say 10,000 game objects, I'd imagine that would be the better method especially if one practices streaming infinite objects the whole list thing wouldn't work...