Search Unity

Question Hit detection of a HE Grenade

Discussion in 'Scripting' started by danielkutifa, Jan 12, 2022.

  1. danielkutifa

    danielkutifa

    Joined:
    Dec 5, 2021
    Posts:
    5
    Hi all,

    I am trying to create a script for an explsoive grenade but I am having trouble with the hit detection.

    If I'm using only Physics.OverlapSphere the grenade will hit through walls.

    Then I added a raycast check for every object in the OverlapSphere but now it will hit just the first object and no other objects behind it.

    So I want to hit everything in the Physics.OverlapSphere except when objects are behind walls.

    My current code:
    Code (CSharp):
    1.    IEnumerator ThrowGrenade()
    2.     {
    3.         Transform playerOrientation = GetComponentInParent<PlayerController>().lookroot;
    4.        GameObject thrownGrenade = Instantiate(grenade, playerOrientation.position, playerOrientation.rotation);
    5.         Rigidbody rbThrownGrenade = thrownGrenade.GetComponent<Rigidbody>();
    6.         rbThrownGrenade.AddForce(playerOrientation.forward * throwForce, ForceMode.Impulse);
    7.  
    8.         yield return new WaitForSeconds(delay);
    9.  
    10.         Instantiate(explosionEffect, thrownGrenade.transform.position, thrownGrenade.transform.rotation);
    11.         Collider[] colliders = Physics.OverlapSphere(thrownGrenade.transform.position, radius);
    12.         foreach (Collider nearbyObject in colliders)
    13.         {
    14.             Ray ray = new Ray(thrownGrenade.transform.position, nearbyObject.transform.position - thrownGrenade.transform.position);
    15.            
    16.             RaycastHit hit;
    17.             if (Physics.Raycast(ray, out hit))
    18.             {
    19.                 print(hit.transform.gameObject.name);
    20.             }
    21.            
    22.            // if (nearbyObject.GetComponent<PlayerController>()) print("Player got hit");
    23.             //else print(nearbyObject.name + " got hit");
    24.         }
    25.  
    26.  
    27.  
    28.        
    29.     }
    I'm a beginner and I would really appreciate any help.


    Thanks :)
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Are you looking to simulate discrete pieces of grenade shrapnel? In that case choose how many pieces fly out, iterate them and pick random (or spherical) directions for them, and raycast the distance you want them to go. Based on that distance, they would penetrate / damage the struck colliders appropriately.

    If you're looking to simulate blast damage you probably need to start with shrapnel simulation on a regular grid, possibly even a much-higher-density grid and then apply damage according to estimated surface area facing the grenade.

    Alternately you could iterate all the overlap objects you collect and then re-raycast at them to see if they are indeed hittable at any given point from the blast point of the grenade.
     
  3. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,073
    Check RaycastAll. Don't forget the result is unordered.
     
  4. danielkutifa

    danielkutifa

    Joined:
    Dec 5, 2021
    Posts:
    5
    Thank you both for helping me!

    I have still 2 questions:
    1. Did I use the Array.Sort method right?
    2. Sometimes the player doenst get hit. If I'm behind an object with the layer 11(Walls etc) it always prints for the player:
    print($"Damage for {nearbyObject.gameObject.name} prevented by {hit.transform.gameObject.name}");
    but rarely prints:
    print($"No world geometry prevented damage for {nearbyObject.gameObject.name}");
    if I'm not behind a layer 11(e.g. Wall) object.

    Is it because I use an empty game object with a Character Controller attached and the hitbox is somehow buggy or what could be the reason?

    Code (CSharp):
    1. Collider[] colliders = Physics.OverlapSphere(thrownGrenade.transform.position, radius);
    2.         foreach (Collider nearbyObject in colliders)
    3.         {
    4.  
    5.             RaycastHit[] hits;
    6.             hits = Physics.RaycastAll(thrownGrenade.transform.position, nearbyObject.transform.position - thrownGrenade.transform.position, radius);
    7.  
    8.        
    9.             Array.Sort(hits, delegate (RaycastHit x, RaycastHit y) { return x.distance.CompareTo(y.distance); });
    10.  
    11.  
    12.             foreach (RaycastHit hit in hits)
    13.             {
    14.                 if (hit.transform.gameObject.layer == 11)
    15.                 {
    16.                     print($"Damage for {nearbyObject.gameObject.name} prevented by {hit.transform.gameObject.name}");
    17.                     break;
    18.                 }
    19.  
    20.                 if (nearbyObject.gameObject == hit.transform.gameObject)
    21.                 {
    22.                     print($"No world geometry prevented damage for {nearbyObject.gameObject.name}");
    23.                     break;
    24.                 }
    25.  
    26.             }
    Thanks in advance for helping me!
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Anytime you think a fundamental low-level element of Unity is "buggy," you want to stop and check yourself. Likely it is just fine and you are feeding it incorrect data.

    Why not use Debug.DrawRay() coupled with Debug.Break() to visualize where these rays are going? Or just spawn line renderers in the scene with their endpoints set to the rays... super-simple fast debugging tricks.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    I pulled some ray visualization code out of my games and cleaned it up...

    https://gist.github.com/kurtdekker/539c021eac02c5b1a164691c5bd99afc

    Sample use:

    Code (csharp):
    1.         Vector3 position = Vector3.zero;
    2.         for (int i = 0; i < 10; i++)
    3.         {
    4.             RayViz.Create(position, position + Random.onUnitSphere * Random.Range( 0.5f, 5.0f));
    5.         }
    6.         Debug.Break();        // pause the game
     
  7. danielkutifa

    danielkutifa

    Joined:
    Dec 5, 2021
    Posts:
    5
    Thanks for your tipps and time!

    Already did a test with a simple Debug.DrawRay.

    It somehow shoots the ray under the player(blue line) other objects (red line) gets targeted fine.

    Here is a picture: https://postimg.cc/1g97GTCN
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Is the ray being launched at y == 0 and hitting the ground?

    You might need to lift your grenade "blast point" up a little if it is right against the ground.
     
  9. danielkutifa

    danielkutifa

    Joined:
    Dec 5, 2021
    Posts:
    5
    This is the raycast code:
    Physics.RaycastAll(thrownGrenade.transform.position, nearbyObject.transform.position - thrownGrenade.transform.position, radius);


    I found the problem while writing!

    The empty gameobject that I use as a player is at y postion 0. But the collider from the Character Controller is above 0.

    I need to somehow change the position. Because if I lift up the empty game object the Character Controller moves also up.

    Or can edit somehow the raycast when I'm targeting a player?

    Code (CSharp):
    1. Collider[] colliders = Physics.OverlapSphere(thrownGrenade.transform.position, radius);
    2.         foreach (Collider nearbyObject in colliders)
    3.         {
    4.  
    5.             RaycastHit[] hits;
    6.             hits = Physics.RaycastAll(thrownGrenade.transform.position, nearbyObject.transform.position - thrownGrenade.transform.position, radius);
    7.  
    8.  
    9.             Array.Sort(hits, delegate (RaycastHit x, RaycastHit y) { return x.distance.CompareTo(y.distance); });
    10.  
    11.  
    12.             foreach (RaycastHit hit in hits)
    13.             {
    14.                 if (nearbyObject.name == "Player")
    15.                 {
    16.                     Debug.DrawRay(thrownGrenade.transform.position, nearbyObject.transform.position - thrownGrenade.transform.position, Color.blue, 50f);
    17.                     Debug.Break();
    18.                         }
    19.                 else Debug.DrawRay(thrownGrenade.transform.position, nearbyObject.transform.position - thrownGrenade.transform.position, Color.red, 50f);
    20.  
    21.                 if (hit.transform.gameObject.layer == 11)
    22.                 {
    23.  
    24.                     print($"Damage for {nearbyObject.gameObject.name} prevented by {hit.transform.gameObject.name}");
    25.                     break;
    26.                 }
    27.  
    28.                 if (nearbyObject.gameObject == hit.transform.gameObject)
    29.                 {
    30.                     //Debug.DrawRay(thrownGrenade.transform.position, nearbyObject.transform.position - thrownGrenade.transform.position, Color.red, 50f);
    31.                     print($"No world geometry prevented damage for {nearbyObject.gameObject.name}");
    32.                     break;
    33.                 }
    34.  
    35.             }
     
    Last edited: Jan 13, 2022
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    You might need a separate larger collider on the player that is ONLY used for raycasting grenades. This collider would be on a separate GameObject so you could mark its Layer to not interact with anything else in the game via the physics collision matrix. The Raycast could still find it.
     
  11. danielkutifa

    danielkutifa

    Joined:
    Dec 5, 2021
    Posts:
    5
    Yeah that seems to work. Thanks again for your time!