Hello, I’m trying to make a Tower Defense style game, and I’ve been stuck on one problem. Before I tell what the problem is, let me explain what I’ve been working on so far.
I’m scripting for enemies which do NOT follow pre-set path, but actively ATTACK towers near them on their way to the core(Life of the player).
There are two types of towers: Attacker and Blocker. Attacker turret, just as what its name implements, attacks enemies within it’s attack range. Blocker turret cannot attack enemies, but they can block enemies’ path to the core and literally manipulate the battlefield by being deployed on it. I attached navmesh obstacle object component to towers(both blocker and attacker) and set the carve option on.
And here comes my problem. When the blocker turrets are already deployed on the battlefield, the enemies(they have navmesh agent component) look for the shortest path to the core(which is not what I intended). I’d rather want them to act like if they didn’t know what’s up ahead , and recognize turrets’ navmesh obstacle component only when they get close enough to the turrets(in the search radius). For this, enemy should only recognize navmesh obstacles of turrets within its search radius, and ignore those that are outside of its search radius.
There is also a priority system(Enemies prioritize Attacker turrets than Blocker turrets), and I mention this for you to understand the script ahead better.
please ignore debug.logs, I added them to keep track on the script. This script is attached to the enemy, and I hasn’t added attack system of enemy.
private Transform core; //transform of the core
private NavMeshAgent agent; //navmeshagent of this enemy object
private Transform currentTarget, tr;
private float distanceToTarget;
private List<Transform> turretsInRange = new List<Transform>();
private float distance;
void Start()
{
agent = this.transform.parent.gameObject.GetComponent<NavMeshAgent>();
tr = GetComponent<Transform>();
core = GameObject.FindGameObjectWithTag("CORE").transform;
SetTarget(currentTarget = core.transform);
// at the begining, currentTarget is set to be the core
StartCoroutine("Scan"); // Scans nearby turrets every 0.2 seconds
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("$$$$$$$$$$$$$$$$$$$");
Debug.Log(currentTarget.tag.ToString() + " " +
GetRemainingDistance(currentTarget).ToString());
}
}
void SetTarget(Transform targetPoint) // sets destination of navmesh agent
{
Debug.Log("Target ReSet");
agent.SetDestination(targetPoint.position);
Debug.Log(currentTarget.tag.ToString() + " " +
GetRemainingDistance(currentTarget).ToString());
//GetRemainingDistance method returns the path length between
this enemy and the transform sent as parameter
}
void SearchTarget(bool hasAttacker)
{
Debug.Log("has Attacker " + hasAttacker.ToString());
float distanceToPotentialTarget, distanceToCurrentTarget;
float minDistanceToPotentialTarget = Mathf.Infinity;
Transform minPotentialTarget = currentTarget;
distanceToCurrentTarget = GetRemainingDistance(currentTarget);
if (hasAttacker && currentTarget.tag != "ATTACKER")
// if attacker is nearby, reset the distance to current target to infinity
distanceToCurrentTarget = Mathf.Infinity;
if (turretsInRange.Count != 0)
{
foreach (Transform potentialTarget in turretsInRange)
{
//Debug.Log(turretsInRange.Count.ToString() +
" distance to potential target" + distanceToPotentialTarget.ToString());
if ((!hasAttacker && potentialTarget.tag == "BLOCKER")
|| potentialTarget.tag == "ATTACKER")
{
Debug.Log(potentialTarget.tag);
distanceToPotentialTarget = GetRemainingDistance(potentialTarget);
if (distanceToPotentialTarget < minDistanceToPotentialTarget)
// finds the most closest turret
{
minDistanceToPotentialTarget = distanceToPotentialTarget;
minPotentialTarget = potentialTarget;
}
}
}
//Debug.Log(minDistanceToPotentialTarget.ToString());
//Debug.Log(GetRemainingDistance(currentTarget).ToString());
if (minDistanceToPotentialTarget < distanceToCurrentTarget)
// if there's any new turret closer than the current target-
// which was found 0.2s before, reset the destination of navmesh agent
SetTarget(currentTarget = minPotentialTarget);
}
else // if there's no turrets in search radius, set the destination to core
{
Debug.Log("Destination set to Core");
if (currentTarget != core)
SetTarget(currentTarget = core);
}
}
IEnumerator Scan()
{
bool hasAttacker = false; // this is for prioritizing system
while (true)
{
Collider[] turretList = Physics.OverlapSphere(tr.position, 5f);
// used physics.overlap instead of trigger because trigger didn't work out quite well...
turretsInRange.Clear();
// puts transform of all turrets nearby after cleaning up the turretsinrange list
foreach (Collider oneTurret in turretList)
{
if (oneTurret.gameObject != null)
if ((oneTurret.tag == "ATTACKER" || oneTurret.tag == "BLOCKER") &&
GetRemainingDistance(oneTurret.transform) <= 15f) // 15 is search radius
{
if (oneTurret.tag == "ATTACKER")
hasAttacker = true;
turretsInRange.Add(oneTurret.transform);
}
}
SearchTarget(hasAttacker);
hasAttacker = false;
yield return new WaitForSeconds(0.2f);
}
}
float GetRemainingDistance(Transform target)
// made this method because navmesh.remainingdistance doesn't work with many corners
{
NavMeshPath path = new NavMeshPath();
if (target == null)
return Mathf.Infinity;
NavMesh.CalculatePath(tr.position, target.position, NavMesh.AllAreas, path);
if (path.status == NavMeshPathStatus.PathPartial || path.corners.Length == 0)
return Mathf.Infinity;
Debug.Log("corners!!!!!!!!");
Debug.Log(path.corners.Length.ToString());
Vector3 previousCorner = path.corners[0];
float lengthSoFar = 0.0F;
int i = 1;
while (i < path.corners.Length)
{
Vector3 currentCorner = path.corners*;*
lengthSoFar += Vector3.Distance(previousCorner, currentCorner);
previousCorner = currentCorner;
i++;
}
return lengthSoFar;
}
Wonder if I shouldn’t have used navmesh at all for this? If there’s any improvement that can be made, please let me know.