What is "right way" to call method from another script?

Hey everyone.
I’m fairly new to both Unity and programming. Recently I finished my first project, which was based on a course. In the last few days I’ve been trying to understand how things work, by doing some changes on the code and adding some features the course didn’t cover.
So, I’m having a hard time trying to figure out how to call methods that are from another script. I did some research and I made it work in some cases, although not really having a full understanding why it really works.

Say we have ScriptA and ScriptB, and in ScriptA I wanna call a Method from ScriptB.

public class ScriptB
{
   public void DoSomething()
   {
      Debug.Log("Method from script B was called");
   }
}

After some extensive research and a lot of tests I ended up with 3 different solutions.

Solution 1:
Attach both scripts to the same object, lets call it ObjectA. In this case, ScriptA will look like this:

public class ScriptA;
{
    private ScriptB scriptB;
    void Start()
    {
        scriptB = GetComponent<ScriptB>;
    }
    void Update()
    {
        scriptB.DoSomething();
    }
}

Here I got a little confused. Why I have to “GetComponent”? I mean, isn’t the variable scriptB a type from the class ScriptB? Why I can’t just invoke the method without this line of code? Ok, moving on.

Solution 2:
Attach the ScriptA to ObjectA, and ScriptB to ObjectB. Here, i have to make the field scriptB public, so in the inspector I can put ObjectB inside this field.

public class ScriptA;
{
    public ScriptB scriptB;

    void Update()
    {
        scriptB.DoSomething();
    }
}

In this case, I can invoke the method in a direct way.

Solution 3:
Solution 3 is using a Tag on ObjectB (lets call the tag just “B”), then we find the ObjectB in the script A.

public class ScriptA;
{
    private ScriptB scriptB;
    void Start()
    {
        scriptB = GameObject.FindGameObjectWithTag("B").GetComponent<ScriptB>;
    }
    void Update()
    {
        scriptB.DoSomething();
    }
}

And if there’s some other approach that I missed, I would be glad to know about.
Since I’m very tired (6 AM here), theres a pretty good chance I made some mistakes when typing the code. Also, my English is pretty bad, but I tried my best. Feel free to ask me anything.
Really appreciate any help!

1 Like

If you dig deeper in the coding stuff, you will find that the type of variable isn’t equal to the content of the variable.
You are defining a variable which CAN contain an object type ScriptB. But you can have many ScriptB in your project. Even if we narrow it down to MonoBehaviours, you can have ScriptB on every single game object in your hierarchy. You need to tell to the compiler which one you want to use (if it isn’t MonoBehaviour, you can create a new one to store in there too).
So the GetComponent line tells the compiler which ScriptB you want to store in there.

You do not. You just replaced the GetComponent line with dragging and dropping in the inspector. They mean (almost) the same thing. If you’re working with game objects you can always choose between GetComponent and dragging and dropping a reference into the variable but you always will need one or the other.

It is exactly the same as the first option, you just need to find the game object first, since it is not the current one. Try to separate them in your mind.
GameObject.FindGameObjectWithTag("B") is one operation GetComponent<ScriptB>() is another. If you want, you can do this with your current game object as well. But it isn’t needed since you already have a reference to the current game object built in.

There is no “right way” to call a method from another object. There is only one way: .methodName(); And that’s it.
How you can acquire that reference is a question. You can use either of these methods you listed here and you will be fine. You will find, if you create game objects and/or components from scripts though, the drag and drop method will be out of the question.

4 Likes

Solution 1 is okay as long as you ensure that the component is present (and that component can only be added once or it doesn’t matter which of them you take). Add [RequireComponent(typeof(ScriptB))] above the class declaration of ScriptA. To your question: You need to call GetComponent<ScriptB>() here because the declared variable has no value yet (it has the default value null before).

Solution 2 is okay, but I would not expose the variable publicly. It is good practice to hide as much as possible and expose things only through properties. Add a [SerializeField] Attribute in front of ScriptB scriptB (and remove public / replace with private).
Also, you should make sure that the script has been assigned. Either you require it (I usually put a Debug.Assert(scriptB != null); in Awake() or Start() or OnEnable()) - or - you only call this method if it the object was assigned. You can do this easily with the ?-operator like this: scriptB?.DoSomething().

Solution 3 is not okay. It relies on so many things that have to be configured inside Unity that it is likely something was missed: The object must be tagged with “B”, the object must have a ScriptB-component attached, you must not misspell “B”.
I use tags and FindObjectByName only for rapid prototyping. Never for production.

Yes: your scripts do not derive from MonoBehaviour. With normal classes your questions do not make much sense. Also you put a semicolon after the class declarations of ScriptA which is not allowed (compile error).

2 Likes

I think you are on the right track and I applaud you for making the effort to investigate your options by actually writing code and running it. Here’s a link to a video I made a while back that addresses this topic:

1 Like

Hello, again.
First, I wanna thanks for your answers, they were really helpful.

@
Your explanation was very insightful, it really cleared things up for me. So it’s all about references, and how you get them. I see now how all the solutions get a reference and point to other object. Very good observations, thanks for the help!
@Hosnkobf
Very concise explanation. I also like how you point the good practices. Although I am new to programming, one thing I hear a lot is the importance of good practices. I didnt know about much of the code you mentioned, but for sure it has a great value. Thanks for sharing this knowledge.
Man, I really forgot about the MonoBehaviour. I typed the code in notepad++, as an example, that was not the actual code I was testing in my project, but thanks for pointing that out. And about the semicolons… I just keep doing this all the time, I see parenthesis and it is automatic D:
@Stevens-R-Miller
Thanks for the words, man. I always try to understand what happen “behind the scenes” instead of just asking for a solution. I watched your video, sadly I did not find it before, it is very well explained. Right now I’m taking a look in the Polymorphism video, good stuff.

Glad you liked it enough to watch another. Let me know if there are other topics you’d like me to cover.