Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more..
    Dismiss Notice
  3. Dismiss Notice

How to find closest object within certain radius

Discussion in 'Scripting' started by trex0225, Mar 26, 2020.

  1. trex0225

    trex0225

    Joined:
    Jan 25, 2020
    Posts:
    2
    How would you find an object closest to a certain transform within a certain radius? I have a weapon pickup system that should pick up the closest weapon. I have it so when you press E, all objects with a weapon pickup class should check if they are the closest available weapon, and if they are, they should go to the player.
    Sorry for unnecessary stuff in the script -- I am relativily new to
    I have a is grounded variable because for some reason my rigidbodies aren't detecting the colliers.
    Here's my script:
    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class WeaponPickup : MonoBehaviour
    7. {
    8.     // Start is called before the first frame update
    9.     public int childNum;
    10.     public Transform playerTransform;
    11.     [SerializeField]
    12.     private bool pickedUp = false;
    13.     public List<Transform> otherWeapons;
    14.     public Rigidbody rb;
    15.     public Transform thisTransform;
    16.     [SerializeField]
    17.     private bool isGrounded = false;
    18.     public LayerMask groundMask;
    19.     public float groundDistance = 0.2f;
    20.     public Transform groundCheck;
    21.     public WeaponSwitching weaponHolder;
    22.     [SerializeField]
    23.     private bool selectedWeaponIsThisItem = false;
    24.     public bool stillGoing = true;
    25.     void Start()
    26.     {
    27.         rb.isKinematic = false;
    28.         rb.useGravity = true;
    29.     }
    30.     //public Transform target;
    31.     public float speed = 10f;
    32.     [SerializeField]
    33.     private bool goingTowards = false;
    34.     // Update is called once per frame
    35.     private void OnEnable()
    36.     {
    37.         rb.isKinematic = false;
    38.         rb.useGravity = true;
    39.         WeaponPickup.Pool.Add(this);
    40.     }
    41.     void Update()
    42.     {
    43.         isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
    44.         if(isGrounded)
    45.         {
    46.             rb.isKinematic = true;
    47.             pickedUp = false;
    48.             WeaponSwitching.holdingWeapon = false;
    49.         }
    50.         if (Input.GetKeyDown(KeyCode.Q))
    51.         {
    52.             if(!pickedUp)
    53.             {
    54.                 if(Vector3.Distance(thisTransform.position, playerTransform.position) < 5f)
    55.                 {
    56.                     if (WeaponSwitching.holdingWeapon == false)
    57.                     {
    58.                         goingTowards = true;
    59.                     } else
    60.                     {
    61.                         if (selectedWeaponIsThisItem)
    62.                         {
    63.                             pickedUp = false;
    64.                             throwSelf();
    65.                         }
    66.                     }
    67.                 }
    68.             } else
    69.             {
    70.                 if (selectedWeaponIsThisItem)
    71.                 {
    72.                     pickedUp = false;
    73.                     throwSelf();
    74.                 }
    75.             }
    76.         }
    77.         if(goingTowards)
    78.         {
    79.             stillGoing = false;
    80.             goTowards();
    81.             if(Vector3.Distance(thisTransform.position, playerTransform.position) < 1f)
    82.             {
    83.                 float curClosest = Vector3.Distance(thisTransform.position, playerTransform.position);
    84.                 if(FindClosestWeapon(thisTransform.position) == this)
    85.                 {
    86.                     stillGoing = true;
    87.                 }
    88.                 if (stillGoing)
    89.                 {
    90.                     pickedUp = true;
    91.                     goingTowards = false;
    92.                     WeaponSwitching.holdingWeapon = true;
    93.                     foreach (Renderer r in GetComponentsInChildren<Renderer>())
    94.                         r.enabled = false;
    95.                     WeaponSwitching.selectedWeapon = childNum;
    96.                     weaponHolder.SelectWeapon();
    97.                     selectedWeaponIsThisItem = true;
    98.                 }
    99.             }
    100.         }
    101.     }
    102.  
    103.     void goTowards()
    104.     {
    105.         rb.isKinematic = true;
    106.         rb.useGravity = false;
    107.         stillGoing = true;
    108.         //pickup
    109.         float step = speed * Time.deltaTime;
    110.             transform.position = Vector3.MoveTowards(transform.position, playerTransform.position, step);
    111.        
    112.     }
    113.  
    114.     void throwSelf()
    115.     {
    116.         stillGoing = false;
    117.         Debug.Log("Throw");
    118.         thisTransform.position = playerTransform.position + playerTransform.up*2;
    119.         foreach (Renderer r in GetComponentsInChildren<Renderer>())
    120.             r.enabled = true;
    121.         rb.isKinematic = false;
    122.         rb.useGravity = true;
    123.         rb.AddRelativeForce(10, 10, 0);
    124.         WeaponSwitching.selectedWeapon = 0;
    125.         weaponHolder.SelectWeapon();
    126.         selectedWeaponIsThisItem = false;
    127.     }
    128.  
    129.  
    130.     public readonly static HashSet<WeaponPickup> Pool = new HashSet<WeaponPickup>();
    131.  
    132.    
    133.  
    134.     private void OnDisable()
    135.     {
    136.         WeaponPickup.Pool.Remove(this);
    137.     }
    138.  
    139.  
    140.  
    141.     public static WeaponPickup FindClosestWeapon(Vector3 pos)
    142.     {
    143.         WeaponPickup result = null;
    144.         float dist = float.PositiveInfinity;
    145.         var w = WeaponPickup.Pool.GetEnumerator();
    146.         while (e.MoveNext())
    147.         {
    148.             float d = (w.Current.transform.position - pos).sqrMagnitude;
    149.             if (d < dist)
    150.             {
    151.                 result = w.Current;
    152.                 dist = d;
    153.             }
    154.         }
    155.         return result;
    156.     }
    157. }
    158.  
     
  2. ptgodz

    ptgodz

    Joined:
    Aug 4, 2016
    Posts:
    106
    You could put your WeaponPickup class on your player object to save each weapon having to run the script. It will be more efficient if you have more than 1 weapon in your scene.

    I'd personally just use Physics.OverlapSphere and check for any colliders with a Weapon class attached. If there were any collisions, then you could just loop through each of them and perform a distance check on the weapons position vs player position, and pick the closet one
     
  3. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Once you do Physics.OverlapSphere as suggested by ptgodz
    you can try and use my min max comparators to find the nearest one (scroll to the end for the actual distance checkers)

    i.e.
    Code (csharp):
    1. var result = Physics.OverlapSphere(...);
    2.  
    3. var pts = new Vector3[result.Length];
    4. for(int i = 0; i < result.Length; i++) pts[i] = result[i].transform.position;
    5.  
    6. var nearestPoint = player.transform.position.NearestOf(out int index, out float distance, pts);
    7. Debug.Log($"Nearest object is {result[index].gameObject.name} at a distance of {distance}");
    you might as well make your own variant that accepts Collider[] right off the bat (instead of Vector3[]).

    in that case, you can avoid the point population step
    Code (csharp):
    1. var result = Physics.OverlapSphere(...);
    2.  
    3. var nearestPoint = player.transform.position.NearestOf(out int index, out float distance, result);
    4. Debug.Log($"Nearest object is {result[index].gameObject.name} at a distance of {distance}");
     
    Last edited: Mar 26, 2020
    HeyItsLollie likes this.
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    here, I modified it to work with Collider[] for your case, just add this to the library

    Code (csharp):
    1.  
    2.   static private int nearest_impl(in Vector3 point, out float sqrDistance, Collider[] colliders) {
    3.     if(colliders == null) throw new ArgumentNullException();
    4.     if(colliders.Length == 0) throw new IndexOutOfRangeException();
    5.  
    6.     int index = 0;
    7.     sqrDistance = sqrdist(point, colliders[0].transform.position);
    8.  
    9.     float sqrdist(in Vector3 a, in Vector3 b) {
    10.       float x = a.x - b.x, y = a.y - b.y, z = a.z - b.z;
    11.       return x * x + y * y + z * z;
    12.     }
    13.  
    14.     if(colliders.Length > 1) {
    15.       int hlen = colliders.Length >> 1;
    16.       float sd = 0f;
    17.       for(int i = 0, j = colliders.Length - 1; i <= hlen; i++, j--) {
    18.         if(i < hlen || (colliders.Length & 1) != 0) {
    19.           if(i > 0) {
    20.             sd = sqrdist(point, colliders[i].transform.position);
    21.             if(sd < sqrDistance) { sqrDistance = sd; index = i; }
    22.           }
    23.           if(i < hlen) {
    24.             sd = sqrdist(point, colliders[j].transform.position);
    25.             if(sd < sqrDistance) { sqrDistance = sd; index = j; }
    26.           }
    27.         }
    28.       }
    29.     }
    30.  
    31.     return index;
    32.   }
    33.  
    34.   static public Vector3 NearestOf(this Vector3 point, params Collider[] colliders)
    35.     => colliders[nearest_impl(point, out _, colliders)].transform.position;
    36.  
    37.   static public Vector3 NearestOf(this Vector3 point, out float distance, params Collider[] colliders) {
    38.     int index = nearest_impl(point, out distance, colliders);
    39.     distance = Mathf.Sqrt(distance);
    40.     return colliders[index].transform.position;
    41.   }
    42.  
    43.   static public Vector3 NearestOf(this Vector3 point, out int index, params Collider[] colliders) {
    44.     index = nearest_impl(point, out _, colliders);
    45.     return colliders[index].transform.position;
    46.   }
    47.  
    48.   static public Vector3 NearestOf(this Vector3 point, out int index, out float distance, params Collider[] colliders) {
    49.     index = nearest_impl(point, out distance, colliders);
    50.     distance = Mathf.Sqrt(distance);
    51.     return colliders[index].transform.position;
    52.   }
    53.  
     
    HeyItsLollie and DMorock like this.