Search code examples
c#unity-game-enginevectorstack-overflow

StackOverflowException when reflecting a Raycast2D


I'm trying to make a simple puzzle system for a game involving beams of light and mirrors in Unity. The light beams are created using an empty GameObject that casts a Raycast2D and uses a LineRenderer to display the beam. When a beam collides with a mirror object I simply use Vector2.Reflect to calculate the new direction.

The implementation works fine when the mirrors are static. When I try to move them around in-game, it causes random stack overflow errors, and there doesn't seem to be a pattern. Here's an example of a working mirror setup:

Here's what happens when I try to move a mirror: enter image description here

I'm guessing it's due to the mirror somehow reflecting the beam back and causing an infinite reflection loop, but I'm not sure why that would happen.

Relevant code:

public class LightBeam
{
    Vector2 position;
    Vector2 direction;

    GameObject lightBeam;
    LineRenderer lightRender;

    List<Vector2> lightPoints = new List<Vector2>();

    // A new LightBeam object is created every update
    public LightBeam(Vector2 startPos, Vector2 dir, Material material)
    {
        this.lightRender = new LineRenderer();
        this.lightBeam = new GameObject();
        this.lightBeam.name = "LightBeam";

        this.position = startPos;
        this.direction = dir;

        this.lightRender = this.lightBeam.AddComponent(typeof(LineRenderer)) as LineRenderer;
        this.lightRender.startWidth = 0.1f;
        this.lightRender.endWidth = 0.1f;
        this.lightRender.material = material;
        this.lightRender.startColor = Color.white;
        this.lightRender.endColor = Color.white;

        this.lightRender.sortingLayerName = "Foreground";

        CastRay(startPos, dir, lightRender);
    }

    void CastRay(Vector2 pos, Vector2 dir, LineRenderer beam)
    {

        lightPoints.Add(pos);

        // Source of the error
        RaycastHit2D hit = Physics2D.Raycast(pos, dir, 10, LayerMask.GetMask("Solid"));

        if(hit)
        {
            CheckHit(hit, dir, lightRender);
        }
        else
        {
            lightPoints.Add(lightPoints[lightPoints.Count-1] + dir * 10);
        }

        UpdateBeam();
    }

    void UpdateBeam()
    {
        lightRender.positionCount = lightPoints.Count;

        for(int i = 0; i < lightPoints.Count; i++)
        {
            lightRender.SetPosition(i, lightPoints[i]);
        }
    }

    void CheckHit(RaycastHit2D hit, Vector2 dir, LineRenderer beam)
    {
        if(hit.collider.gameObject.tag == "Mirror")
        {
            Vector2 pos = hit.point;
            Vector2 newDir = Vector2.Reflect(dir, hit.normal);

            CastRay(pos, newDir, beam);
        }
        else
        {
            lightPoints.Add(hit.point);
            UpdateBeam();
        }
    }
}

Error message:

StackOverflowException: The requested operation caused a stack overflow.
UnityEngine.ContactFilter2D.CreateLegacyFilter (System.Int32 layerMask, System.Single minDepth, System.Single maxDepth) (at C:/buildslave/unity/build/Modules/Physics2D/ScriptBindings/Physics2D.bindings.cs:2297)
UnityEngine.Physics2D.Raycast (UnityEngine.Vector2 origin, UnityEngine.Vector2 direction, System.Single distance, System.Int32 layerMask) (at C:/buildslave/unity/build/Modules/Physics2D/ScriptBindings/Physics2D.bindings.cs:843)
LightBeam.CastRay (UnityEngine.Vector2 pos, UnityEngine.Vector2 dir, UnityEngine.LineRenderer beam) (at Assets/Scripts/LightBeam.cs:50)
LightBeam.CheckHit (UnityEngine.RaycastHit2D hit, UnityEngine.Vector2 dir, UnityEngine.LineRenderer beam) (at Assets/Scripts/LightBeam.cs:82)
LightBeam.CastRay (UnityEngine.Vector2 pos, UnityEngine.Vector2 dir, UnityEngine.LineRenderer beam) (at Assets/Scripts/LightBeam.cs:55)
LightBeam.CheckHit (UnityEngine.RaycastHit2D hit, UnityEngine.Vector2 dir, UnityEngine.LineRenderer beam) (at Assets/Scripts/LightBeam.cs:82)
LightBeam.CastRay (UnityEngine.Vector2 pos, UnityEngine.Vector2 dir, UnityEngine.LineRenderer beam) (at Assets/Scripts/LightBeam.cs:55)
LightBeam.CheckHit (UnityEngine.RaycastHit2D hit, UnityEngine.Vector2 dir, UnityEngine.LineRenderer beam) (at Assets/Scripts/LightBeam.cs:82)

UPDATE: It turns out the beam is reflecting infinitely. I added a maxReflections value to limit the number of loops, but it doesn't do much except suppress the StackOverflow error, and the beam still flickers when the mirrors are moved.

Also, trying to Debug.Log(pos) in the CastRay method crashes Unity entirely.

void CastRay(Vector2 pos, Vector2 dir, LineRenderer beam, int maxReflections)
    {
        
        if(maxReflections <= 0)
        {
            Debug.Log("Too many reflections");
            return;
        }

Solution

  • As it turns out, the beam disappears when its origin is inside one of the mirrors. I solved it by moving the new Raycast's origin slightly when colliding with a mirror.

    I found the solution here: https://answers.unity.com/questions/1176130/need-help-ironing-out-this-weird-error-in-raycast2.html