# Stopping a reflecting raycast/linerender laser

Update: Doing some more experimenting... it's not the early break that's causing the problem in the slightest. It does the same thing if I set the reflections to more than 2 and have no if or break in there.

So, I need to rethink this whole thing from scratch and try to find a better tutorial, I guess.

I'm working on a game for a game jam (I asked, asking for help is permitted :-)) involving lasers that reflect off of mirrors that are adjusted by the player to make the laser hit a target. Thanks to some tutorials, I managed to get the lasers to reflect, but they reflect off of everything and won't stop unless I manually set a number of reflections to stop at.

I've tried adding an if statement to break the reflection loop if it doesn't hit a mirror, as you'll see in the code, but it just results in a laser going off on a weird angle after hitting the non-mirror target.

I should add that I'm very new at this. I'm basically figuring things out as I go for this game jam. So, I know it's probably a simple mistake, or maybe I followed the completely wrong tutorial, but I just can't see it.

Can anybody please point me to the mistake?

void Update()
{
//clamp the number of reflections between 1 and int capacity
nReflections = Mathf.Clamp(nReflections, 1, nReflections);
//cast a new ray forward, from the current attached game object position
ray = new Ray(goTransform.position, goTransform.forward);

//represent the ray using a line that can only be viewed at the scene tab
Debug.DrawRay(goTransform.position, goTransform.forward * 100, Color.magenta);

//set the number of points to be the same as the number of reflections
numPoints = nReflections;

//make the lineRenderer have that number of points
lineRenderer.positionCount = numPoints;
//Set the first point of the line at the current attached game object position
lineRenderer.SetPosition(0, goTransform.position);

for (int i = 0; i <= nReflections; i++)
{
//If the ray hasn't reflected yet
if (i == 0)
{
//Check if the ray has hit something
if (Physics.Raycast(ray.origin, ray.direction, out hit, 100))//cast the ray 100 units at the specified direction
{
//the reflection direction is the reflection of the current ray direction flipped at the hit normal
direction = Vector3.Reflect(ray.direction, hit.normal);
//cast the reflected ray, using the hit point as the origin and the reflected direction as the direction
ray = new Ray(hit.point, direction);

//Draw the normal - can only be seen at the Scene tab, for debugging purposes
Debug.DrawRay(hit.point, hit.normal * 3, Color.blue);
//represent the ray using a line that can only be viewed at the scene tab
Debug.DrawRay(hit.point, direction * 100, Color.magenta);

//Print the name of the object the cast ray has hit, at the console
//Debug.Log("Object name: " + hit.transform.name);

//if the number of reflections is set to 1
if (nReflections == 1)
{
//add a new vertex to the line renderer
lineRenderer.positionCount = ++numPoints;
}

//set the position of the next vertex at the line renderer to be the same as the hit point
lineRenderer.SetPosition(i + 1, hit.point);

}
}
else // the ray has reflected at least once
{
//Check if the ray has hit something
if (Physics.Raycast(ray.origin, ray.direction, out hit, 100))//cast the ray 100 units at the specified direction
{
//the refletion direction is the reflection of the ray's direction at the hit normal
direction = Vector3.Reflect(direction, hit.normal);
//cast the reflected ray, using the hit point as the origin and the reflected direction as the direction
ray = new Ray(hit.point, direction);

//Draw the normal - can only be seen at the Scene tab, for debugging purposes
Debug.DrawRay(hit.point, hit.normal * 3, Color.blue);
//represent the ray using a line that can only be viewed at the scene tab
Debug.DrawRay(hit.point, direction * 100, Color.magenta);

//Print the name of the object the cast ray has hit, at the console
// Debug.Log("Object name: " + hit.transform.name);

//add a new vertex to the line renderer

lineRenderer.positionCount = ++numPoints;

//set the position of the next vertex at the line renderer to be the same as the hit point
lineRenderer.SetPosition(i + 1, hit.point);
}
}

if (hit.collider.tag != "Mirror")
{
break;
}
}
}


Screenshot from my prototype of the extra reflection. It should not reflect off of that cube, and especially not at that weird angle (the foreground object is my primitive laser model, shooting off to the right):

Right now your code is "raycast hit something: reflect unconditionally." If you want it to only reflect off mirrors you need to add some code:

//...
//Check if the ray has hit something
if (Physics.Raycast(ray.origin, ray.direction, out hit, 100))//cast the ray 100 units at the specified direction
{
//the refletion direction is the reflection of the ray's direction at the hit normal
direction = Vector3.Reflect(direction, hit.normal);
//cast the reflected ray, using the hit point as the origin and the reflected direction as the direction

//NEW!
//if the ray hit a mirror, reflect! Otherwise do nothing.
if(hit.transform.name == "Mirror") {
ray = new Ray(hit.point, direction);
}
else {
}
//Draw the normal - can only be seen at the Scene tab, for debugging purposes
Debug.DrawRay(hit.point, hit.normal * 3, Color.blue);
//represent the ray using a line that can only be viewed at the scene tab
Debug.DrawRay(hit.point, direction * 100, Color.magenta);

//Print the name of the object the cast ray has hit, at the console
// Debug.Log("Object name: " + hit.transform.name);
//add a new vertex to the line renderer
lineRenderer.positionCount = ++numPoints;
//set the position of the next vertex at the line renderer to be the same as the hit point
lineRenderer.SetPosition(i + 1, hit.point);
}
//...


By setting i to 9999, it means that the code will treat this loop as the last one, it will finish drawing this portion of the line, then terminate. This isn't the best way to do this, but it leaves your code laid out the same way it already was. If the if(hit == "mirror") section is moved to the end of its parent section, then we can do else { break; } instead, which will break out of the for-loop early.

You'll have to do this in both halves of your update loop, as you have a "if hasn't yet bounced" versus "has previously bounced" sections which are largely identical.

Code is also assuming your mirrors are named "Mirror" which they may not be, and using tags would be better. Limits of my being able to write functional code that explains is intent without knowing what your scene looks like.

• Thanks for helping! Unfortunately, this isn't quite a working fix. The linerenderer will no longer go to the final object, stopping at the mirror before the non-mirror object, and shoots off in the direction towards my laser emitter model again. (I also just added a line to the code to make sure that it was not set to loop, and it still happens.) 1drv.ms/i/s!Aj0TjF5ApmITj-IFExlVzqaD7YydAg Jun 14 '17 at 18:21
• Probably means you need to loop one more time, try... i = nReflections instead. Jun 14 '17 at 18:37
• No luck. That just adds an extra bounce off of the laser model, again at a seemingly random angle. (That's assuming I re-add the ray line to the 'else', otherwise. it does nothing different at all.) I've tried multiple things to get it to do just one more round or to calculate just one last position, but it doesn't seem to work, (I even tried, admittedly by mistake, taking out the ray from the else again and setting it to nReflections - 1, but that just crashed Unity every time I hit the play button.) Jun 14 '17 at 19:27
• Hmm. I'll fiddle with this for a bit, but you definitely need some way of recognizing that you've collided with a non-mirror and to terminate the loop. Jun 14 '17 at 19:32
• Doing some more experimenting... it's not the early break that's causing the problem in the slightest. It does the same thing if I set the reflections to more than 2 and have no if or break in there. So, I need to rethink this whole thing from scratch and try to find a better tutorial, I guess. Jun 15 '17 at 21:14

A user on itch.io (adroitconceptions) helped me figure out the problem: The logic and code was completely messed up. The code he helped me update now works perfectly.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent (typeof(LineRenderer))]
public class RayCastReflection : MonoBehaviour
{
//this game object's Transform
private Transform goTransform;
//the attached line renderer
private LineRenderer lineRenderer;
//a ray
private Ray ray;
//a RaycastHit variable, to gather informartion about the ray's collision
private RaycastHit hit;
//reflection direction
private Vector3 direction;
//the number of reflections
public int nReflections = 2;
//max length
public float maxLength = 100f;
//the number of points at the line renderer
private int numPoints;
//private int pointCount;
void Awake ()
{
//get the attached Transform component
goTransform = this.GetComponent<Transform> ();
//get the attached LineRenderer component
lineRenderer = this.GetComponent<LineRenderer> ();
}
void Update ()
{
//clamp the number of reflections between 1 and int capacity
nReflections = Mathf.Clamp (nReflections, 1, nReflections);
ray = new Ray (goTransform.position, goTransform.forward);
lineRenderer.positionCount = 1;
lineRenderer.SetPosition (0, goTransform.position);
float remainingLength = maxLength;
//bounce up to n times
for (int i = 0; i < nReflections; i++) {
// ray cast
if (Physics.Raycast (ray.origin, ray.direction, out hit, remainingLength)) {
//we hit, update line renderer
lineRenderer.positionCount += 1;
lineRenderer.SetPosition (lineRenderer.positionCount - 1, hit.point);
// update remaining length and set up ray for next loop
remainingLength -= Vector3.Distance (ray.origin, hit.point);
ray = new Ray (hit.point, Vector3.Reflect(ray.direction, hit.normal));
// break loop if we don't hit a Mirror
if (hit.collider.tag != "Mirror")
break;
} else {
// We didn't hit anything, draw line to end of ramainingLength
lineRenderer.positionCount += 1;
lineRenderer.SetPosition (lineRenderer.positionCount - 1, ray.origin + ray.direction * remainingLength);
break;
}
}
}
}

• Glad you got it worked out. :) I see a if (hit.collider.tag != "Mirror") break; in there, which definitely supports my conclusion, even if there was something else going on as well. I didn't have the availability to whip up a project to debug it myself. Jun 16 '17 at 20:30