Raycast ricochet only works for a couple of Rays

I’m working on an AI script that can sense the player and shoot them using ricocheting bullets. To achieve this, I use a script that casts rays in all directions that collide with walls. Raycasting to the walls works great and I get some nice ricocheted rays, but most of them don’t ricochet at all and I can’t figure out why.

using UnityEngine;

public class TargetFollowing : MonoBehaviour
{
  [SerializeField] private LayerMask _layerMask;
  [SerializeField] private int _numberOfRays;
  [SerializeField] private float _raycastDistance;
  private Transform _transform;

  private void Awake() {
    _transform = GetComponent<Transform>();
  }

  private void Update() {
    for (var i = -_numberOfRays / 2; i < _numberOfRays / 2; i++) {
      var direction = GetRelativeDirection(new Vector3(Mathf.Sin(i), Mathf.Cos(i), 0f));
      CastRay(_transform.position, direction, 3);
    }
  }

  private Vector3 GetRelativeDirection(Vector3 originalDirection) {
    return _transform.TransformDirection(originalDirection);
  }

  private void CastRay(Vector3 currentPosition, Vector3 rayDirection, int numberOfRicochets) {
    var raycastHit = Physics2D.Raycast(currentPosition, rayDirection, _raycastDistance, _layerMask.value);
    if (raycastHit.collider == null) {
      Debug.DrawRay(currentPosition, rayDirection * _raycastDistance, Color.red);
      return;
    }
    var targetPosition = rayDirection * raycastHit.distance;
    Debug.DrawRay(currentPosition, targetPosition, GetRaycastColor(numberOfRicochets));
    if (numberOfRicochets <= 0) {
      return;
    }
    var ricochetDirection = Vector2.Reflect(rayDirection, raycastHit.normal);
    CastRay(raycastHit.point, ricochetDirection, numberOfRicochets - 1);
  }

  private static Color GetRaycastColor(int ricochetNumber) {
    switch (ricochetNumber) {
      case 0: return Color.cyan;
      case 1: return Color.green;
      case 2: return Color.yellow;
      case 3: return Color.magenta;
      default: return Color.black;
    }
  }

}

The values of the serialized properties I used in the screenshots at the bottom are:

  • Layer Mask: only Walls
  • Number Of Rays: 45
  • Raycast Distance: 100

Playing around with the raycast distance didn’t really help either.


I figured out the problem in my script by displaying the normals of each raycast collision. First, every collision that didn’t ricochet had two normal directions. This indicated to me, that there was a raycast from and to that exact point. Then I used ~Physics2D.RaycastAllto find all collisions and proceeded to print out the distance of each hit for every raycast with multiple collisions. The Console was filled with exponentially more "0" than other distances. I updated my code with this at the top of myCastRay` method:

var allRaycastHits = Physics2D.RaycastAll(currentPosition, rayDirection, Mathf.Infinity, _layerMask.value);
var raycastHit = allRaycastHits.FirstOrDefault(h => h.distance > float.Epsilon);
if (raycastHit == default) {
  Debug.DrawRay(currentPosition, rayDirection * Mathf.Infinity, Color.red);
  return;
}

So basically, give me the first collision that isn’t the point of origin and use that to display the ricochet.