Trying to find the minimum Vector3.distance from an array

I’ve been trying to get the debug log to give me a readout of only the tagged object with the lowest distance. I’ve been playing around with Mathf.min but I’m either using it wrong or am doing something wrong on a more fundamental level.

the following script is placed on an object called mobOne and is tagged mobTeamOne, and the two opposing tags in question are mobTwo and Sphere.

Here’s the script (please ignore some of the naming conventions, I’m new and the purpose of some of these variables has shifted a few times.):

using UnityEngine;
using System.Collections;

public class MobOneScript: MonoBehaviour
{
    public GameObject target;


    public float moveSpeed = 5f;

    public GameObject[] closestMob;

    //float distance = Vector3.Distance(this.transform.position, GameObject.FindGameObjectsWithTag("mobTeamTwo"));

    // Use this for initialization
    void Start ()
    {
 
        //public int closestMobDistance = Mathf.Min(this.transform.position, closestMob.transform.position);
        closestMob = GameObject.FindGameObjectsWithTag("mobTeamTwo");
        //target = GameObject.FindWithTag("mobTeamTwo");


        for (int i = 0; i < closestMob.Length; i++)
        {
            /*
            float mobInQuestion = closestMob.transform.position;
            float thisOne = this.transform.position;

            float distCheck = Mathf.Min(mobInQuestion, thisOne);
            */
            Debug.Log("Magnitude: " + (Vector3.Distance(closestMob.transform.position, this.transform.position)));

            //The above and below currently feed out the same information.

            float distCheck = Vector3.Distance(closestMob.transform.position, this.transform.position);
            Debug.Log("Distance Check: " + (Mathf.Min(distCheck)));
        }

     

    }
 
   // Update is called once per frame
    void Update ()
    {


        //transform.LookAt (target.transform);


        if (Vector3.Distance(this.transform.position, target.transform.position)>= 10.0f)
        {
            transform.LookAt(target.transform);
            transform.Translate (Vector3.forward * moveSpeed * Time.deltaTime);
        }

    }

   /*
    private void FixedUpdate()
    {
        for (int i = 0; i < closestMob.Length; i++)
        {
            Debug.Log("Magnitude: " + Vector3.Distance(closestMob.transform.position, this.transform.position));
        }
    }
    */

}

Every time I run it, it gives me the debug log of the distance for both of the objects in question. If I can just get it to look at and move in the direction of the nearest entity with a given tag, that’s all I’m after.

Agreed - please read the post on code tags.

You could use distance or sqr magnitude.

If you have a list of n (number) of objects, one idea is to assign the closest enemy to the first one in the list. Next, go through the list starting at first+1 until the end and if if it’s closer, assign it as the closest enemy.
After the loop, you’ll have your target for look at/approach. :slight_smile:

Ah. Still getting used to these forums. Thanks for the tip.

@methos5k I think I get what you’re saying, as in have the closest enemy be listed as 0 in the array? If I am to do that, how do I specify it? Would I be stating that the minimum distance reorders the array? I’m not finding much material covering minimum values and arrays, or rather nothing I understand at this point.

Do you have any specifics of how to sort the Array? I get the concept of what I need to do, but not how I can do it.

There is an example of the approach here:

http://blog.boredmormongames.com/2014/07/cycling-through-targets-on-tower.html

Maybe his blog helped you?

My example, I meant that if your array had, let’s say 5 entries…Right away, you can say the closest enemy is enemy[0] in the array. Note, of course this isn’t necessarily going to be true, we just put it there to start.
Then, as you go through the array/list, if any entry is closer, we replace that variable “closest” with the entry.
When the loop is done, whatever is in that variable will be correct. :slight_smile:

That script has a few errors in it, two of which were easily resolved, but one still eludes my comprehension…

private List targets = new List;

and it reads: “A new expression requires (), [ ], {} after type” What exactly this means, I’m not quite sure yet.

I’m not sure how to express that though. I don’t know how to say “Find the minimum distance and make it [0]” Calling out the minimum is kind of my missing link at the moment.

Whoops. That needs to look like this

private List<transform> targets = new List<transform>();

I’m honestly surprised no one has pointed that out to me yet. I’ve been directing people there for a while now.

Not a problem. I open up the script and there’s red notches all up and down it!

Hrm… even fixing that more errors popped up. I had been shown this script before, but upon seeing the long list of errors, assumed it was for an old version of C#.

I also am more inclined to use an array approach, but am faced with the stumbling block of finding the lowest Vector3 distance and setting it to [0]

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class TargetFinder : MonoBehaviour
{

    // The main point of this class is to keep this target set
    // Hence we don't do anything unless something is actually looking for a target
    public Transform Target
    {
        get
        {
            return FindTarget();
        }
    }

    // This list holds all of the targets that are in range
    // I've chosen to populate the list using triggers
    // For a slower firing tower you could populate the list with an overlap sphere
    private List<transform> targets = new List<transform>();

    // The tower needs a trigger to determine if a target is in range
    // This code assumes the only colliders running around are enemies.
    //If not you will want to use tag checking or some other method to verify the collider is actually an enemy
    void OnTriggerEnter(Collider other)
    {
        targets.add(other.transform);
    }

    // The tower removes targets from the list that move out of range.
    void OnTriggerExit(Collider other)
    {
        if (targets.contains(other.transform))
        {
            targets.remove(other.transform);
        }
    }

    // This method is the work horse of the class
    // Be sure to understand this method, even if you don't get anything else
    private Transform FindTarget()
    {

        // First job is to see if there is anything in the list
        // No point running expensive code if nothing is in range
        if (targets.count <= 0)
        {
            return null;
        }

        // Now we remove everything that is null from the list
        // Null transforms get on the list when a target is in range and destroyed
        // This is called a lambda function. Don't ask how it works, but it does.
        targets.RemoveAll(item => item == null);

        // And then we check again if there are any items left
        if (targets.count <= 0)
        {
            return null;
        }

        // Now we check each remaining target in turn to see which one we should be aiming at
        // The code will check each possible target in turn to calculate a score
        // The score will be compared to the current best score
        // We will store the best target and score in bestTarget and bestScore
        Transform bestTarget;
        float bestScore = -999999;
        foreach (Transform currentTarget in targets)
        {
            float currentScore = ScoreTarget(currentTarget);
            if (currentScore > bestScore)
            {
                bestTarget = currentTarget;
                bestScore = currentScore;
            }
        }

        // Now our only job is to return the target we found
        return bestTarget;
    }

    // This method is used to score the targets
    // My implementation will just check the distance from the tower, closer targets get a higher score
    // However you can make this as complex as you like
    private float ScoreTarget(Transform target)
    {
        float score = 0;
        score = 100 - (target.position - transform.position).sqrMagnitude;
        return score;
    }
}

It currently displays 8 errors, mainly around the specified variables. it doesn’t like “transform” “add” “contains” “remove” “count” “foreach” or “bestTarget”

@Rehtael : A lot of those errors are an issue with capitalization.

targets.Count
//and 
targets.Add

for instance.

I’d recommend: Learn
and this, as well: Learn game development w/ Unity | Courses & tutorials in game design, VR, AR, & Real-time 3D | Unity Learn
The first is general, and you can choose somewhere to start.
The second has many topics on scripting and how you can use it in Unity. :slight_smile:

@methos5k

I’ve been brushing up on those lessons, but they tend to explain the idea more than practical use. And they don’t really mention how each thing interacts with other things. Great for conveying the purpose, but not the usage. They did a great job of showing me what an array is, but you know. Not how to really capitalize on them. Hence I come to the forums seeking more in-depth explanations.

Here’s how I do that kind of thing

        closestMob = GameObject.FindGameObjectsWithTag("mobTeamTwo");
        //target = GameObject.FindWithTag("mobTeamTwo");
        float distanceMin = float.MaxValue;
        int closestMobIndex = -1;
     
        for (int i = 0; i < closestMob.Length; i++)
        {
            float distCheck = Vector3.Distance(closestMob.transform.position, this.transform.position);
           
            if(distCheck < distanceMin){
              distanceMin = distCheck;
              closestMobIndex = i;
            }
        }
2 Likes

Thank you so very much. I’ll analyze this and see what I can learn from it.

@aaro4130

Tried out that script and tried getting the debug to tell me which of mobTeamTwo was in position [0] as shown below.

 void Start ()
    {
      
        //public int closestMobDistance = Mathf.Min(this.transform.position, closestMob.transform.position);
        closestMob = GameObject.FindGameObjectsWithTag("mobTeamTwo");
        //target = GameObject.FindWithTag("mobTeamTwo");
        float distanceMin = float.MaxValue;
        int closestMobIndex = -1;

        for (int i = 0; i < closestMob.Length; i++)
        {
         
            //The above and below currently feed out the same information.
          
            float distCheck = Vector3.Distance(closestMob[i].transform.position, this.transform.position);

            if(distCheck < distanceMin)
            {
                distanceMin = distCheck;
                closestMobIndex = i;
            }
            Debug.Log("Distance Check: " + closestMob[0]);
        }

      

    }

and it returned:

Distance Check: Cube (UnityEngine.GameObject)
UnityEngine.Debug:Log(Object)
MobOneScript:Start() (at Assets/MobOneScript.cs:38)

Distance Check: Cube (UnityEngine.GameObject)
UnityEngine.Debug:Log(Object)
MobOneScript:Start() (at Assets/MobOneScript.cs:38)

Distance Check: Cube (UnityEngine.GameObject)
UnityEngine.Debug:Log(Object)
MobOneScript:Start() (at Assets/MobOneScript.cs:38)

know that the Cube is the middle distance, between mobTwo and Sphere.

So, either I’m missing something on how to call upon the minimum distance, or the script has something slightly off.

For that user’s code, you want to do this: closestMob[closestMobIndex];
as opposed to closestMob[0]

Weird. Anyway, its the exact same approach as @aaro4130 proposed. So no point trying to fix it.

Well yes. You should be debugging closestMob[closestMobIndex].

Edit: Sniped

Thanks everyone!

No problem, man. Have fun with your game :slight_smile: