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. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Pick random point inside box collider

Discussion in 'Scripting' started by mrCharli3, Jul 21, 2018.

  1. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    956
    Using this script i almost get the effect I want, but some points that get picked are a bit outside the box collider. Am I missing something?

    Code (CSharp):
    1. private Vector3 RandomPointInBox()
    2.     {
    3.         return _collider.bounds.center + new Vector3(
    4.            (Random.value - 0.5f) * _collider.bounds.size.x,
    5.            (Random.value - 0.5f) * _collider.bounds.size.y,
    6.            (Random.value - 0.5f) * _collider.bounds.size.z
    7.         );
    8.     }
     
  2. hasanbayat

    hasanbayat

    Joined:
    Oct 18, 2016
    Posts:
    626
    You can simply use Random.Range instead and get a random point inside a Bounds, like this:
    Code (CSharp):
    1. public static Vector3 RandomPointInBounds(Bounds bounds) {
    2.     return new Vector3(
    3.         Random.Range(bounds.min.x, bounds.max.x),
    4.         Random.Range(bounds.min.y, bounds.max.y),
    5.         Random.Range(bounds.min.z, bounds.max.z)
    6.     );
    7. }
    And use the method like this:
    Code (CSharp):
    1. RandomPointInBounds(myCollider.bounds);
    Hope this helps.
    Thanks.
     
  3. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    956
    This is really nice, but do you know of a solution that takes the rotation of the transform into account? :) This will always act as if the rotation is 0,0,0.
     
  4. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Collider bounds always form an AABB. You'll need to cache the original bounds, without rotation, and then use TransformPoint on them to form an OBB.
     
  5. mrCharli3

    mrCharli3

    Joined:
    Mar 22, 2017
    Posts:
    956
    Cool, whats an OBB?
     
  6. Doug_B

    Doug_B

    Joined:
    Jun 4, 2017
    Posts:
    1,596
    AABB is Axis-Aligned. It never rotates, rather shrinks and grows to contain the entire object.

    An OBB is Object-Aligned. It does not need to shrink or grow as it follows the orientation of the enclosed object. GroZZleR's point is that Unity handles the AABB but it is up to you to handle the OBB yourself.
     
    kalechips_, Chambers88 and mrCharli3 like this.
  7. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    360
    So does anyone have the *complete* code to do this that works with rotation, scale and position of the box collider on a gameObject?
     
  8. tetto_green

    tetto_green

    Joined:
    Dec 22, 2015
    Posts:
    35
    I use collider.ClosestPoint method to know if the point is inside. The point is inside if the returned vector is equal to the point, otherwise it's out of the collider:
    Code (CSharp):
    1. Vector3 GetRandomPointInCollider()
    2.     {
    3.         var point = new Vector3(
    4.             Random.Range(collider.bounds.min.x, collider.bounds.max.x),
    5.             Random.Range(collider.bounds.min.y, collider.bounds.max.y),
    6.             Random.Range(collider.bounds.min.z, collider.bounds.max.z)
    7.         );
    8.  
    9.         if (point != collider.ClosestPoint(point))
    10.         {
    11.             Debug.Log("Out of the collider! Looking for other point...");
    12.             point = GetRandomPointInVolume();
    13.         }
    14.  
    15.         return point;
    16.     }
     
    Last edited: Aug 26, 2019
  9. OpticalOverride

    OpticalOverride

    Joined:
    Jan 13, 2013
    Posts:
    161
    Came here looking for a quick copy/paste job, didn't get it :(

    Unfortunately I'm not a huge fan of tetto_green's recursive method. While it works, it could theoretically loop infinitely (although the probability is it won't). My code mathematically gets a point within the bounds on the "first try", so to speak.

    So for any future lazy programmers like me, here's the copy/paste. ;)

    First, here's the "nice way" as an extension to the BoxCollider class because I'm fancy and like things clean.
    Code (CSharp):
    1.  
    2. public static Vector3 GetRandomPointInsideCollider( this BoxCollider boxCollider )
    3. {
    4.     Vector3 extents = boxCollider.size / 2f;
    5.     Vector3 point = new Vector3(
    6.         Random.Range( -extents.x, extents.x ),
    7.         Random.Range( -extents.y, extents.y ),
    8.         Random.Range( -extents.z, extents.z )
    9.     );
    10.  
    11.     return boxCollider.transform.TransformPoint( point );
    12. }
    13.  
    For anyone who doesn't know, make sure to place the above code inside a static class, so something like "public static MyExtensions { ...(code here) }". Then it can be used as
    Code (CSharp):
    1. BoxCollider b;
    2. ...
    3. Vector3 point = b.GetRandomPointInsideCollider();
    And here's the simple method in case someone is unfamiliar with extension methods:
    Code (CSharp):
    1.  
    2. public Vector3 GetRandomPointInsideCollider( BoxCollider boxCollider )
    3. {
    4.     Vector3 extents = boxCollider.size / 2f;
    5.     Vector3 point = new Vector3(
    6.         Random.Range( -extents.x, extents.x ),
    7.         Random.Range( -extents.y, extents.y ),
    8.         Random.Range( -extents.z, extents.z )
    9.     );
    10.  
    11.     return boxCollider.transform.TransformPoint( point );
    12. }
    13.  
    Enjoy!
     
    Last edited: Mar 24, 2021
  10. MikeOder

    MikeOder

    Joined:
    Apr 8, 2016
    Posts:
    7
    OpticalOverride - this is great. If your boxcolliders aren't centered, you'll also want to add boxCollider.center to your point value before you transform it:

    Code (CSharp):
    1. public Vector3 GetRandomPointInsideCollider( BoxCollider boxCollider )
    2. {
    3.     Vector3 extents = boxCollider.size / 2f;
    4.     Vector3 point = new Vector3(
    5.         Random.Range( -extents.x, extents.x ),
    6.         Random.Range( -extents.y, extents.y ),
    7.         Random.Range( -extents.z, extents.z )
    8.     )  + boxCollider.center;
    9.     return boxCollider.transform.TransformPoint( point );
    10. }
     
  11. Chrispykins

    Chrispykins

    Joined:
    Dec 23, 2019
    Posts:
    5
    I had to implement these functions for the 2D physics system, so here's the code for a
    BoxCollider2D
    and a
    CircleCollider2D
    using extension methods to get a cleaner syntax:

    Code (CSharp):
    1.  
    2. public static Vector2 GetRandomPoint(this BoxCollider2D box) {
    3.  
    4.         Vector2 extents = box.size * 0.5f;
    5.  
    6.         Vector2 localPoint = new Vector2(
    7.             Random.Range(-extents.x, extents.x),
    8.             Random.Range(-extents.y, extents.y)
    9.         );
    10.  
    11.         localPoint += box.offset;
    12.  
    13.         return box.transform.TransformPoint(localPoint);
    14.     }
    15.  
    16.     public static Vector2 GetRandomPoint(this CircleCollider2D circle) {
    17.  
    18.         float angle = Random.Range(0, Mathf.PI * 2);
    19.         float distance = circle.radius * Mathf.Sqrt(Random.Range(0f, 1f)); // sqrt for even distribution
    20.  
    21.         Vector2 localPoint = new Vector2(distance * Mathf.Cos(angle), distance * Mathf.Sin(angle));
    22.         localPoint += circle.offset;
    23.  
    24.         return circle.transform.TransformPoint(localPoint);
    25.     }
    One note: Unity's circle collider always remains a circle even under scaling transformations, but this version scales the random point according to the transform, so you can use this to generate random points in an ellipse as well.

    Usage is like this:
    box.GetRandomPoint()
    or
    circle.GetRandomPoint()

    I also have one for
    CapsuleCollider2D
    , if anyone's interested, but its kind of a mess and it only handles local scaling correctly. It doesn't work properly if the parent has non-uniform scaling.
     
    Last edited: Jun 30, 2023