How to call a method from another script using a string?
Hello,
I have two scripts (A and B). Script A calls a method from script B. Normally I would have used:
FindObjectOfType<ScriptB>().methodName();
The thing is that ScriptB doesn't always have the same name and I need a way to dynamically call this method. My understanding from other threads is that a combination between passing the type as a string and send message with the method name should solve this problem.
The script looks like this:
string scriptName = (SceneManager.GetActiveScene().name.Split('_')[2] + "Controller");
scriptName = char.ToUpper(scriptName[0]) + scriptName.Substring(1);
string methodName = "CheckAnswer";
FindObjectOfType(System.Type.GetType(scriptName)).SendMessage(methodName);
The part where I'm stuck is SendMessage(methodName) as SendMessage is not available for Object...
Really appreciate any help.
Answer by Commoble · Mar 11, 2017 at 01:05 AM
Correct me if I'm wrong, but if I'm reading your problem right, these are your issues:
You have several unrelated script types, each with a special function on them, and the function is the same name on each of these scripts, and you want to be able to call this function from Script A, regardless of which type of script Script B is.
You need to get a reference to Script B or the script's gameobject, regardless of which type Script B is.
To solve (1): In this situation, the first thing I'd do is use class inheritance to make each script class a subtype of the same base class. For example, if these are scripts for different enemy types, you can make each script class inherit from an Enemy class (that itself inherits from MonoBehaviour). Then, you can take the function shared by the two scripts, and put it in Enemy. So now you get a reference to an Enemy script, and you can call that function regardless of which subtype of Enemy you have.
Now sometimes, your scripts are just too different to use object inheritance like this. In this case (and only after carefully considering whether your setup can be refactored to use inheritance like this), THEN SendMessage becomes useful. SendMessage allows you to invoke a function by string in any script on a given GameObject, and BroadcastMessage also invokes that function in every script on every child object in that GameObject's hierarchy (this can get very slow if the object has lots of children).
In either case, though, what we need in the first place is a reference to the gameobject or the script on it. These are mostly the same; if you have a variable someScript
containing a reference to a script (or any component class), you can get its gameobject via someScript.gameObject
. If you have a reference to a gameobject, you can get a reference to a script on it with someObject.GetComponent<ScriptType>()
(and if you have to use SendMessage and have the gameobject reference, you don't need a reference to the script itself).
So now we're at (2) -- getting a reference. By far the best way to reference a script or gameobject from another script is to link them directly in the scene editor. This is possible when both objects are part of the scene asset; i.e. you can see both of them in the scene editor, neither is instantiated during runtime. In this case, you can link one object to another by going to your Script A and declaring public GameObject otherObject;
or public ScriptType otherScript;
, which will create a field in Script A's inspector when it's a component on an object. You can drag a gameobject or relevant script to that field to link it to the Script A component, and that public variable in the script's code will be filled out by Unity for you, allowing you to reference that object or script directly without searching by name.
On the other hand, if either or both of your objects are being instantiated at runtime (and HAVE to be instantiated at runtime), then this gets a bit trickier, because we can't link them in the scene editor. At this point, my thought process is usually something like this:
Do the objects interact via collisions, triggers, raycasts, or similar interactions? These all grant the interacting objects references to each other.
If not, then can I use the scripts that instantiated Script A and Script B's objects to pass references to them? Call them A Creator and B Creator. If these have a reference to each other or interact with each other, they may be able to pass information about Script A and B when Script A and B are created (if neither script A nor B are created during runtime, then this is irrelevant because you can link them directly, as described earlier).
If not, then do I have a script that can be referenced globally without searching by string (I won't go into how to do this here), and is Script A or B important enough for that global script to have a reference to it?
Finally, if none of these apply, then I'll search for the object by string, preferably by tag rather than by name.
From here, you have a direct reference, and you can call the function on the script or call SendMessage on the gameobject.
This got a bit long-winded, but the point I'm trying to make here is that it's only very, very rarely that you actually need to search for objects by string to call functions in their scripts. Sometimes you can solve a complicated problem by throwing it out and solving a simple problem instead, once you're aware of the simpler problem's existence.
If nothing in this answer applies to you, we'll need to know the context in much deeper detail.
Hi Commoble,
First let me thank you for taking the time writing this great answer.
After carefully analyzing your input and looking over the way my game is structured, the best solution would have been to use inheritance from the start. This would have solved not only this problem, but countless others I encountered during development.
At this point the game is almost complete and it will take way too much time to refactor the code and implement inheritance, so I will take your other provided solution: search by tag and do a Send$$anonymous$$essage on the gameobject. GameObject.FindGameObjectWithTag("LevelControllerTag").GetComponent<$$anonymous$$onoBehaviour>().Send$$anonymous$$essage(methodName);
I will definitely use inheritance in my next project or maybe even refactor this one if it will prove feasible.
Again, thanks a lot for the great answer. I actually learned a lot from it.
If you have the gameobject and you're using Send$$anonymous$$essage, you don't need to use GetComponent. Send$$anonymous$$essage applies to the gameobject itself, and if you invoke it on a component, it'll still apply to the whole gameobject, not just the component.
Your answer
![](https://koobas.hobune.stream/wayback/20220612112900im_/https://answers.unity.com/themes/thub/images/avi.jpg)