Search Unity

Calling a function in another script ?

Discussion in 'Scripting' started by T4NK32, Aug 19, 2019.

  1. T4NK32

    T4NK32

    Joined:
    Aug 7, 2019
    Posts:
    16
    I have a GameObject called Ctrl - under that (for now) 2 MonoBehaviour-scripts: Spawner and LifeManager (That's 2 script-components - not visible (dragable) in Hierarchy).

    Now I want call a function in one of them, from a script in another GameObject called Enemy.

    How do I get a "hook" I can use to get access to LifeManager from Enemy?
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    There are tons of ways to get references to other objects depending on your specific situation. I've listed a lot of them in this article (you can ignore the first little section, which is persuading people that GameObject.Find is bad, and just proceed to the examples. From the limited information you've given here, I think that the Singleton option might be what you want in this situation (make sure you read the warnings and caveats about it first, it's really easy to fall into the trap of overusing singletons), but try out any of them that seem to make sense for what you're trying to do.
     
    T4NK32 likes this.
  3. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    There are two good ways. One, in 'Enemy" you could make a field that is exposed in to the inspector that takes a "LifeManager". And then simply drag the GameObject that has the "LifeManager" in to that field.
    Or if there are is only going to be one "LifeManager" you could make it what is called a Singleton pattern.
    Which looks like this.

    Code (CSharp):
    1. public class LifeManager : MonoBehaviour
    2. {
    3. public static LifeManager _instance;
    4.     public static LifeManager Instance
    5.     {
    6.          get {
    7.              if (_instance == null)
    8.              {
    9.                  _instance = GameObject.FindObjectOfType<LifeManager>();
    10.            
    11.                  if (_instance == null)
    12.                  {
    13.                      GameObject container = new GameObject("Life Manager");
    14.                      _instance = container.AddComponent<LifeManager>();
    15.                 }
    16.              }
    17.    
    18.              return _instance;
    19.          }
    20.     }
    21.    
    22.     // Your other code for LifeManager here.
    23. }
    Then you would call it like so
    Code (CSharp):
    1. LifeManager.Instance.MyLifeManagerMethodOrVariable;
     
    T4NK32 likes this.
  4. T4NK32

    T4NK32

    Joined:
    Aug 7, 2019
    Posts:
    16
    Never mind, I found an example using Find in the awake function of Enemy

    My problem was that Emeny is a prefab = Won't take an actual reference (if I understand correctly?)
     
    Last edited: Aug 19, 2019
  5. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    When you instantiate it, store a reference to the object you created.
     
  6. T4NK32

    T4NK32

    Joined:
    Aug 7, 2019
    Posts:
    16
    It seems impossible!

    I'm trying to create a static class to store the pointers in, but either:

    A class derived from type 'MonoBehaviour can't be static.

    Or (if I remove the ": MonoBehaviour") the compiler refuses to load the script. "Behaviour missing"
     
  7. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    You don't make the class itself static, just the reference to the instance of it is static.
     
    Joe-Censored likes this.
  8. T4NK32

    T4NK32

    Joined:
    Aug 7, 2019
    Posts:
    16
    But how can I get the reference (from the prefab) to the class storing the statics, it the class isn't static?

    Without using GameObject.Find - which really is visibly slow my (tower defense) enemies are born with large gaps inbetween : (
     
  9. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    I'm having a hard time understanding which part you're not understanding.

    You put the singleton code (as seen on @MechaWolf99's post) on the object that you have one copy of, and now that object can be referenced anywhere. The class isn't static - there still exists an instance of the class in the scene - but the class does have a static reference to that object. And because the class is public, you can do this on code that's on the prefab:
    Code (csharp):
    1. LifeManager.instance.SomeFunction();
    The "LifeManager.instance" is how you get the reference from the prefab (or anywhere). LifeManager is the class that "stores the statics".
     
  10. T4NK32

    T4NK32

    Joined:
    Aug 7, 2019
    Posts:
    16
    THANK YOU - Finally it works - sorry if I'm a bit thick : )

    So, I'm actually creating a new temporary copy of the class (? it doesn't pop up in the Hierarchy while playing..)

    And another thing; the code in LifeManagers Start() is never run..?

    It must be happening at a very early stage - before any values have been fetched from the Inspector.. But I can call the Start() function from another script using LifeManager.Instance.Start() - I'm putting that in the Ctrl "folder" - ALL problems solved (so long as I only have one scene...)
     
    Last edited: Aug 20, 2019
  11. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    It's Start(), not start(). Capitalization matters. :)
     
  12. T4NK32

    T4NK32

    Joined:
    Aug 7, 2019
    Posts:
    16
    Fixed :oops:

    Now that I have a little more "foundation" I've been reading your Regarding GameObject.Find and found that I can replace the above "singleton pattern" with this (much shorter and more obvious):

    using UnityEngine;
    public class LifeManager : MonoBehaviour
    {
    public static LifeManager Instance;
    void OnEnable() { Instance = this; }
    ...


    It seems to work just the same(?), but also has the Start()-is-never-run problem.

    Is there a way to make sure it's executed in a timely fashion, without relying on an "external push" - It would be nice to be able to make it an altogether local afair...

    EDIT:
    It was my own fault again: I spelled the LifeManager.Start() function with lowercase s so I had to call it "manually" : (
     
    Last edited: Sep 17, 2019
  13. athgen113

    athgen113

    Joined:
    May 25, 2018
    Posts:
    46