Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Creative solution needed: Need to adjust reach of grab mechanic

Discussion in 'Scripting' started by Not_Sure, Oct 28, 2020.

  1. Not_Sure

    Not_Sure

    Joined:
    Dec 13, 2011
    Posts:
    3,546
    So I've got a ray that shoots out from my camera in an FPS to highlight and grab items.

    I'm have an issue with the reach that needs a creative solution.

    Obviously being a straight shot from the camera, items that are at the same height as the camera can be grabbed at further distances on the xz plain.

    I'd like it so that the ray changes length based on where I'm looking, but REALLY don't want to run Pythagorean's theory every damn frame.

    One thought I had was to add a capsule, shoot a ray from an empty gameobject towards the camera, take where it hits, then use the distance from that hit to the camera to get the range, and finally use a second ray to actually cast to items.

    Seems like overkill though.

    Any thoughts?

     
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,070
    Have you actually tested this theory of yours? Sounds like you know the computers inside and out.
    Besides you don't need a raycast, just a distance to the object. But good luck doing it without the "expensive" Pythagoras theorem. I've met a guy once who did this with ifs, and to this day I can't possibly fathom how. But what do you know, it seems there is this advanced alien knowledge out there, and some people have access to it, to do wonders in their frames in zero time.
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,520
    Take the delta from the raycast to the touch point:

    Code (csharp):
    1. Vector3 delta = hitInfo.point - ray.origin;
    Now flatten the Y:

    Code (csharp):
    1. delta.y = 0;
    Now presto you can check
    delta.magnitude
    and get the effect you list as "What I want"

    PS: computers can do a LOT of Pythagorean theories every frame: every call to .magnitude does one. Don't let it fret you, unless you have like 10,000 raycasts each frame. With one or ten, you'll be fine.
     
  4. VishwasGagrani

    VishwasGagrani

    Joined:
    May 12, 2018
    Posts:
    84
  5. Not_Sure

    Not_Sure

    Joined:
    Dec 13, 2011
    Posts:
    3,546
    How would sphere cast help?

    I’m casting a ray to select items to pick up.

    my problem is that things on the ground require the player to get much closer to the item.

    If I used a sphere cast it would hit multiple items, and I don’t see how that would address the issue at all.
     
  6. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,070
    If you have many items and this is why you want to avoid Pythagoras, you're supposed to limit the number of objects that are within a certain radius, by doing something that is cheap. This process is known as culling and should be enough to guarantee that you'll never compute more than 2-4 distances per frame.

    The cheapest way to cull is to implement an axis-aligned bounding box check, and then confirm the nearest object by computing squared distances, which is a lightning fast computation (sqrDistance = x * x + y * y).

    If you do need to compute the actual distance in the end, you finally compute the square root of the winning result. (distance = Mathf.sqrt(sqrDistance))

    In pseudocode
    Code (csharp):
    1. List<GameObject> candidates = testAABB(items, player.bounds);
    2. float[] distances = findNearestOf(candidates, out int index);
    3. Debug.Log($"nearest object is {candidates[index].name} at a distance of {distances[index]}");
     
    Last edited: Oct 29, 2020
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,070
    If you want an efficient distance comparator, you can grab my Min/Max extensions specifically tailored for this job. And if you don't need the actual distance in the end, but just an index of the nearest point, you can use the method overload without it.
     
  8. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,070
    Here's one possible implementation
    Code (csharp):
    1. int FindNearestColliderInside(Bounds bounds, List<Collider> items, out float distance) {
    2.   var bestSqrDist = float.PositiveInfinity;
    3.   int bestIndex = -1;
    4.  
    5.   for(int i = 0; i < items.Count; i++) {
    6.     if(bounds.Intersects(items[i].bounds)) {
    7.       var diff = items[i].bounds.center - bounds.center;
    8.       var sqrDist = Vector3.Dot(diff, diff);
    9.       if(sqrDist < bestSqrDist) {
    10.         bestSqrDist = sqrDist;
    11.         bestIndex = i;
    12.       }
    13.     }
    14.   }
    15.  
    16.   distance = (bestIndex >= 0)? Mathf.Sqrt(bestSqrDist) : 0f;
    17.   return bestIndex;
    18. }
    edit: oops made a typo
     
    Last edited: Oct 29, 2020