Best way to implement common 'Enemy' script

Say i have ~100 enemy types in my game and so i have ~100 different scripts for each enemy. I want them to all do common things, such as display text above their heads when they take damage, they all have health, mana, resistances, they all take damage in a similar way according to their Armour and resistances etc. How do i implement this without writing health, text displaying functions etc for every single enemy? Could i just implement a common ‘Enemy’ script that handles all the basic functions, variables etc? Would i just attach this script to every enemy? Is there a better way to implement this?

Thanks.

1 Like

You would have one enemy script that has multiple component scripts, like a combat class to control stats and skills, and possibly an AI script, inventory, etc. You can use an enum for an ‘enemytype’ or ‘enemybehavior’ to create different classes or specifications of an enemy’s type or behavior. The enemy’s behavior, attacks, stats, etc could be varied based on the enemytype and behavior. The main enemy script could be the AI or you could offload it to another script that behaves based on the enemytype/behavior. If the behavior is vastly different between two enemies you could have individual AI scripts that are chosen based on the main enemy script.

You can also use prefabs in the Unity editor to create premade gameobjects with specific values on your enemy script and its component scripts. - or the values can be set based on the script, or a combination of both. You could for example set the enemy type to a string “Gnoll1” and have it load specific stats in Start based on the string. You could then also attach or load the prefabs to a script that spawns enemies using prefabs or a string that sets the values.

Eventually you’d want to store the enemy types in some sort of file and then have it read from that, but if you’re still learning I would wait on that for a while.

An enemy is really nothing more than a concept.

The concrete implementation are the mechanics (=components/monobehaviours) that make a gameobject an enemy. You dont even need an “enemy script”, but can have a gameobject with a bunch of scripts attached act like one.

Make a Move-component, make TurnTowardsPlayer-component, make a AttackIfInRange-component.
Attach all 3 to a gameObject - now you have an enemy.
Duplicate it, make it move faster - you have 2 enemy types.
Duplicate it again, make it move slower but attack harder, 3rd enemy type.

Still the same amount of scripts …

(Ofc this is oversimplified)

No this is really really bad to do, especially for large games.

Right this is what i thought. Would save me a lot of copy pasting. A separate script for each enemy type controlling its unique behavior that uses and works off of the common scripts.

Cheers.

Have a look at inheritance in C#.
Inheritance is one of the core fundamentals of object-oriented programming, allowing you to define common fields & methods that derived scripts will all have.

Here’s a visual example:


Here, we have three types of Vehicles: Car, Boat, and Plane, which all inherit from a parent Vehicle class.
Along side their own attributes and methods, because they inherit from Vehicle, they also get Vehicle’s fields and methods (as long as they’re not private), meaning that Car, Boat, and Plane all have the following attributes:

  • A driver seat
  • An engine
  • An exhaust

And they can all perform the following operations:

  • Turn on
  • Turn off
  • Drive
  • Break

Here’s how this would look through code:
Code

//The "abstract" keyword here means we cannot instantiate a Vehicle, rather we must instantiate something that inherits from Vehicle.
//This isn't always necessary, but it makes sense in this case.
public abstract class Vehicle {
   public Transform driverSeat;
   public Transform engine;
   public Transform exhaust;

   public void TurnOn() {
      //etc...
   }

   public void TurnOff() {
      //etc...
   }

   public void Drive() {
      //etc...
   }

   public void Break() {
      //etc...
   }
}
//Car inherits Vehicle, giving it all of Vehicle's attributes and methods.
public class Car : Vehicle {
   public Transform axle;
   public Transform windShield;

   public void LeftTurnSignal(bool on) {
      //etc...
   }

   public void RightTurnSignal(bool on) {
      //etc...
   }
}
//Boat inherits Vehicle, giving it all of Vehicle's attributes and methods.
public class Boat : Vehicle {
   public string captain;
   public int crewSize;

   public void Dock() {
      //etc...
   }

   public void UnDock() {
      //etc...
   }

   public void DropAnchor() {
      //etc...
   }

   public void RaiseAnchor() {
      //etc...
   }
}
//Plane inherits Vehicle, giving it all of Vehicle's attributes and methods.
public class Plane : Vehicle {
   public float wingspan;
   public int passengerCapacity;

   public void TakeOff() {
      //etc...
   }

   public void Land() {
      //etc...
   }

   public void PitchUp() {
      //etc...
   }

   public void PitchDown() {
      //etc...
   }

   public void RollLeft() {
      //etc...
   }

   public void RollRight() {
      //etc...
   }
}

There are very intricate structures you can accomplish with inheritance, and it can be a very powerful way of reusing code and reducing redundancy.

3 Likes

I would have one enemy script that is like a controller/overview of the enemy that they all have, then have components like Combat, AI, Movement, Inventory. The AI script would vary based on the behavior type. That way you can treat all enemies as one type when working with things like collisions and other interactions. In most cases you wouldn’t need multiple different types of AI scripts unless the AI is incredibly complex, you can use flags like patrolRadius, aggressiveness etc to alter the behavior.

Edit: Inheritance is good too, but unless your enemies have very complex differences it might not be worth it and might just overcomplicate things.

Create a base Enemy prefab. Make different Prefab Variants for each type.

One health script can be used for all 100 of them.

for really large games you go exactly that route - just that you use ECS/DOTS :wink:

There are a lot of different approaches in this thread, not all of which are compatible, so:

I would highly suggest doing exactly what Vryken suggested and have a look at inheritance. You would want to define an abstract class “Enemy” that has/does all the common things you want your enemies to have, like having HP, having a function to TakeDamage() or Attack() and so on.
You then create subtypes of Enemy by inheriting from Enemy. For example, just like a Boat is also a Vehicle, a Wolf is also an Enemy, so it does all an Enemy does (having HP, Attack(), …) but extends the functionality of a normal enemy, by adding to it, or by rewriting parts of it. This way you implement all the common things only once (when defining the abstract class “Enemy”), and every subtype of Enemy will have this exact functionality. It also allows you to overwrite, say, the TakeDamage() function for a specific subtype of Enemy (for example Wolfes take damage in a different way).
I believe this is exactly what you want.
You’d still have to attach the script for “Wolf” to each Wolf-Enemy. However, as others said you can simply do it once and save the completed Wolf GameObject as a prefab, which you can reuse or spawn from script whenever needed.

Other than that, if the game is going to have a huge amount of enemies active at the same time (like at least hundreds if not thousands) you would want to take a loot at the new DOTS architecture Unity offers (like NotYes brought up) to make this highly efficient. However, it is different from object oriented programming and maybe a tad complicated, so i wouldnt recomment it, especially for a beginner.

1 Like

Thank you this has worked wonders!

1 Like

I would suggest reading this discussion when contemplating inheritance, inheritance is not necessarily the wrong answer to this, but composition would be my approach and I think it is worth understanding for any developer. A general rule I see if that once you properly understand composition, you will only use inheritance is relatively rare instances. But that is a huge debate and beyond the scope of this thread. Just thought this might add to someone’s day.

1 Like

Thank you, it did :slight_smile:

1 Like

Please use the Like button to show appreciation rather than necroing old threads.

Thanks.

1 Like