- Home /
How can I access a function without knowing the script/class name to which it belongs?
Here is the situation. (This situation is purely hypothetical for now, I'm explaining the situation so you can tell me the best approach before I attempt to write the code).
The game isn't exactly a beat-em-up; but for the purposes of this example, you can think of it like a beat-em-up for now.
I have a hundred different character prefabs that each of which needs to have a script attached to it with some kind of "Attack" function. The function will include different code depending on which character prefab it is attached to. So for example, one character might swing a sword when the attack function is called, whereas another character might shoot a bow.
It is a multiplayer game, and so, each player picks a character in a menu screen, and afterwards battle commences. Depending on which characters were selected, a prefab of each character is instantiated and assigned to a player at the beginning of combat.
Separate to all of this I have an InputManager script that is checking what inputs have been pressed by either player and then needs to call the attack function on that player's character accordingly.
Is there an easy way to do this without having to have a massive 100-long "switch/case" statement on a single script which the InputManager attempts to access? The moment that I try to separate out the attack function so that is inside of multiple scripts/classes I get the problem of finding a way of identifying the class to which the attack function belongs (at least as far as I'm aware). Because different players can pick the same character I can't really have some static function for each of the characters different attacks (again as far as I'm aware).
My question is this; how would you go about solving this kind of issue? (I'm aware I might be thinking about it the wrong way, so any suggestions are welcome).
P.S. I will be using C#, although javascript solutions are also welcome.
It would be ideal if there was a way of accessing a function on "some" script that has been assigned to a GameObject without having to specify which script it is (so long as only one script on the object has that function). But I don't know if that's possible / how to do that.
Answer by aldonaletto · Jun 09, 2013 at 02:33 PM
A good solution is to use SendMessage("Attack") - the function Attack is called by name, thus you don't have to worry about the script name. This is used in the good (but unfortunately no more available) FPS Tutorial: the function "Fire" is called via SendMessage in the weapon script, no matter which weapon is select. If you want to call a function by name in another object, just prefix SendMessage with a reference to the object:
somePlayer.SendMessage("Attack");
If Attack is in a script attached to a child of somePlayer, use BroadcastMessage instead: this version calls the specified function in any script down in the hierarchy.
That works perfectly! And so simple! Thanks loads.
And the Broadcast$$anonymous$$essage allows for parameters as well! Is there any cons to using either Send$$anonymous$$essage or Broadcast$$anonymous$$essage that I should be aware of?
Send$$anonymous$$essage/Broadcast$$anonymous$$essage/Send$$anonymous$$essageUpwards are said to be a little slow, but this is true only when compared to direct calls. They are fast enough for most purposes, specially when called in response to user input. The FPS Tutorial used this function to fire attacks (like you want to do), and also to apply damage - objects, enemies and the player had an ApplyDamage function which was called via Send$$anonymous$$essage. There were situations in this FPS Tutorial where dozens of Send$$anonymous$$essage/Broadcast$$anonymous$$essage functions were called each frame without causing any perceptible performance drop.
Answer by Nanorock · Jun 09, 2013 at 01:22 PM
This is a design issue. You should have all your script sharing the same interface.
class MyPlayer_1_Script : MonoBehaviour , PlayerScript
{
public void attack(){/*You attack specific to player 1 goes here*/}
blabla
}
class MyPlayer_2_Script : MonoBehaviour , PlayerScript
{
public void attack(){/*You attack specific to player 2 goes here*/}
blabla
}
interface PlayerScript
{
void attack();
}
etc.
In this case all you would need to grab is PlayerScript myPlayer = "your selected Player" , and input would just have to call myPlayer.attack();
Now if you'd like to directly call that just knowing the gameObject, you would use a MonoBehavior base class like so:
class MyPlayer_1_Script : PlayerScript
{
public override void attack(){/*You attack specific to player 1 goes here*/}
blabla
}
class MyPlayer_2_Script : PlayerScript
{
public override void attack(){/*You attack specific to player 2 goes here*/}
blabla
}
class PlayerScript : MonoBehavior
{
public virtual void attack(){}
}
You would then with your gameObject simply do:
PlayerScript player = myGameObject.GetComponent<PlayerScript>();
player.attack();
thanks very much, this sounds like the right approach. I'll give em a try and post up again here if there's any issues.
I'm still having a little trouble with this. Using the interface method, half the problem is the bit which says PlayerScript myPlayer = "your selected player". I currently don't have a way of Unity working out which class/script it needs to be looking at. Anyway, I think you pre-empted that with your second solution, so onto that one.
The problem I'm getting with the second solution is an error with the abstract.
Specifically:
The modifier 'abstract' is not valid for this item.
Any ideas why that could be happening?
I just saw that abstract is not valid for $$anonymous$$onoBehaviour class. Use virtual ins$$anonymous$$d, I edit my answer to reflect that