Inheritance and polymorphism : Accessing specific subclass of enemies

Hi All,

I have a problem (and being a programmer, even if quite new to unity3d, i feel quite dumb about it) and i couldn’t find a solution to it even after searching widely on the web.

I have a base class Enemy containing all the basic functions of an enemy, and i want now to specialize the class for example into Footman, Militia, etc, etc… I wrote a class Enemy that extends the MonoBehavior and everything works fine. I wrote a class Militia that extends Enemy and one named Footman that also extends Enemy. So far still fine.

Now my tower, gets an enemy in range and wants to shoot it, doesn’t matter if it’s a Footman or a Militiaman. Usually the tower, after all the proper selections of the enemy it wants to shoot, calls currentEnemy.getComponent() to access the soldier script and deal damage or whatever. Now how should I manage that in case of different enemy derived classes? I could add a tons of lines of code with checks like “if enemy is type of X, then call X, otherwise call Y”, etc. But isn’t there a somehow smarter and faster way to manage that? To make a stupid example, like to cast the Militia into an Enemy again to access the basic functions (DealDamage(), SlowDown(), etc?)

Thanks in advance,
Best,
H

If I understand correctly your problem you could use virtual functions:

public class Enemy : MonoBehaviour {

	public virtual void SlowDown()
	{
		// Write code for generic slowdown
	}

}

Then, on Footman class:

public class Footman : Enemy {
  
	// if you want special treatment for this soldier, override the virtual function

	public override void SlowDown()
	{
	// special code for this class
	}
}

Hi Jvil, thanks for helping again. Actually no, i already managed everything with virtual and overriding stuff, my problem is how to call the script for the soldier from the tower.

Before i was calling it like target.getComponent(). isn’t there a way to generalize to avoid having to distinguish among target.getComponent(), target.getComponent(), etc?

H

just reading here, i don’t know exaclty what you are talking about, and i’m a learning programmer, but i just thought… why not use a TAG?

Hi Gabo, i think that would be actually a quick solution, but i wanted to find out if there was a “smarter” (don’t get me wrong with this) way to do it, to avoid having to write several if()else() to find out which class type to call.

have you tried to use a raycast, raycasthit and sendMessage?
you can use RaycastHit to determine what you hit.

RaycastHIt hit;
if(physics.Raycast(pos, forward, out hit, 100))
    hit.collider.SendMessage("DealDamage",50,/* does not require receiver message*/);

that way you can directly call the the function of whatever you hit, so it can take damage appropriately.
Edit:
Also, in the subclassed function, override the function accordingly, for example militia’s OnDeath() function would be different from footsoldiers OnDeath() function.

what about adding the proper class name to the gameobject name then? then you parse the name to see wich class to acces.??

Here is a class full of extension methods that I use to access components on gameobjects in a more OOP/C# style.

It includes support for accesssing components as interfaces.

using UnityEngine;
using System.Collections.Generic;

namespace com.spacepuppy.Utils
{
	public static class ComponentUtil
	{

        #region Component Methods

        public static bool IsComponentSource(object obj)
        {
            return (obj is GameObject || obj is Component);
        }

        #region HasComponent

        public static bool HasComponent<T>(this GameObject obj) where T : Component
        {
            return (obj.GetComponent<T>() != null);
        }
        public static bool HasComponent<T>(this Component obj) where T : Component
        {
            return (obj.GetComponent<T>() != null);
        }

        public static bool HasComponent(this GameObject obj, string tp)
        {
            return (obj.GetComponent(tp) != null);
        }
        public static bool HasComponent(this Component obj, string tp)
        {
            return (obj.GetComponent(tp) != null);
        }

        public static bool HasComponent(this GameObject obj, System.Type tp)
        {
            return (obj.GetComponent(tp) != null);
        }
        public static bool HasComponent(this Component obj, System.Type tp)
        {
            return (obj.GetComponent(tp) != null);
        }

        public static bool HasComponentOfInterface<T>(this GameObject obj)
        {
            foreach (var comp in obj.GetComponents<Component>())
            {
                if (comp is T) return true;
            }

            return false;
        }

        public static bool HasComponentOfInterface<T>(this Component obj)
        {
            foreach (var comp in obj.GetComponents<Component>())
            {
                if (comp is T) return true;
            }

            return false;
        }

