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

Are RaycastHit-arrays returned from RaycastAll in proper order?

Discussion in 'Scripting' started by MikeTeavee, Feb 10, 2016.

Thread Status:
Not open for further replies.
  1. MikeTeavee

    MikeTeavee

    Joined:
    May 22, 2015
    Posts:
    194
    I'm making a simple FPS zombie game and I made some code that uses a RaycastAll to shoot zombies. It's pretty neat code because it allows multiple headshots in a single shot, as well as stopping bullets after they hit a wall. It also detects if a door is open or not to determine if the bullet is blocked (this had to be added since my door is a intricate imported mesh.)
    (BTW some LayerMasks will have to be added later since things such as triggers block shots as well :p)

    This is all done with a foreach-loop by checking if an object is an obstruction, and if it is, breaks the loop thus preventing further interaction to take place. Good thing is that it seems to work fine, but I want to know if this is efficient code. I'm nervous because I read a forum post from 2011 that said that the array returned by RaycastAll is arbitrary, meaning this person claimed that the shot to the zombie could be blocked even in a clear line of sight due to RaycastHit-array being out of order... Is this true?

    Here's my code. Tell me what you think. Perhaps there's a better way to have walls block bullets while allowing multi-headshots?

    Code (CSharp):
    1.      
    2. // declared elsewhere
    3. RaycastHit [] multiHitInfo;
    4.  
    5.  
    6. /* RaycastAll as bullet path to interact with enemies
    7. (raycast comes from parent since pistol is off center)
    8. */
    9.         multiHitInfo = (Physics.RaycastAll(transform.parent.position, transform.parent.forward, Mathf.Infinity));
    10.         foreach (RaycastHit rh in multiHitInfo) {
    11.             GunTargetObject = rh.transform.gameObject;
    12.             if (GunTargetObject.tag == "ZombieHead") {
    13.                 Debug.Log("Zombie shot in head!");
    14.                 GunTargetObject.GetComponentInParent<GirlZombieScript>().GetHeadShot();
    15.             } else if (GunTargetObject.tag == "Door" && GunTargetObject.GetComponent<MyDoorScript>().DoorIsOpen == true) {
    16.                 Debug.Log("Bullet was shot through open door");
    17.             } else {
    18.                 Debug.Log("Bullet was stopped by solid object");
    19.             break;
    20.         }
    21. }

     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,514
    Always read the documentation first:
    http://docs.unity3d.com/ScriptReference/Physics.RaycastAll.html

    Of course, the unity docs don't always inform you of the little quarks of their methods. But check none the less, cause often they do tell you, and it only takes 5 seconds.
     
    RafaelGomes00 likes this.
  3. MikeTeavee

    MikeTeavee

    Joined:
    May 22, 2015
    Posts:
    194
    Oops you're right. Damn, what the hell do I do now?
     
  4. frekiidoochmanz

    frekiidoochmanz

    Joined:
    Feb 7, 2016
    Posts:
    158
    You could try launching a sphere at each object with the ray and if it collides with anything in its path in the direction append a list of objects it hit to a list which should be in order and maybe assign each object in a list a variable to keep track of the order and call on this object which was launched and returned the list. While sorting through this list add an if conditional for walls and destroy it or disable or end the loop if hitting it.

    sorry im not sure what you could do but this is one general idea. you could maybe make bullets work this way too for multiple headshots even, (since the sphere of collision could be modified in size.
     
  5. LordKawaii

    LordKawaii

    Joined:
    Nov 24, 2014
    Posts:
    10
    You might also be able to get away with a foreach loop that just checks the distance from the player for each of the returned item's transforms and then sort them in an array. Then you can use that array like a stack and just pop each item off the top and deal with each one till you hit an impenetrable object.
     
    trombonaut, mutty0 and Kiwasi like this.
  6. flaminghairball

    flaminghairball

    Joined:
    Jun 12, 2008
    Posts:
    868
    Again, read the docs :p http://docs.unity3d.com/ScriptReference/RaycastHit.html
    RaycastHit has a distance property that you can use to sort the results by distance, then use your existing obstruction-check code.
     
    Typhereus and Kiwasi like this.
  7. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,514
    System.Array.Sort:
    https://msdn.microsoft.com/en-us/library/cxt053xf(v=vs.110).aspx

    Here is the delegate based version.

    Code (csharp):
    1.  
    2. multiHitInfo =(Physics.RaycastAll(transform.parent.position, transform.parent.forward, Mathf.Infinity));
    3. System.Array.Sort(multiHitInfo, (x,y) => x.distance.CompareTo(y.distance));
    4.  
    5. foreach(var rh in multiHitInfo)
    6. //...
    7.  
    There are other ways as well. For example in here you can see how I perform a lot of physics tests, while avoiding garbage collection, and including sorting using an IComparer called 'RaycastHitDistanceComparer'.
    https://github.com/lordofduct/space...lob/master/SpacepuppyBase/Geom/PhysicsUtil.cs
     
    RafaelGomes00, D0C_, Novack and 5 others like this.
  8. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
  9. MikeTeavee

    MikeTeavee

    Joined:
    May 22, 2015
    Posts:
    194
    OK thanks for the help everyone, I've decided in the future to READ THE DOCS! ... and not just the Unity books on my desk...which also, advise reading the docs. *ahem*:rolleyes:

    btw, nice code lordofduct.
     
    sonictimm and Bovine like this.
  10. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,068
    A little necro here, but it is still important.

    They should call it RaycastAllUnordered.

    And RaycastAll should be ordered.
     
  11. hurleybird

    hurleybird

    Joined:
    Mar 4, 2013
    Posts:
    257
    Yeah, but the other way around. Make a RaycastAllOrdered so that you don't change the behavior of the existing method.
     
    Ruslank100, sonictimm and koirat like this.
  12. paolo-rebelpug

    paolo-rebelpug

    Joined:
    Sep 27, 2021
    Posts:
    22
    Hey there, more necro here but it's worth it since I landed on this post as well today ehhe.

    We solved this issue as follow:

    Code (CSharp):
    1. var hits = Physics.RaycastNonAlloc(mousePositionRay, _results, 200f, mouseInteractionSettings.InteractiveLayers);
    2.              
    3. if (hits == 0)
    4. {
    5. Skip();
    6. }
    7.  
    8. Array.Sort(_results, 0, hits, _raycastDistanceComparer);
    Where _raycastDistanceComparer is an instance of the following class:
    Code (CSharp):
    1. public class RaycastDistanceComparer : IComparer<RaycastHit>
    2.     {
    3.         public int Compare(RaycastHit x, RaycastHit y)
    4.         {
    5.             var colliderInstanceIDComparison = x.colliderInstanceID.CompareTo(y.colliderInstanceID);
    6.             if (colliderInstanceIDComparison != 0) return colliderInstanceIDComparison;
    7.             var distanceComparison = x.distance.CompareTo(y.distance);
    8.             if (distanceComparison != 0) return distanceComparison;
    9.             return x.triangleIndex.CompareTo(y.triangleIndex);
    10.         }
    11.     }
    Hope this help :)
     
    EJSainz likes this.
  13. EJSainz

    EJSainz

    Joined:
    Mar 24, 2015
    Posts:
    30
    This is exactly what we needed. We are making some shooting game with fast moving projectiles that can't be neither colliders nor triggers because reasons. I had already reached to the point where I knew we needed something like this, but you saved us lot of time, thanks!
     
Thread Status:
Not open for further replies.