Hello, we are creating a tower defense game and have a problem with creating persistent list of targets for turrets.
So basicly I am detecting the enemy with collison and add them to a list to keep on track about enemies in my range, also I subscribe to the enemy onKilled event.
I use another list which is a subset of the _targetsInRange these are all the enemies I have a line of sight on.
If the enemy dies I Remove it from both lists, then unsubscribe from the event and reset the target to null and look for another target.
The problem is that sometimes the enemy deletes itself but not from my list. As a result my target lists are full with missing values. How can I avoid this happening?
Also this error only occures occasionally. Sometimes it happens and breaks everything sometimes it works normally. However this problem is more usual if the enemies get damage from other sources like status effects or player damage.
How can I make sure that dead enemies get removed from both target list?
public class TowerShooting : MonoBehaviour
{
private Transform _head;
private Transform _bulletOrigin;
[SerializeField] private List<EnemyController> _targetsInRange = new List<EnemyController>();
[SerializeField] private List<EnemyController> _targetsInSight = new List<EnemyController>();
[SerializeField] private LayerMask _hitLayer;
[SerializeField] private LayerMask _enemyLayer;
[SerializeField] private Transform _target = null;
private bool _canShoot = false;
public void AddTargetToInRange(EnemyController target)
{
_targetsInRange.Add(target);
target.OnEnemyKilled += HandleOnEnemyKilled;
EnsureSight();
}
public void RemoveTargetFromInRange(EnemyController target)
{
_targetsInRange.Remove(target);
if (_targetsInSight.Contains(target))
{
_targetsInSight.Remove(target);
}
target.OnEnemyKilled -= HandleOnEnemyKilled;
_target = null;
EnsureSight();
}
private void EnsureSight()
{
if (_targetsInRange.Count > 0)
{
foreach (EnemyController target in _targetsInRange)
{
var direction = target.transform.position - _origin;
Debug.DrawRay(_origin, direction);
Ray ray = new Ray(_origin, direction);
if (Physics.Raycast(ray, out RaycastHit hit, 1000f, _hitLayer))
{
if (((_enemyLayer.value & (1 << hit.collider.gameObject.layer)) > 0) && (!_targetsInSight.Contains(target)))
{
_targetsInSight.Add(target);
}
else if ((_enemyLayer.value & (1 << hit.collider.gameObject.layer)) <= 0 && _targetsInSight.Contains(target))
{
_targetsInSight.Remove(target);
}
}
}
GetTarget();
}
else
{
_target = null;
}
}
private void GetTarget()
{
if (_targetsInSight.Count > 0)
{
switch (_targetingStyle)
{
case TowerEnums.TargetingStyle.First:
_target = GetEnemyPosition.First(_targetsInSight);
break;
case TowerEnums.TargetingStyle.Last:
_target = GetEnemyPosition.Last(_targetsInSight);
break;
case TowerEnums.TargetingStyle.Strongest:
_target = GetEnemyPosition.Strongest(_targetsInSight);
break;
case TowerEnums.TargetingStyle.Weakest:
_target = GetEnemyPosition.Weakest(_targetsInSight);
break;
case TowerEnums.TargetingStyle.Closest:
_target = GetEnemyPosition.Closest(_targetsInSight, transform.position);
break;
default:
_target = GetEnemyPosition.First(_targetsInSight);
break;
}
}
}
private void HandleOnEnemyKilled(EnemyProperties enemy)
{
EnemyController enemyScript = _target.GetComponent<EnemyController>();
RemoveTargetFromInRange(enemyScript);
}
private void Update()
{
if (_target != null)
{
_canShoot = true;
}
else
{
_canShoot = false;
}
TargetEnemy();
_towerController.Shoot(_canShoot);
if(_targetsInRange.Count > 0)
{
EnsureSight();
}
}
private void TargetEnemy()
{
//Targeting and shooting
}
}