        public static bool HasComponentOfInterface(this GameObject obj, System.Type tp)
        {
            foreach (var comp in obj.GetComponents<Component>())
            {
                if (ObjUtil.IsType(comp.GetType(), tp)) return true;
            }

            return false;
        }

        public static bool HasComponentOfInterface(this Component obj, System.Type tp)
        {
            foreach (var comp in obj.GetComponents<Component>())
            {
                if (ObjUtil.IsType(comp.GetType(), tp)) return true;
            }

            return false;
        }

        #endregion

        #region AddComponent

        public static T AddOrGetComponent<T>(this GameObject obj) where T : Component
        {
            if (obj == null) return null;

            T comp = obj.GetComponent<T>();
            if (comp == null)
            {
                comp = obj.AddComponent<T>();
            }

            return comp;
        }

        public static T AddOrGetComponent<T>(this Component obj) where T : Component
        {
            if (obj == null) return null;

            T comp = obj.GetComponent<T>();
            if (comp == null)
            {
                comp = obj.gameObject.AddComponent<T>();
            }

            return comp;
        }

        public static Component AddOrGetComponent(this GameObject obj, System.Type tp)
        {
            if (obj == null) return null;
            if(!ObjUtil.IsType(tp, typeof(ComponentUtil))) return null;

            var comp = obj.GetComponent(tp);
            if (comp == null)
            {
                comp = obj.AddComponent(tp);
            }

            return comp;
        }

        public static Component AddOrGetComponent(this Component obj, System.Type tp)
        {
            if (obj == null) return null;
            if(!ObjUtil.IsType(tp, typeof(ComponentUtil))) return null;

            var comp = obj.GetComponent(tp);
            if (comp == null)
            {
                comp = obj.gameObject.AddComponent(tp);
            }

            return comp;
        }

        #endregion

        #region GetComponent

        // ############
        // GetComponent
        // #########

        public static bool GetComponent<T>(this GameObject obj, out T comp) where T : Component
        {
            comp = obj.GetComponent<T>();
            return (comp != null);
        }
        public static bool GetComponent<T>(this Component obj, out T comp) where T : Component
        {
            comp = obj.GetComponent<T>();
            return (comp != null);
        }

        public static bool GetComponent(this GameObject obj, string tp, out Component comp)
        {
            comp = obj.GetComponent(tp);
            return (comp != null);
        }
        public static bool GetComponent(this Component obj, string tp, out Component comp)
        {
            comp = obj.GetComponent(tp);
            return (comp != null);
        }

        public static bool GetComponent(this GameObject obj, System.Type tp, out Component comp)
        {
            comp = obj.GetComponent(tp);
            return (comp != null);
        }
        public static bool GetComponent(this Component obj, System.Type tp, out Component comp)
        {
            comp = obj.GetComponent(tp);
            return (comp != null);
        }


        // ############
        // GetFirstComponent
        // #########

        public static T GetFirstComponent<T>(this GameObject obj) where T : Component
        {
            if (obj == null) return default(T);

            var arr = obj.GetComponents<T>();
            if (arr != null  arr.Length > 0)
                return arr[0];
            else
                return default(T);
        }

        public static T GetFirstComponent<T>(this Component obj) where T : Component
        {
            if (obj == null) return default(T);

            var arr = obj.GetComponents<T>();
            if (arr != null  arr.Length > 0)
                return arr[0];
            else
                return default(T);
        }

        public static T GetFirstComponentOfInterface<T>(this GameObject obj)
        {
            if (obj == null) return default(T);

            foreach (object comp in obj.GetComponents<Component>())
            {
                if (comp is T) return (T)comp;
            }

            return default(T);
        }

        public static T GetFirstComponentOfInterface<T>(this Component obj)
        {
            if (obj == null) return default(T);

            foreach (object comp in obj.GetComponents<Component>())
            {
                if (comp is T) return (T)comp;
            }

            return default(T);
        }

        public static bool GetFirstComponentOfInterface<T>(this GameObject obj, out T comp )
        {
            comp = default(T);
            if (obj == null) return false;

            foreach (object c in obj.GetComponents<Component>())
            {
                if (comp is T)
                {
                    comp = (T)c;
                    return true;
                }
            }

            return false;
        }

