TowerDefense - How to make a tower target the first enemy?
Hey guys. I am currently working on a tower defense game and I want the user to be able to change which enemy to target, so the enemy in first (closest to the exit), last (furthest from the exit), close (closest to the tower), and strongest (strongest enemy currently alive). So far I have managed to make the towers only shoot at the closest and strongest enemies, but not the first or last enemies.
To make this a bit clearer, I have been using Brackeys tutorials to make this game. This is the video where he creates the first tower and sets it up to shoot enemies. https://www.youtube.com/watch?v=QKhn2kl9_8I
My issue is though is that he never shows how to get the towers to shoot the first enemy in these tutorials, and I'm sure I'm more likely to get a response here than in the comment section. :D
Thanks everyone!
I haven't followed the tutorial you are talking about, but, supposing your enemies use a PathFinding system, you could compare the remaining distance of the units to deter$$anonymous$$e whether the enemy is the "first" one (= closest to their destination) or the last one.
link textThanks for your response. $$anonymous$$y game uses 'waypoints' to help the enemies navigate through the each level. There are about 15 of them in my first level and they are used every time an enemy needs to turn a corner. The two scripts attached here are used to help the enemies navigate. I'm not sure if I could possibly compare the distance using these waypoitns though because in some maps, the enemies can take multiple routes so it will be hard to deter$$anonymous$$e which enemy is in first and last. I'll certainly have a look into it though and see if I can find a solution.
Thank You!
Answer by Alanisaac · May 24, 2018 at 01:08 AM
In the description on that youtube video there's a link to the entire project on GitHub. If you look at "../Assets/Scripts/Turret.cs", and look at the function "Turret.UpdateTarget()", you'll find the following:
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;
targetEnemy = nearestEnemy.GetComponent<Enemy>();
} else
{
target = null;
}
}
In other words, it looks like it's just using pure distance from the turret to the enemy to target, not "first living enemy in the wave".
But if you wanted to have a targeting behavior that was "first living enemy in the wave", I'd recommend simply giving each Enemy an index (1, 2, 3, ...) when you spawn them in the wave. Then, as below, iterate over the full set of enemies, but instead of distance, find the lowest index enemy that is not dead and target that one.
@Alanisaac Hi there. Thanks for taking your time to look into the tutorial and scripts, I really appreciate that. Unfortuantly, I don't think that idea will work. See, some of my enemies move at different speeds, so for example, my enemies labelled 'Simple' move at speed 10 and the ones labelled 'Tough' move at speed 7. If one of the tough enemies spawn before one of the simple enemies though, the tough one will eventually be behind the simple one depending on the distance travelled, and that tough enemy will of course have a lower index, so the towers will think the tough one is still in first.
I was maybe thinking of setting up some sort of calculation in the enemy script that divides the time alive by the speed of the enemy or something like that, and then get the tower to find the first enemy by finding the highest or lowest value. I have no idea how I could possibly do that though and I'm not sure what calculation I will need to gather these values.
Thanks for your answer anyway!
Answer by JusSumGuy · May 24, 2018 at 11:46 AM
You can make one empty gameobject near the position of your spawn point. Then write a script that says attack the enemy closest to the emptygameobject. That's for attacking the enemy furthest away from exit. To attack the enemy closest to exit make a empty gameobject near the exit and write a script that's says attack the enemy closest to the empty gameobject near the exit point.
Thanks for your answer. I think I can see what your saying. I actually have a cube that is used for the start and the end points, but there's a problem with doing that. If you take a look at this map for example (my start point is in the left house and the end is in the right house) there are many corners that the enemies have to turn round. I think the only way that idea will work is if the track was literally just one straight line from the start to the end. The enemies here follow the path shown. For example, if an enemy was in the bottom left of the path and an enemy in the top right of the path, the one in the bottom left is actually closer to finishing, but the towers will treat the top right one like it is in first, so I don't think that would quite work sadly :( Thanks anyway!
Oh yea you're right lol. Try this make a float distanceCovered; Each time an enemy moves to the next tile add 1 to distanceCovered. Then the enemy with the largest value for that variable is ahead of the pack and the one with the lowest is behind the pack. That should definetly work. If you need help with the scripting part of it let me know.
@JusSumGuy Argh yes, I did have that idea myself actually but I couldn't for the life of me work out the scripting of how to make the turret shoot the enemy with the highest value, not to mention when that enemy is in range. The floats I created before in my 'Enemy' script are slightly different to your 'distanceCovered' one but they do basically the same thing. So I have two floats set up. IEnumerator TimeAlive() { while(isDead == false) { timeAlive = timeAlive + 0.01f; yield return new WaitForSeconds(0.01f);
}
}
This sample of code basically counts up the amount of time an enemy is alive until it dies.
progress = timeAlive * speed;
And then this line of code sets a float called 'progress' equal to the time alive * the current speed of the enemy. So for an enemy that's going slower, the progress will fill up slower if that makes sense. Now all I need is a way to make the towers shoot the enemy with the highest progress that's in range. I tried a few things using lists and the $$anonymous$$athf.$$anonymous$$ax statement but couldn't find a solution, so any help would be appreciated.
Here is the code that I use to make the towers shoot the enemy closest to them...
if (closestEnemy == true)
{
foreach (string tag in tags)
{
GameObject[] gameObjects = GameObject.FindGameObjectsWithTag(tag);
foreach (GameObject enemy in gameObjects)
{
float distanceToEnemy = Vector3.Distance(transform.position, enemy.transform.position);
if (distanceToEnemy < shortestDistance)
{
shortestDistance = distanceToEnemy;
nearestEnemy = enemy;
}
}
}
if (nearestEnemy != null && shortestDistance <= range)
{
target = nearestEnemy.transform;
targetEnemy = nearestEnemy.GetComponent<Enemy>();
}
else
{
target = null;
}
}
Brackeys does very well to explain what each of the variables used in this sample does, and the link to that is in my first question.
Thank you!
Your answer
Follow this Question
Related Questions
Clicking on a 2d image and placing 3d object? 0 Answers
Movement following the A* algorithm 0 Answers
Enemy AI using pathfinding to avoid obstacles 1 Answer
Move a object along a fixed path. 2 Answers