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. Dismiss Notice

Question Raycast passes through collider.

Discussion in 'Physics' started by MachineHead, Sep 13, 2023.

  1. MachineHead

    MachineHead

    Joined:
    Jan 6, 2016
    Posts:
    30
    I'm making a laser beam in Unity using a raycast and a line renderer. The Raycast works great for the most part, it's endpoint will change based on where its colliding with the enemy. However, it will sometimes pass through, at least thats what it looks like, when either the enemy moves, or I move. In this gif, I am moving my character backwards, or away from the larger ship. The beam will pass through the collider, you'll see it flicker past the enemy ship. This also happens when the enemy ship moves.

    I would like to know why this is happening and how to fix it.

    https://pasteboard.co/fjOEDl7PuxEj.gif
     

    Attached Files:

  2. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,630
    Raycasts simply don't miss colliders.

    Unless you give some more details on what you're doing (eg. sharing the code you're using to perform the raycast) it's not possible for anyone to know what the cause of this might be.
     
  3. MachineHead

    MachineHead

    Joined:
    Jan 6, 2016
    Posts:
    30
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class LaserWithActivation : MonoBehaviour
    4. {
    5.     public Transform startPoint;
    6.     public float maxDistance = 20f;
    7.     public float growthDuration = 0.75f;
    8.     public LayerMask hitLayers;
    9.     public Vector3 targetDirection = Vector3.up;
    10.     public GameObject objectToActivate;
    11.  
    12.     private bool isLaserActive = false;
    13.     private LineRenderer lineRenderer;
    14.     private float laserLength;
    15.     private float growthTimer = 0f;
    16.  
    17.     private void Start()
    18.     {
    19.      
    20.         lineRenderer = GetComponent<LineRenderer>();
    21.         lineRenderer.positionCount = 2;
    22.         lineRenderer.SetPosition(0, startPoint.position);
    23.         lineRenderer.SetPosition(1, startPoint.position + startPoint.TransformDirection(targetDirection) * laserLength);
    24.         lineRenderer.enabled = false;
    25.     }
    26.  
    27.     private void FixedUpdate()
    28.     {
    29.      
    30.         if (Input.GetButton("Cross"))
    31.         {
    32.          
    33.             isLaserActive = true;
    34.  
    35.          
    36.             if (growthTimer < growthDuration)
    37.             {
    38.                 growthTimer += Time.deltaTime;
    39.             }
    40.  
    41.          
    42.             float progress = Mathf.Clamp01(growthTimer / growthDuration);
    43.  
    44.          
    45.             laserLength = Mathf.Lerp(0f, maxDistance, progress);
    46.         }
    47.         else
    48.         {
    49.          
    50.             isLaserActive = false;
    51.  
    52.          
    53.             growthTimer = 0f;
    54.         }
    55.  
    56.      
    57.         Vector3 endPoint = startPoint.position + startPoint.TransformDirection(targetDirection) * laserLength;
    58.  
    59.      
    60.         lineRenderer.SetPosition(0, startPoint.position);
    61.         lineRenderer.SetPosition(1, endPoint);
    62.  
    63.      
    64.         lineRenderer.enabled = isLaserActive;
    65.  
    66.      
    67.         if (isLaserActive)
    68.         {
    69.             RaycastHit hit;
    70.             if (Physics.Raycast(startPoint.position, endPoint - startPoint.position, out hit, laserLength, hitLayers))
    71.             {
    72.                 Debug.Log("Laser hit an object with tag: " + hit.collider.gameObject.tag);
    73.                 maxDistance = hit.distance;
    74.  
    75.              
    76.                 objectToActivate.SetActive(true);
    77.                 objectToActivate.transform.position = hit.point;
    78.             }
    79.             else
    80.             {
    81.                 maxDistance = 20f;
    82.             }
    83.         }
    84.         else
    85.         {
    86.          
    87.             objectToActivate.SetActive(false);
    88.  
    89.          
    90.             isLaserActive = false;
    91.  
    92.          
    93.             maxDistance = 20f;
    94.  
    95.          
    96.             lineRenderer.enabled = false;
    97.         }
    98.     }
    99. }
    100.  
     
    Last edited: Sep 13, 2023
  4. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    1,630
    There's quite a few problems with this script:

    - FixedUpdate() can be called zero, one, or multiple times per frame, depending on the time passed since the last frame. You're reading input in FixedUpdate(), which will lead to missing input (in cases where the user presses a button during a frame that has no FixedUpdate() calls) and/or duplicate input (in cases where the user presses a button during a frame that has multiple FixedUpdate() calls). This is a big no-no.

    - maxDistance is set to the raycast's hit distance, but is also used to determine the maximum raycast distance (since laserLength lerps from 0 to maxDistance). As a result, the raycast will flicker as its maximum length is decreased whenever it hits something, and then has to slowly grow back up until it hits the object again, only to have its max distance decreased, etc. This is whats causing the "missed" collisions.

    - You have a 1-frame delay between the line renderer and the raycast, because you're updating the line renderer before the raycast has had a chance to hit anything. Ideal update order would be: input, raycast, then line renderer.

    A very rough fixed version (addresses problems 2 and 3, not 1):
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Laser : MonoBehaviour
    4. {
    5.     public Transform startPoint;
    6.     public float maxDistance = 20f;
    7.     public float growthDuration = 0.75f;
    8.     public LayerMask hitLayers;
    9.     public Vector3 targetDirection = Vector3.up;
    10.     public GameObject objectToActivate;
    11.  
    12.     private bool isLaserActive = false;
    13.     private LineRenderer lineRenderer;
    14.     private float laserLength;
    15.     private float growthTimer = 0f;
    16.  
    17.     private void Start()
    18.     {
    19.  
    20.         lineRenderer = GetComponent<LineRenderer>();
    21.         lineRenderer.positionCount = 2;
    22.         lineRenderer.SetPosition(0, startPoint.position);
    23.         lineRenderer.SetPosition(1, startPoint.position + startPoint.TransformDirection(targetDirection) * laserLength);
    24.         lineRenderer.enabled = false;
    25.     }
    26.  
    27.     private void FixedUpdate()
    28.     {
    29.  
    30.         if (Input.GetKey(KeyCode.F))
    31.         {
    32.  
    33.             isLaserActive = true;
    34.  
    35.  
    36.             if (growthTimer < growthDuration)
    37.             {
    38.                 growthTimer += Time.deltaTime;
    39.             }
    40.  
    41.  
    42.             float progress = Mathf.Clamp01(growthTimer / growthDuration);
    43.  
    44.  
    45.             laserLength = Mathf.Lerp(0f, maxDistance, progress);
    46.         }
    47.         else
    48.         {
    49.  
    50.             isLaserActive = false;
    51.  
    52.  
    53.             growthTimer = 0f;
    54.         }
    55.  
    56.         Vector3 endPoint = startPoint.position + startPoint.TransformDirection(targetDirection) * laserLength;
    57.         lineRenderer.SetPosition(0, startPoint.position);
    58.  
    59.         if (isLaserActive)
    60.         {
    61.             lineRenderer.enabled = isLaserActive;
    62.  
    63.             RaycastHit hit;
    64.             if (Physics.Raycast(startPoint.position, endPoint - startPoint.position, out hit, laserLength, hitLayers))
    65.             {
    66.                 Debug.Log("Laser hit an object with tag: " + hit.collider.gameObject.tag);
    67.  
    68.                 lineRenderer.SetPosition(1, startPoint.position + startPoint.TransformDirection(targetDirection) * hit.distance);
    69.  
    70.                 objectToActivate.SetActive(true);
    71.                 objectToActivate.transform.position = hit.point;
    72.             }
    73.             else
    74.             {
    75.                 lineRenderer.SetPosition(1, startPoint.position + startPoint.TransformDirection(targetDirection) * laserLength);
    76.             }
    77.  
    78.         }
    79.         else
    80.         {
    81.  
    82.             objectToActivate.SetActive(false);
    83.  
    84.             isLaserActive = false;
    85.  
    86.             lineRenderer.enabled = false;
    87.         }
    88.     }
    89. }
     
    Last edited: Sep 13, 2023
    MachineHead likes this.
  5. MachineHead

    MachineHead

    Joined:
    Jan 6, 2016
    Posts:
    30
    That did the trick.
    Only issue is the object activated at the end of the raycast, the hit point, the "splash" damage at the point of impact.

    When i slide the laser off the edge of the enemy, the laser goes back to its max distance, but the splash damage sprite stays put and doesnt go with it.

    Other than that, it all works great. I was thinking the order in which i had the code made was off, but i didnt think of the max distance issue. Cant thank you enough.