For each loop (finding closest enemy to the player)

Hello! I would like to make a bot be able to choose enemy targets. Either randomly or depending on the distance (the closest one available). I’ve written a script, but it doesn’t seem to work. Is there any mistake?

Thank you very much!

	void OnTriggerStay(Collider other){
		if (other.tag == "enemy") {
		//if there is more than 1 enemy inside the trigger, - add all enemies into the arraylist
			if (enemyarray.Count > 1) {
				//choose random enemy
				randomenemy = Random.Range(0f,100f);
				if (randomenemy <= 50f){
				randomnumber = Random.Range(0, enemyarray.Count);
				enemy = enemyarray [randomnumber];
				}
				//or choose closest enemy
				else if (randomenemy >50f){
					foreach (GameObject enemy in enemyarray){
						 distance = Vector3.Distance(enemy.transform.position, transform.position);
						if (distance < storeddistance){
						enemy = enemy; //Console gives the following error: "Cannot assign enemy because it is a 'foreach iteration variable'"
							}
							else if (distance > storeddistance){
							storeddistance = distance;
							}
						}
				}
			}
			//else if there is 1 enemy inside the trigger, it is the enemy
			else if((enemyarray.Count == 1)){
				enemy = other.gameObject;
			}
}

Hi,

First we need to know how enemyarray is declared and filled with GameObjects, with only the code sample you provided it may be the array that is faulty but we can’t know.

Second, make sure randompenemy is an INT and remember that Random.Range is [inclusive - exclusive] for INTs (and only [inclusive - inclusive] for FLOATs) → So you need to call Random.Range(0, enemyarray.Count + 1); to get all the items in it.

Third, you need to name your foreach variable differently, it seems enemy is already taken by something else in your code: when you do enemy = enemy, nothing can happen since Unity will understand internalVariable = internalVariable.

Finally, you need to store distance into storeddistance when you find a closer enemy, not when it’s further. Since the current enemy that is the closest is to be the reference, you want to know if the following enemies are closer to it so you need to compare the following distances to the current closest enemy !

Your foreach should look like this:

storeddistance = 1000; //Initialize largely enough so you're sure
//that the first enemy will be closer than that

foreach (GameObject currentEnemy in enemyarray) {
   distance = Vector3.Distance(...);
   if (distance < storeddistance) {
      storeddistance = distance;
      enemy = currentEnemy;
   }
}

KdRWaylander, Thank you very much for your detailed explanation! I tried to fix the script and updated the code. Uploading here.

using UnityEngine;
using System.Collections;

//for Generic list arrays
using System.Collections.Generic;

public class enemyscript : MonoBehaviour {

	
	public float fieldOfViewAngle = 110f;           // Number of degrees, centred on forward, for the enemy see.
	public bool playerInSight;                      // Whether or not the player is currently sighted.
	
	private NavMeshAgent nav;                       // Reference to the NavMeshAgent component.
	public SphereCollider col;                      // Reference to the sphere collider trigger component.

	public List<GameObject> enemyarray;				//all enemies inside the trigger
	public GameObject enemyplayer;					//our enemy target
	public GameObject currentenemytarget;			//current enemytarget

	public Vector3 direction;
	public float angle;								//Field of view
	
	public Vector3 personalLastSighting;            // Last place this enemy spotted the player.
	public float distance;							
	public float storeddistance;

	public int enemyheard;
	public int numberofenemiesseen;
	public int maxenemypossible; 					//max number of enemies in the game



	public int randomnumber;
	public int randomenemy;



	// Use this for initialization
	void Start () {
		// Setting up the references.
		nav = GetComponent<NavMeshAgent>();
		col = GetComponent<SphereCollider>();
		storeddistance = 1000000; //so first loop is always correct;
		maxenemypossible = 100;

	}


	void OnTriggerEnter(Collider other){
		if (other.tag == "enemy") {
			//every enemy that enters is stored in array
			enemyarray.Add(other.gameObject);
		}
	}

	void OnTriggerStay(Collider other){
		if (other.tag == "enemy") {
		
			//if there is more than 1 enemy inside the trigger, add all enemies into the array
			if (enemyarray.Count > 1) {

				randomnumber = Random.Range(0,100);
				if (randomnumber <= 50){
		
					//choose random enemy
					randomenemy = Random.Range(0, enemyarray.Count+1);
					currentenemytarget = enemyarray [randomenemy];
				}
					//or choose closest enemy
					else if (randomnumber > 50){
						
						foreach (GameObject enemy in enemyarray){

							distance = Vector3.Distance(other.transform.position, transform.position);
								if (distance < storeddistance) {
									storeddistance = distance;
									currentenemytarget = enemy;
								}
									else if (distance > storeddistance){
										storeddistance = distance;
									}
						}
				}
			}



			//else if there is 1 enemy inside the trigger, it is the enemy
			else if((enemyarray.Count == 1)){
				currentenemytarget = other.gameObject;
			}


			//enemy is in the trigger, but not yet known whether is seen
			playerInSight = false;
			direction = other.transform.position - transform.position;
			angle = Vector3.Angle(direction, transform.forward);

			// If the angle between forward and where the player is, is less than half the angle of view...
			if(angle < fieldOfViewAngle * 0.5f)
			{
				
				//	Debug.DrawRay(transform.position + transform.up, direction.normalized, Color.red);
				RaycastHit hit;
				
				//		Debug.Log("Hit Object: " + hit.collider.gameObject.name);
				
				// ... and if a raycast towards the player hits something...
				//			if(Physics.Raycast(transform.position + transform.up, direction.normalized, out hit, col.radius))
				if(Physics.Raycast(transform.position + transform.up, direction.normalized, out hit, col.radius*Mathf.Max(transform.localScale.x, transform.localScale.y, transform.localScale.z)))
				{
					Debug.DrawRay(transform.position, direction, Color.red);
					Debug.Log(hit.collider.gameObject.name);
					
					// ... and if the raycast hits the player...
					if(hit.collider.gameObject.tag == "enemy")
					{
						// ... the player is in sight.
						playerInSight = true;
						Debug.Log ("enemy is seen");
						numberofenemiesseen ++;
					}
				}
			}
		}




			//if hearing distance < trriger radius
			if(CalculatePathLength(currentenemytarget.transform.position) <= col.radius*Mathf.Max(transform.localScale.x, transform.localScale.y, transform.localScale.z)){
				Debug.Log ("stop");
				enemyheard ++;
				personalLastSighting = currentenemytarget.transform.position;
			}



		}

	
	// If the player leaves the trigger zone...
	void OnTriggerExit(Collider other){
		
		if (other.tag == "enemy") {
			enemyarray.Remove(other.gameObject);
			enemyheard --;
			numberofenemiesseen --;
			playerInSight = false;
		}

		
	}


	//calculate hearing distance;
	float CalculatePathLength (Vector3 targetPosition)
	{
		// Create a path and set it based on a target position.
		NavMeshPath path = new NavMeshPath();
		if(nav.enabled)
			nav.CalculatePath(targetPosition, path);
		
		// Create an array of points which is the length of the number of corners in the path + 2.
		Vector3[] allWayPoints = new Vector3[path.corners.Length + 2];
		
		
		// The first point is the enemy's position. ?? http://unity3d.com/learn/tutorials/projects/stealth/enemy-sight?playlist=17168 ??
		allWayPoints[0] = transform.position;
		
		// The last point is the target position.
		allWayPoints[allWayPoints.Length - 1] = targetPosition;
		
		// The points inbetween are the corners of the path.
		for(int i = 0; i < path.corners.Length; i++)
		{
			allWayPoints[i + 1] = path.corners*;*
  •  }*
    
  •  // Create a float to store the path length that is by default 0.*
    
  •  float pathLength = 0;*
    
  •  // Increment the path length by an amount equal to the distance between each waypoint and the next.*
    
  •  for(int i = 0; i < allWayPoints.Length - 1; i++)*
    
  •  {*
    

_ pathLength += Vector3.Distance(allWayPoints*, allWayPoints[i + 1]);_
_
}_
_
//Debug.Log (“distance is:” + Vector3.Distance (enemy.transform.position, transform.position));_
_
//Debug.Log (“path distance is:” + pathLength);_
_
return pathLength;_
_
}*_

}