- Published on
Improving Mechanics for my Tower Defense Game
Introduction
Hello everyone Pofay here with another article.
If you've played my tower defense game you might notice one issue that ticks you off (there are a lot of them.) but one stands out in terms of gameplay.
The gameplay issue is how slow the turret changes targets. It has a slight delay in changing targets after its current target dies.
This causes low hp targets a chance to slip past the turret and reach the goal. This is due to the current implementation of the TurretTargeting.cs
script attached in all turrets.
Context
When I started making this game, I followed Brackey's Tutorial on making a Tower Defense Game
I followed most of the tutorial's content but wrote the code with my style.
But there was one thing that I couldn't change to fit my standards, which was the algorithm for the turrets to find a target.
// TurretTargeting.cs
public Transform CalculateNextTarget(float range)
{
// 1. Query GameObjects that have the "Enemies" tag.
var enemies = GameObject.FindGameObjectsWithTag(targetTag);
var shortestDistance = Mathf.Infinity;
GameObject nearestEnemy = null;
// 2. Perform Distance calculation
foreach (var enemy in enemies)
{
var distanceToEnemy = Vector3.Distance(transform.position, enemy.transform.position);
// 3. Set current target based on shortest distance
if (distanceToEnemy < shortestDistance)
{
shortestDistance = distanceToEnemy;
nearestEnemy = enemy;
}
}
// 4. Check if target is in range and is still alive.
if (nearestEnemy != null && IsInRange(shortestDistance, range)
&& nearestEnemy.GetComponent<EnemyHealth>().isActiveAndEnabled)
{
return nearestEnemy.transform;
}
else
{
return null;
}
}
The main problem with this algorithm is at step 1 and step 2. Provided you have 100-150 enemies in your scene:
- Each turret has to query all enemies in the scene (including the disabled ones)
- Each turret needs to run a distance calculation loop on all enemies before finding the right target.
This isn't an issue if the number of enemies in your scene is static. (not spawned using code)
But for a scenario like this game where it's spawning enemies in intervals (via script) then it just doesn't fit.
The Fix
The fix for the issue was to change how the script queries the enemies.
Instead of querying them all, I decided to use Physics.OverlapSphere
to make the tower only get the enemies near its range.
This makes the number of enemies in the distance calculaton loop lower than the previous implementation. (I don't have the exact numbers for this.)
public Transform CalculateNextTarget(float range)
{
// 1. Query targets near range of tower
var possibleTargets = Physics.OverlapSphere(transform.position, range, targetMask);
var shortestDistance = Mathf.Infinity;
GameObject nearestTarget = null;
// 2. Perform distance calculation
foreach (var target in possibleTargets)
{
var distanceToEnemy = Vector3.Distance(transform.position, target.transform.position);
// 3. Set current target based on distance
if (distanceToEnemy < shortestDistance)
{
shortestDistance = distanceToEnemy;
nearestTarget = target.gameObject;
}
}
// 4. Check if target is in range and is still alive.
if (nearestTarget != null && IsInRange(shortestDistance, range)
&& nearestTarget.GetComponent<EnemyHealth>().isActiveAndEnabled)
{
return nearestTarget.transform;
}
else
{
return null;
}
}
With that change in the script, the turrets will switch targets immediately when their current target dies.
Ending thoughts
Although I'm not that versed in performance, I could see its effects if you neglect it on this game.
This was a lesson for me to take performance into account when writing games and any software that needs it.
With that said, there's still several issues in the game that I may yet tackle:
- Rapid instantiation/Destruction of Projectiles (which can be solved by pooling)
- Alt-tabbing causes the screenG to move to the current mouse position
- Some sounds keeps playing even when the game is paused
And a number of improvements:
- Change VFX (since I bought a new pack.)
- Improve lighting and post-processing
Until then, happy coding. You can view the code for the game here.
(P.S as of writing this post, the fix is live on the current itch.io page)