- Home /
How to make a targeting system to target an enemy furthest on the track
I am making a tower defense game. in short, my towers current targeting system make it so that they target the closest enemy to themselves. i am trying to make a targeting system that sets the towers target to the enemy furthest on the track. i currently have a progress variable that tracks their progress on the track by multiplying their time alive by their speed. because i have different types of enemies, this task proves difficult to me and was wondering if anybody could help me
void UpdateTarget()
{
GameObject[] enemies = GameObject.FindGameObjectsWithTag(enemyTag);
float shortestDistance = Mathf.Infinity;
GameObject nearestEnemy = null;
foreach (GameObject enemy in enemies)
{
float distanceToEnemy = Vector3.Distance(transform.position, enemy.transform.position);
if (distanceToEnemy < shortestDistance)
{
shortestDistance = distanceToEnemy;
nearestEnemy = enemy;
}
}
if (nearestEnemy != null && shortestDistance <= range)
{
target = nearestEnemy.transform;
}
else
{
target = null;
}
}
let me know if you need any more information about the code for example the progress variable
Answer by Cornelis-de-Jager · Aug 19, 2019 at 03:53 AM
Create a base enemy class that has functions and attributes that will all enemies will have.
public abstract class Enemy : MonoBehaviour {
public float health;
public float timeAlive;
public float speed;
public Enemy (){
timeAlive = 0;
}
/// <summary>
/// Call this in FixedUpdate
/// </summary>
void OnTick () =>
timeAlive += Time.deltaTime;
/// <summary>
/// Calculates the target priority for a certai nenemy
/// </summary>
public abstract float GetTargetPriority ();
}
Below we will use custom enemy classes derived from the base evenmy class. Attach them to your enemies (1 per enemy) You can create as many of your own as you like, below i created an enemy that has armor and one that keeps ketting faster and faster. Note they derive from the Enemy abstract class. You can customize their behaviour this way and add write custom priority functions to suite your needs.
public class SpeedIncreasingEnemy : Enemy {
void FixedUpdate () {
// Do the on tick function
OnTick();
// Increase speed slightly every tick
speed += Time.deltaTime / 2;
}
public float GetTargetPriority () {
return timeAlive * speed + health;
}
}
public class ArmoredEnemy : Enemy {
public float armor;
void FixedUpdate () => OnTick();
public float GetTargetPriority () {
return timeAlive * speed + health + armor;
}
}
... Create your own custom classes
Now we can easily select target priority through your script. I made small changes that should work
void UpdateTarget()
{
GameObject[] enemies = GameObject.FindGameObjectsWithTag(enemyTag);
GameObject nearestEnemy = enemies.FirstOrDefault(); // using System.Linq;
foreach (GameObject enemy in enemies)
{
if (nearestEnemy == enemy)
continue;
if (nearestEnemy.GetComponent<Enemy>().GetTargetPriority() < enemy.GetComponent<Enemy>().GetTargetPriority())
nearestEnemy = enemy;
}
// This code is fine
if (nearestEnemy != null && shortestDistance <= range)
{
target = nearestEnemy.transform;
}
else
{
target = null;
}
}
I am receiving the error 'ArmoredEnemy' does not implement inherited abstract member 'Enemy.GetTargetPriority()'
when i try and make the armored enemy class, also when you are making your own custom classes, do you make them inside the enemy class? @Cornelis-de-Jager
So the error you are receiving is because the Enemy.GetTargetPriority() function should be implemented in your ArmoredEnemy class. I might have speleld it incorrectly and thats why it is giving an error. But let me explain abstract classes. They are basically a class that you can inherrit from, but you can't use directly. You say that the members in the class are abstract, meaning you only give the function names, you have to fill out the detail in the other classes that inherrit from it. But you have to implement those functions.
Think of it like a requirements list. Or like an interface if you have ever worked with those. You list all the functions that need to be implemented in the abstract class/interface. Then when you extend/inherrit from it you simply need to implement those functions.
Where a interface and abstract class differs, is that a abstract class can contain variables and ACTUAL non abstract functions that can be called from child classes.
An no, don't make them inside the Enemy class. Simply create new scripts like you would with any other. To make them a child of the Enemy class / Or inherrit from the enemy class you simple change this:
public class $$anonymous$$yCustomClass : $$anonymous$$onobehaviour {...}
to this:
public class $$anonymous$$yCustomClass : Enemy {...}
That will tell Unity that $$anonymous$$yCustomClass
is a child class of Enemy / Inherrits from the Enemy class.
okay thanks, that will work but rn im just going to work on the gameplay of it and later i will implement that system. thanks
I also receive the error when i change the class enemy to abstract
The class named 'Enemy' is abstract. The script class can't be abstract! UnityEngine.Object:Instantiate(Transform, Vector3, Quaternion) waveSpawner:SpawnEnemy01() (at Assets/Scripts/waveSpawner.cs:51)
but when i dont have it abstract this error pops up
'Enemy.GetTargetPriority()' is abstract but it is contained in non-abstract class 'Enemy' 79 Active
Ok so this a strange thing Unique to Unity only. You can't Instantiate from a abstract class. Ins$$anonymous$$d create an abstract function class Spawn:
// In abstract class
public abstract void Spawn();
// In inherited class
public void Spawn() { ... }
Alternatively, you can remove all abstract references from your class and just make it a normal class - you can still inherit from it just like you would from an abstract class. And is a bit easier to start off with.
Your enemy class will then look like this:
public class Enemy : $$anonymous$$onoBehaviour {
public float health;
public float timeAlive;
public float speed;
public Enemy (){
timeAlive = 0;
}
/// <summary>
/// Call this in FixedUpdate
/// </summary>
void virtual OnTick () =>
timeAlive += Time.deltaTime;
/// <summary>
/// Calculates the target priority for a certai nenemy
/// </summary>
public virtual float GetTargetPriority (){
return timeAlive * speed + health;
}
}
This way all your child classes already has the GetTargetPriority and will fix any errors you previously got from it. But if you want to customize it, then this is how it will look (Using Armored Enemy class):
public class ArmoredEnemy : Enemy {
public float armor;
void FixedUpdate () => OnTick();
// Notice the OVERRIDE statement
// The parent class needs to be virtual to be able to be Overriden
public override float GetTargetPriority () {
return timeAlive * speed + health + armor;
}
}
okay thanks, that will work but rn im just going to work on the gameplay of it and later i will implement that system. thanks