(C#/OOP) Accessing one public function belonging to multiple gameObjects?

I’m facing a bit of an OOP dilemma and would greatly appreciate any help.

My current situation is this:
I’m designing a horizontal SHMUP and the player’s ship shoots lasers. I’ve given the lasers collision detection so that when they collide with anything tagged as “Enemy,” the player’s score is increased and both the enemy and laser are destroyed.

I’ve since decided to add a second enemy, and that’s where I’ve encountered problems. Rather than copy–paste the code from the first enemy’s script, I decided to muck around with OOP by creating a brand new script for the second enemy and having it inherit everything from the first one. In other words, the second enemy’s script is now a child to the first enemy’s.

The problem I’m having is related to both enemies sharing identical functions, namely the “getValue” function, which is a public function that returns an integer storing how much the enemy is worth when destroyed. (This is especially true since I’ve made the second enemy worth more than the first one.)

The laser calls the “getValue” function when it makes contact with enemies. My plan is to have the laser (or rather its script) do most of the dirty work by calling public functions belonging to other gameObjects. For example, it grabs the score from the enemy’s “getValue” script, then increases the player’s score by calling an “increaseScore” function belonging to the player’s ship’s script.

Here’s an example of the laser’s code in which it calls the enemy’s “getValue” function:

    void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.CompareTag("Enemy"))
        {
            float score;

            // Get how many points the enemy is worth
           score = other.gameObject.GetComponent<Enemy1Script>().getValue();

            // Give the player as many points as the enemy is worth
            player.GetComponent<PlayerScript>().increaseScore(score);

            // Destroy both the enemy and laser
            Destroy(other.gameObject);
            Destroy(this.gameObject);
        }
    }

My specific problem is this: How am I supposed to make the code on line 7 (score = . . .) also apply to the second enemy if I must specifically type the name of the enemy’s script in GetComponent? The first enemy’s script is named Enemy1Script and the second enemy’s is Enemy2Script, but I can only enter one, even despite the fact that both scripts share the same “getValue” function! In other words, I want to access the “getValue” function regardless which enemy (or enemy’s script) it belongs to; but I seemingly can’t.

I understand that I could use a series of If–Then statements to take different actions based on the gameObject’s name. For example, if its name were Enemy1, Enemy1Script’s “getValue” would be accessed via GetComponent. If it were Enemy 2, then Enemy2Script’s “getValue” would be accessed. But to me that seems like a long-winded way of doing it and would, at least in my opinion, almost defeat the purpose of using tags!

Is there any other way to get around this besides a series of If–Then statements like I described? Am I perhaps going about this the wrong way by having the lasers handle everything by calling public functions themselves?

Again, I would greatly appreciate any help. Thank you in advance!

What you are after is polymorphism:

public class Parent  : MonoBehaviour{
     public virtual MyMethod(){ print("Parent")}
}
public class Sub: Parent{
    public override MyMethod(){print("Child");}
}

public class Manager:MonoBehaviour{
     public Parent [] objects;
     void Start(){
         foreach(Parent p in parents){
                p.MyMethod();
         }
    }
}

To understand the process, create an object with Parent script and one with Sub script. Then drag both in the Parent array. Press play and enjoy.

I won’t go into details since the net has all you need. Again, polymorphism.

There’s two solutions here:

  • Create a common base class named Enemy. It should be abstract (ie. can’t be implemented directly). Then let your different enemies inherit from Enemy. So you’d have

    public abstract class Enemy : Monobehaviour {

     //all the behaviour you used to have in the first Enemy
    
     public abstract int getValue(); //your subclasses will have to override this
    

    }

Then when you implement the actual enemies, you let them inherit from Enemy:

public class SmallEnemy : Enemy {
    public override int getValue() {
        return 1;
    }
}

public class LargeEnemy : Enemy {
    public override int getValue() {
        return 5;
    }
}

Now when you do GetComponent, it will find either the SmallEnemy or the LargeEnemy script, and use that script’s getValue to figure out the value

  • Just have the one enemy class. Expose the amount of points in the inspector. Create more enemies by creating prefabs with different sprites and different values in the points field.

This is the most “Unity” way of doing this thing, and is often a bunch easier to implement. When you do this, you should split the different behaviours of an enemy into different scripts - so you have one health script and one move/attack script on the same prefab. Then you create new enemies by mixing and matching those scripts into new enemies.