简体   繁体   中英

Unity/C# - Referencing a method on another object

I decided to have a play with Unity a couple of days ago and despite being an old hand at programming, both Unity and C# are completely new to me. I'm working in 2D.

I've created a little game to try and learn the engine, which (uniquely) involves firing bullets at aliens. I'm using the bullet object to detect when it hits something (and destroy itself), and in the cases where it hits something that needs to react - the target object to decide what its response should be.

Here's the relevant part of BulletController which is attached to each instance of the bullet:

    // Check to see if the bullet is colliding with anything.
void OnCollisionEnter2D(Collision2D col)
{
    if (col.gameObject.tag == "Terrain") {
        this.audio.Play ();
        int smokeIndex = 0;
        GameObject smoke = (GameObject) Instantiate(bulletSmoke[smokeIndex],transform.position,transform.rotation); 
        Destroy (smoke, 5f);
        Destroy (gameObject,0.05f);
    }

    if (col.gameObject.tag == "Enemy") { 
        col.gameObject.GetComponent<BasicAlienController>().TakeHit (1,1);
    }

}

And for completeness, the (placeholder) part from BasicAlienController :

public void TakeHit(int damageType, int damageAmount) {
    print ("Taking hits");
}

I had originally typed up a massive question on how to call TakeHit on the alien's script, but after a fair bit of trial and error, figured it out to the above, it works well but I don't feel great about how it's written. It doesn't seem right to blindly believe that there's a BasicAlienController on each enemy, because later on, I might extend that with alien-specific controllers so they can react in different ways. It also doesn't seem right to blindly believe that there is also a TakeHit() method available on everything tagged as being 'Alien', I mean, I guess there should be, but it might not always be the case.

So this is a multi part question:

  1. How can I make sure I make sure I call the relevant controller based on the enemy type, if I have (for example) FlyingAlienController and WalkingAlienController without having to specifically query the name of the object and then do a potentially big switch? If I can only have one script attached to a GameObject then why can't I just reference 'the script', whatever its name is?

  2. How can I check that there is a TakeHit() method available, or is it just up to me to make sure I always expose that method on anything that's tagged as being 'Alien'?

  3. Bonus question, what's the deal with the < > around the controller name, I'm new to C# and haven't seen that syntax before, why wouldn't it just have been gameObject.GetComponent("BasicAlienController") or similar?

1. How can I make sure I make sure I call the relevant controller based on the enemy type, if I have (for example) FlyingAlienController and WalkingAlienController without having to specifically query the name of the object and then do a potentially big switch? If I can only have one script attached to a GameObject then why can't I just reference 'the script', whatever its name is?

You can actually have as many scripts as you want on a GameObject. My suggestion is to make an interface that each of your controllers implement then use "SendMessage". There's a performance hit with SendMessage, but for this situation I think it's the right choice.

http://docs.unity3d.com/ScriptReference/GameObject.SendMessage.html

Unfortunately you can't strict type that call, but it'll do. You code might look something like this:

BasicAlienController:

public class BasicAlienController : MonoBehavior, ITakeHit
{
    // It can only take a single input
    public void TakeHit(object args) 
    {
        // Logic here
        print ("Taking hits in BasicAlienController ");
    }
}

FlyingAlienController :

public class FlyingAlienController : MonoBehavior, ITakeHit
{
    // It can only take a single input
    public void TakeHit(object args) 
    {
        // Logic here
        print ("Taking hits in FlyingAlienController ");
    }
}

BulletController :

// Check to see if the bullet is colliding with anything.
void OnCollisionEnter2D(Collision2D col)
{
    if (col.gameObject.tag == "Terrain") 
    {
        this.audio.Play ();
        int smokeIndex = 0;
        GameObject smoke = (GameObject) Instantiate(bulletSmoke[smokeIndex],transform.position,transform.rotation); 
        Destroy (smoke, 5f);
        Destroy (gameObject,0.05f);
    }

    if (col.gameObject.tag == "Enemy") 
    { 
        int[] hit = new int[] {1, 1};
        col.gameObject.SendMessage("TakeHit", hit);
        // If you don't want to require a receiver
        // col.gameObject.SendMessage("TakeHit", hit, SendMessageOptions.DontRequireReceiver);
    }
}

3. Bonus question, what's the deal with the < > around the controller name, I'm new to C# and haven't seen that syntax before, why wouldn't it just have been gameObject.GetComponent("BasicAlienController") or similar? C# generic. See Andy's link.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM