3
\$\begingroup\$

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):

Screenshot from my prototype of the extra reflection.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

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 {
                i = 9999; //not the best, see additional comments
            }
            //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.

\$\endgroup\$
5
  • \$\begingroup\$ 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 \$\endgroup\$
    – Fobok
    Commented Jun 14, 2017 at 18:21
  • \$\begingroup\$ Probably means you need to loop one more time, try... i = nReflections instead. \$\endgroup\$ Commented Jun 14, 2017 at 18:37
  • \$\begingroup\$ 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.) \$\endgroup\$
    – Fobok
    Commented Jun 14, 2017 at 19:27
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Jun 14, 2017 at 19:32
  • \$\begingroup\$ 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. \$\endgroup\$
    – Fobok
    Commented Jun 15, 2017 at 21:14
1
\$\begingroup\$

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);    
        //start with just the origin
        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;
            }
        }
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Jun 16, 2017 at 20:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .