Search Unity

Trying to find the minimum Vector3.distance from an array

Discussion in 'Scripting' started by Rehtael, Jul 3, 2017.

  1. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    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.):

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class MobOneScript: MonoBehaviour
    5. {
    6.     public GameObject target;
    7.  
    8.  
    9.     public float moveSpeed = 5f;
    10.  
    11.     public GameObject[] closestMob;
    12.  
    13.     //float distance = Vector3.Distance(this.transform.position, GameObject.FindGameObjectsWithTag("mobTeamTwo"));
    14.  
    15.     // Use this for initialization
    16.     void Start ()
    17.     {
    18.  
    19.         //public int closestMobDistance = Mathf.Min(this.transform.position, closestMob.transform.position);
    20.         closestMob = GameObject.FindGameObjectsWithTag("mobTeamTwo");
    21.         //target = GameObject.FindWithTag("mobTeamTwo");
    22.  
    23.  
    24.         for (int i = 0; i < closestMob.Length; i++)
    25.         {
    26.             /*
    27.             float mobInQuestion = closestMob.transform.position;
    28.             float thisOne = this.transform.position;
    29.  
    30.             float distCheck = Mathf.Min(mobInQuestion, thisOne);
    31.             */
    32.             Debug.Log("Magnitude: " + (Vector3.Distance(closestMob.transform.position, this.transform.position)));
    33.  
    34.             //The above and below currently feed out the same information.
    35.  
    36.             float distCheck = Vector3.Distance(closestMob.transform.position, this.transform.position);
    37.             Debug.Log("Distance Check: " + (Mathf.Min(distCheck)));
    38.         }
    39.  
    40.      
    41.  
    42.     }
    43.  
    44.    // Update is called once per frame
    45.     void Update ()
    46.     {
    47.  
    48.  
    49.         //transform.LookAt (target.transform);
    50.  
    51.  
    52.         if (Vector3.Distance(this.transform.position, target.transform.position)>= 10.0f)
    53.         {
    54.             transform.LookAt(target.transform);
    55.             transform.Translate (Vector3.forward * moveSpeed * Time.deltaTime);
    56.         }
    57.  
    58.     }
    59.  
    60.    /*
    61.     private void FixedUpdate()
    62.     {
    63.         for (int i = 0; i < closestMob.Length; i++)
    64.         {
    65.             Debug.Log("Magnitude: " + Vector3.Distance(closestMob.transform.position, this.transform.position));
    66.         }
    67.     }
    68.     */
    69.  
    70. }


    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.
     
    Last edited: Jul 3, 2017
  2. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
  3. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    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. :)
     
  4. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    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.
     
  5. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    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.
     
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
  7. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    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. :)
     
  8. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    That script has a few errors in it, two of which were easily resolved, but one still eludes my comprehension...

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

    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.
     
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Whoops. That needs to look like this

    Code (csharp):
    1. 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.
     
  10. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    Not a problem. I open up the script and there's red notches all up and down it!
     
  11. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    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]

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class TargetFinder : MonoBehaviour
    6. {
    7.  
    8.     // The main point of this class is to keep this target set
    9.     // Hence we don't do anything unless something is actually looking for a target
    10.     public Transform Target
    11.     {
    12.         get
    13.         {
    14.             return FindTarget();
    15.         }
    16.     }
    17.  
    18.     // This list holds all of the targets that are in range
    19.     // I've chosen to populate the list using triggers
    20.     // For a slower firing tower you could populate the list with an overlap sphere
    21.     private List<transform> targets = new List<transform>();
    22.  
    23.     // The tower needs a trigger to determine if a target is in range
    24.     // This code assumes the only colliders running around are enemies.
    25.     //If not you will want to use tag checking or some other method to verify the collider is actually an enemy
    26.     void OnTriggerEnter(Collider other)
    27.     {
    28.         targets.add(other.transform);
    29.     }
    30.  
    31.     // The tower removes targets from the list that move out of range.
    32.     void OnTriggerExit(Collider other)
    33.     {
    34.         if (targets.contains(other.transform))
    35.         {
    36.             targets.remove(other.transform);
    37.         }
    38.     }
    39.  
    40.     // This method is the work horse of the class
    41.     // Be sure to understand this method, even if you don't get anything else
    42.     private Transform FindTarget()
    43.     {
    44.  
    45.         // First job is to see if there is anything in the list
    46.         // No point running expensive code if nothing is in range
    47.         if (targets.count <= 0)
    48.         {
    49.             return null;
    50.         }
    51.  
    52.         // Now we remove everything that is null from the list
    53.         // Null transforms get on the list when a target is in range and destroyed
    54.         // This is called a lambda function. Don't ask how it works, but it does.
    55.         targets.RemoveAll(item => item == null);
    56.  
    57.         // And then we check again if there are any items left
    58.         if (targets.count <= 0)
    59.         {
    60.             return null;
    61.         }
    62.  
    63.         // Now we check each remaining target in turn to see which one we should be aiming at
    64.         // The code will check each possible target in turn to calculate a score
    65.         // The score will be compared to the current best score
    66.         // We will store the best target and score in bestTarget and bestScore
    67.         Transform bestTarget;
    68.         float bestScore = -999999;
    69.         foreach (Transform currentTarget in targets)
    70.         {
    71.             float currentScore = ScoreTarget(currentTarget);
    72.             if (currentScore > bestScore)
    73.             {
    74.                 bestTarget = currentTarget;
    75.                 bestScore = currentScore;
    76.             }
    77.         }
    78.  
    79.         // Now our only job is to return the target we found
    80.         return bestTarget;
    81.     }
    82.  
    83.     // This method is used to score the targets
    84.     // My implementation will just check the distance from the tower, closer targets get a higher score
    85.     // However you can make this as complex as you like
    86.     private float ScoreTarget(Transform target)
    87.     {
    88.         float score = 0;
    89.         score = 100 - (target.position - transform.position).sqrMagnitude;
    90.         return score;
    91.     }
    92. }
    93.  
    It currently displays 8 errors, mainly around the specified variables. it doesn't like "transform" "add" "contains" "remove" "count" "foreach" or "bestTarget"
     
    Last edited: Jul 4, 2017
  12. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
  13. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    @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.
     
  14. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    490
    Here's how I do that kind of thing

    Code (CSharp):
    1.         closestMob = GameObject.FindGameObjectsWithTag("mobTeamTwo");
    2.         //target = GameObject.FindWithTag("mobTeamTwo");
    3.         float distanceMin = float.MaxValue;
    4.         int closestMobIndex = -1;
    5.      
    6.         for (int i = 0; i < closestMob.Length; i++)
    7.         {
    8.             float distCheck = Vector3.Distance(closestMob.transform.position, this.transform.position);
    9.            
    10.             if(distCheck < distanceMin){
    11.               distanceMin = distCheck;
    12.               closestMobIndex = i;
    13.             }
    14.         }
     
    Rehtael and Kiwasi like this.
  15. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    Thank you so very much. I'll analyze this and see what I can learn from it.
     
  16. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    @aaro4130

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

    Code (CSharp):
    1.  void Start ()
    2.     {
    3.      
    4.         //public int closestMobDistance = Mathf.Min(this.transform.position, closestMob.transform.position);
    5.         closestMob = GameObject.FindGameObjectsWithTag("mobTeamTwo");
    6.         //target = GameObject.FindWithTag("mobTeamTwo");
    7.         float distanceMin = float.MaxValue;
    8.         int closestMobIndex = -1;
    9.  
    10.         for (int i = 0; i < closestMob.Length; i++)
    11.         {
    12.          
    13.             //The above and below currently feed out the same information.
    14.          
    15.             float distCheck = Vector3.Distance(closestMob[i].transform.position, this.transform.position);
    16.  
    17.             if(distCheck < distanceMin)
    18.             {
    19.                 distanceMin = distCheck;
    20.                 closestMobIndex = i;
    21.             }
    22.             Debug.Log("Distance Check: " + closestMob[0]);
    23.         }
    24.  
    25.      
    26.  
    27.     }
    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.
     
  17. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    For that user's code, you want to do this:
    Code (csharp):
    1. closestMob[closestMobIndex];
    as opposed to closestMob[0]
     
  18. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    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
     
  19. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    Thanks everyone!
     
  20. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    No problem, man. Have fun with your game :)
     
  21. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    Hm. Might not be done yet. Sadly, the LookAt tutorial doesn't cover what happens when something is trying to look at two things at once and you want it to stop. Even though the lookat specifies to look at the nearest of the tagged enemy, it does that until another tagged enemy comes in range, then it tries to do both, even breaking the x and z rotation constraints.

    With it being in Update, shouldn't the line that makes it look toward the closest thing stop applying to whatever is no longer the closest thing? Do I need to add in a line telling it to stop?
     
  22. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    It's a little hard to guess at what exactly you mean. LookAt only works on 1 target at a time. It can break the constraints, though, because those are physics constraints (if that's what you had meant), and I believe have no bearing on if you change the rotation yourself, as opposed to the engine doing it.

    Can you post your updated code? Perhaps I or someone else can help you understand what might be wrong.
    Also, in the Update loop, is it maybe because two objects are "switching places": like one is closer, then soon after a different one is? Anyhow, code would certainly help shed some light.
     
  23. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    Code (CSharp):
    1. void Update ()
    2.     {
    3.  
    4.         //public int closestMobDistance = Mathf.Min(this.transform.position, closestMob.transform.position);
    5.         closestMob = GameObject.FindGameObjectsWithTag("mobTeamTwo");
    6.         //target = GameObject.FindWithTag("mobTeamTwo");
    7.         float distanceMin = float.MaxValue;
    8.         int closestMobIndex = -1;
    9.  
    10.         for (int i = 0; i < closestMob.Length; i++)
    11.         {
    12.  
    13.             //The above and below currently feed out the same information.
    14.  
    15.             float distCheck = Vector3.Distance(closestMob[i].transform.position, this.transform.position);
    16.  
    17.             if (distCheck < distanceMin)
    18.             {
    19.                 distanceMin = distCheck;
    20.                 closestMobIndex = i;
    21.             }
    22.            // Debug.Log("Distance Check: " + closestMob[closestMobIndex]);
    23.         }
    24.  
    25.         //transform.LookAt (target.transform);
    26.  
    27.  
    28.         if (Vector3.Distance(this.transform.position, closestMob[closestMobIndex].transform.position)>= 5.0f)
    29.         {
    30.             transform.LookAt(closestMob[closestMobIndex].transform.position);
    31.             transform.Translate (Vector3.forward * moveSpeed * Time.deltaTime);
    32.         }
    33.  
    34.     }
    EDIT: mobTwo is set to just move backwards if mobOne gets too close. Then mobOne freezes up and tries to look between them.
     
    Last edited: Jul 5, 2017
  24. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    Oh, forgot to quote or @ you.
     
  25. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I saw your reply, without quoting me, don't worry. I am just not sure what the issue is.
    All I know is that LookAt doesn't look at two things at once. When you call it, it looks there and that's it..

    If you want it to always pick the closest, that's what it'll do. If you want it to stay on 1 until something happens, then that means you don't want it to switch once it's found one (closest) target.. Beyond that, I'm not sure what's wrong from your explanation or even code, atm.
     
  26. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    I wish that were the case. Once it meets the conditions for two tagged targets at once, it splits between them. The capsule tilts at an odd angle, where it's not quite looking at the cube.

    I just tried rearranging everything and found it's no longer tilting at the cube, despite not changing the script, but now it stays looking at mobTwo, despite the cube being closer, and mobOne stops moving because the tagged cube is now within range.

    So... Now I'm slightly less but mostly more confused.

    Edit: So this time it DID tilt toward the cube, but it didn't the time before despite me not changing anything.

    Edit: Ran it again without changing anything again, and it stayed looking at mobTwo this time.
     
  27. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    From your posts + edits, I think it's fair to say that it's understandable how you (and I) are confused :)
     
  28. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    My only guess is that it's alternating between which one meets the conditions per frame, and it keeps changing because of an unlocked framerate.

    Beyond that I've got no clue.

    If that IS true, it's probably not looking at the nearest mobTeamTwo tag
     
  29. Rehtael

    Rehtael

    Joined:
    Jun 20, 2017
    Posts:
    53
    On a hunch I tried changing update to fixed update, and it's not tilting anymore, but it's not turning to face the cube, even when it stops because the cube is the closest thing.
     
  30. Sarmad110

    Sarmad110

    Joined:
    Apr 18, 2019
    Posts:
    3
    I just put this code from my old programming tasks

    GameObject [] target;
    float zombie,zd;
    void findZombies()
    {
    int index=0;
    target = GameObject.FindGameObjectsWithTag("zombie");
    foreach (GameObject gb in target)
    {
    zd = Vector3.Distance(gb.transform.position, transform.position);
    if(index==0){
    {
    zombie=zd;
    }
    if (index > 0)
    {
    if (zombie > zd)
    {
    zombie = zd;
    foundIndex = index;
    }
    }
    index++;
    }
    agent.SetDestination(target[foundIndex].transform.position);
    }
     
    Last edited: Oct 8, 2020
  31. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,108