        public static bool GetFirstComponentOfInterface<T>(this Component obj, out T comp)
        {
            comp = default(T);
            if (obj == null) return false;

            foreach (object c in obj.GetComponents<Component>())
            {
                if (c is T)
                {
                    comp = (T)c;
                    return true;
                }
            }

            return false;
        }

        // ############
        // GetComponents
        // #########

        public static T[] GetComponentsOfInterface<T>(this GameObject obj)
        {
            if (obj == null) return null;

            var lst = new List<T>();
            foreach (object comp in obj.GetComponents<Component>())
            {
                if (comp is T) lst.Add((T)comp);
            }
            return lst.ToArray();
        }

        public static T[] GetComponentsOfInterface<T>(this Component obj)
        {
            if (obj == null) return null;

            var lst = new List<T>();
            foreach (object comp in obj.GetComponents<Component>())
            {
                if (comp is T) lst.Add((T)comp);
            }
            return lst.ToArray();
        }

        #endregion

        #region RemoveComponent

        public static void RemoveComponent<T>(this GameObject obj) where T : Component
        {
            var comp = obj.GetComponent<T>();
            if (comp != null) Object.Destroy(comp);
        }

        public static void RemoveComponent<T>(this Component obj) where T : Component
        {
            var comp = obj.GetComponent<T>();
            if (comp != null) Object.Destroy(comp);
        }

        public static void RemoveComponent(this GameObject obj, string tp)
        {
            var comp = obj.GetComponent(tp);
            if (comp != null) Object.Destroy(comp);
        }

        public static void RemoveComponent(this Component obj, string tp)
        {
            var comp = obj.GetComponent(tp);
            if (comp != null) Object.Destroy(comp);
        }

        public static void RemoveComponent(this GameObject obj, System.Type tp)
        {
            var comp = obj.GetComponent(tp);
            if (comp != null) Object.Destroy(comp);
        }

        public static void RemoveComponent(this Component obj, System.Type tp)
        {
            var comp = obj.GetComponent(tp);
            if (comp != null) Object.Destroy(comp);
        }

        public static void RemoveFromOwner(this Component comp)
        {
            Object.Destroy(comp);
        }

        #endregion

        #endregion

    }
}

and you can say:

var enemy = target.GetFirstComponet<Enemy>();//no matter if it's FootSoldier or MilitiaMan, you get it back

Do keep in mind, when you have 2 components that extend the same type. They BOTH can be put on the same gameobject. This is why it’s GetFirstComponent. You should avoid adding multiple components like this…

To allow it though, I’ve been working on a wrapper component that delegates calls to multiple components of similar types on a gameobject. I just don’t have the design down yet.

I also have this other function that based off some odd utilities I have allows me to ‘castTo’ between different component types. What it really does is just takes in any kind of object, and will grab the gameobject, component, component as interface, or convert it to a primitive type, based on what it is, and what it’s changing to.

        /// <summary>
        /// Unique casting routine that will allow for casting between components of a gameobject 
        /// this allows you to treat a gameobject as its component...
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T CastTo<T>(object obj)
        {
            if (obj is T) return (T)obj;

            var tp = typeof(T);
            if (ObjUtil.IsType(tp, typeof(Component))  GameObjectUtil.IsGameObjectSource(obj))
            {
                var go = GameObjectUtil.GetGameObjectFromSource(obj);
                object comp = go.GetComponent(tp);
                return (T)comp;
            }

            if (ConvertUtil.IsSupportedType(tp))
            {
                return ConvertUtil.ToPrim<T>(obj);
            }

            return default(T);
        }

It requires some other util classes that I have including ConvertUtil (converts values between prim types), ObjUtil (Type reflection utility), GameObjectUtil (gameobject utility).

It’s useful for dealing with components as interfaces. I can retrieve objects in wrappers, as components, as gameobejcts, as whatever, as long as they implement an interface. And I may want to convert to another quickly.

//obj could be a transform, a gameobject, a component, or even a generic object that implements an interface that describes a component
void DoSomething(Object obj)
{
    var comp = CastTo<ISomeComponent>(obj);
}

Now yes, all these functions have overhead. Sometimes a high processing overhead. So you shouldn’t just be using them all willy nilly. But they really help in cleaning up the code and allowing me to use interfaces and abstract classes a lot… which I do in most of my design patterns.

I’m kind of a fan of composition over inheritance. Like, instead of having this:

public class Enemy
{
    public virtual void AttackEnemy()
{
... general attack code
}
}

public class MilitiaThing() : Enemy
{
  public override void AttackEnemy()
{
  .. militia specific attack code
}
}

You may want to consider something like using the strategy pattern. NO SERIOUSLY ITS NOT THAT BAD :smile:

public class enemy
{

// In the enemy constructor, you give it the attack logic.
public enemy (AttackType attackType)
{
  _attackType = attackType;
}

public void AttackEnemy()
{
  _attackType.Attack();
}
}

So, let’s say you have enemy type A, and it uses attack type A. And enemy type B, and it uses attack type B.

you would just do this:

Enemy militiaEnemy = new Enemy(new MilitiaAttack());

or

Enemy otherEnemy = new Enemy(new OtherAttack());

You can easily define new types of attacks (ranged attack, whatever) and dynamically change what type of attacks your enemies use. It’s not locked in code anymore. So, let’s say you have an enemy that uses ranged attacks until it gets too close. You can just do this when you first create the enemy.

Enemy enemy = new Enemy(new RangedAttack());

but, if it gets too close:

If (enemy.distance < 50)
{
  enemy.AttackType= new MeleeAttack();
}

And when you get ready for the enemy to attack, you just tell it…to attack. It it uses its logic that you assigned to do so:

enemy.Attack();

You don’t -care- whether it is a melee/militia enemy, or a gunner. You are an -enemy-, attack.

This calls:

public void Attack()
{
 _attackType.Attack();
}

The nice thing about this…the attack logic isn’t built into each enemy anymore. The enemy class just calls the attack logic. And it keeps your enemy code very clean.
The attack logic (like ranged attack) then -only- cares about ranged attacks, and only has code for that. Want to see your melee attack logic? Just look in the MeleeAttack.Attack() method. You don’t have all this logic splattered all over your code solution.

So, instead of a gunner being a subclass of ranged enemy being a subclass of enemy (and you managing layers of the code), you just create an enemy, and right then and there give it the attack logic to use.
Anyway, just a thought. Maybe useful, maybe not :slight_smile:

You can do it through reflection or message passing, those are really your only two real options for code obfuscation and simplicity.

Here’s an off-the-cuff Reflection example.

using System;
using System.Reflection;

namespace ReflectionExample
{
    public class Wizard : Enemy {
        private void HandleDamage(int dam) {
            Console.WriteLine("Wizard.HandleDamage(" + dam + ")");
        }
    }

    public class Ogre : Enemy {
        private void HandleDamage(int dam) {
            Console.WriteLine("Ogre.HandleDamage(" + dam + ")");
        }
    }

    public class Enemy { 
        protected int hitPoints;
        private MethodInfo handleDamage;

        public Enemy() {
            // If the parent class does not have a non-public/non-static HandleDamage method, then this call will return null.
            handleDamage = this.GetType().GetMethod("HandleDamage", BindingFlags.NonPublic | BindingFlags.Instance);
            if (handleDamage == null)
                Console.WriteLine("Warning! Enemy parent class " + this.GetType().ToString() + " does not have a required HandleDamage() method.");
        }

        public void AddDamage(int dam) {
            if (handleDamage != null)
                handleDamage.Invoke(this, new object[] { dam } ); // this will throw an ArgumentException if the parameters don't match the method signature
        }
    }
}

Just keep in mind that reflection is not a fast operation, so you don’t want to do this tens-of-thousands of times per frame.

We can debate the finer points of code design until the cows come home. I generally regard reflection as a poor design decision, and would opt to use virtual methods or interfaces to solve these problems. Code obfuscation and simplicity (on this level) is not an issue when you are the only person who will ever see the code.

I’d rather wield the power of both.

Strategy pattern is nice, but it can’t handle every piece of composition inheritance.

Wowow, thanks guys, just got rid of some post-friday headache and back to my problem. I’ll read carefully through the last posts that seem to suggest the solutions closer to the ones i was thinking about. The first answer of lordofduct summoned in me some memories of a course i had some years ago (and the awful and unpleasant woman that was teaching it), and i guess that was the way i was talking about. I’ll read through it and then figure out some more details. Thanks guys!

Hello!, maybe is a little later, but I came here looking for the same answer @drHogan did. I’ve found this: GetComponent for an inherited class MonoBehaviour?