[FREE]bouncing laser script!

Hello Community,
Today I want to share with you my script for bouncing raycast lasers.WEBDEMO I am doing this for two main reasons:

  • I want feedback to make this evolve and become better.
  • I love free stuff but I feel I have taken too much with out contributing some back

Enjoy and please help this improve through comments.
JS:

#pragma strict
@script RequireComponent(LineRenderer)
var dist : int; //max distance for beam to travel.
var lr : LineRenderer;
var winTag : String; // i was using for minigame, if laser touches this tag , win
var reftag :String; //tag it can reflect off.
var limit : int = 100; // max reflections
private var verti  :int = 1; //segment handler don't touch.
private var iactive :boolean;
private var currot : Vector3;
private var curpos : Vector3;
function Start () {


}

function Update () {

    lr.enabled = Input.GetKey(KeyCode.Space);
    if (Input.GetKey(KeyCode.Space)||Input.GetKeyUp(KeyCode.Space)){
        DrawLaser();
    }
}
function DrawLaser()
{
    verti = 1;
    iactive = true;
    currot = transform.forward;
    curpos = transform.position;
    lr.SetVertexCount(1);
    lr.SetPosition(0,transform.position);

    while(iactive)
    {
        verti ++;
        var hit : RaycastHit;
        lr.SetVertexCount(verti);
        if (Physics.Raycast(curpos,currot,hit,dist))
        {
            //verti++;
            curpos=hit.point;
            currot = Vector3.Reflect(currot,hit.normal);
            lr.SetPosition(verti-1,hit.point);
            if (hit.transform.gameObject.tag != reftag){
                iactive = false;
            }
        }
        else
        {
            //verti++;
            iactive = false;
            lr.SetPosition(verti-1,curpos+100*currot);
        
        }
        if (verti >limit)
        {
        iactive = false;
        }
    
    
    }



}

Good script, but I can’t figure out how to get it working with multiplayer. If anyone has any idea why, please let me know!

How do you mean.

Your web player is broken it seem, doesn’t load. Make sure both files are in the folder.

It shows up for the client, but not for anyone else connected

Thanks for the script, after some digging I could fix the line renderer problem, it works great now. If anyone is wondering, here’s the script: (thanks to you and Patrick234)

void DrawLaser () {
        /*
        public int laserDistance = 100; //max raycasting distance
        public int laserLimit = 10; //the laser can be reflected this many times
        public LineRenderer laserRenderer; //the line renderer
        */

        int laserReflected = 1; //How many times it got reflected
        int vertexCounter = 1; //How many line segments are there
        bool loopActive = true; //Is the reflecting loop active?
        Vector2 laserDirection = transform.up; //direction of the next laser
        Vector2 lastLaserPosition = transform.position; //origin of the next laser

        laserRenderer.SetVertexCount(1);
        laserRenderer.SetPosition(0, transform.position);

        while (loopActive) {
            RaycastHit2D hit = Physics2D.Raycast(lastLaserPosition, laserDirection, laserDistance);

            if (hit) {
                laserReflected++;
                vertexCounter += 3;
                laserRenderer.SetVertexCount (vertexCounter);
                laserRenderer.SetPosition (vertexCounter-3, Vector3.MoveTowards(hit.point, lastLaserPosition, 0.01f));
                laserRenderer.SetPosition(vertexCounter-2, hit.point);
                laserRenderer.SetPosition(vertexCounter-1, hit.point);
                lastLaserPosition = hit.point;
                laserDirection = Vector3.Reflect(laserDirection, hit.normal);
            } else {
                laserReflected++;
                vertexCounter++;
                laserRenderer.SetVertexCount (vertexCounter);
                laserRenderer.SetPosition (vertexCounter - 1, lastLaserPosition + (laserDirection.normalized * laserDistance));

                loopActive = false;
            }
            if (laserReflected > laserLimit)
                loopActive = false;
        }
    }

Im glad you like it!

Little bit too late but i took your .js and transform it into c# and it had a little problem with raycasthit = physics.raycast because it was missing the out hit parameter.

[RequireComponent(typeof(LineRenderer))]
public class BouncingLaser : MonoBehaviour
{

    public int laserDistance;
    public LineRenderer mLineRenderer;
    public string bounceTag;
    public int maxBounce;
    private float timer = 0;
   
    // Use this for initialization
    void Start ()
    {
       
    }
   
    // Update is called once per frame
    void Update ()
    {
        if (Input.GetKeyDown ("space") && !mLineRenderer.enabled) {
            timer = 0;
            StartCoroutine ("FireMahLazer");   
        }
    }
   
    IEnumerator FireMahLazer ()
    {
        //Debug.Log("Running");
        mLineRenderer.enabled = true;
        int laserReflected = 1; //How many times it got reflected
        int vertexCounter = 1; //How many line segments are there
        bool loopActive = true; //Is the reflecting loop active?
       
        Vector3 laserDirection = transform.forward; //direction of the next laser
        Vector3 lastLaserPosition = transform.localPosition; //origin of the next laser
   
        mLineRenderer.SetVertexCount (1);
        mLineRenderer.SetPosition (0, transform.position);
        RaycastHit hit;

        while (loopActive) {

            if (Physics.Raycast (lastLaserPosition, laserDirection, out hit, laserDistance) && hit.transform.gameObject.tag == bounceTag) {
               
                    Debug.Log ("Bounce");
                    laserReflected++;
                    vertexCounter += 3;
                    mLineRenderer.SetVertexCount (vertexCounter);
                    mLineRenderer.SetPosition (vertexCounter - 3, Vector3.MoveTowards (hit.point, lastLaserPosition, 0.01f));
                    mLineRenderer.SetPosition (vertexCounter - 2, hit.point);
                    mLineRenderer.SetPosition (vertexCounter - 1, hit.point);
                    mLineRenderer.SetWidth (.1f, .1f);
                    lastLaserPosition = hit.point;
                    laserDirection = Vector3.Reflect (laserDirection, hit.normal);
                } else {
           
                    Debug.Log ("No Bounce");
                    laserReflected++;
                    vertexCounter++;
                    mLineRenderer.SetVertexCount (vertexCounter);
                    Vector3 lastPos = lastLaserPosition + (laserDirection.normalized * laserDistance);
                    Debug.Log ("InitialPos " + lastLaserPosition + " Last Pos" + lastPos);
                    mLineRenderer.SetPosition (vertexCounter - 1, lastLaserPosition + (laserDirection.normalized * laserDistance));

                    loopActive = false;
                }
            if (laserReflected > maxBounce)
                loopActive = false;
        }
       
        if(Input.GetKey("space") && timer < 2)
        {
            yield return new WaitForEndOfFrame();
            timer+=Time.deltaTime;
            StartCoroutine("FireMahLazer");
        }
        else{
            yield return null;
            mLineRenderer.enabled = false;
        }
    }
}

Anyway your code was pretty good and it works fine.
Thanks for sharing the knowledge!

EDIT: The script now works on a 3D space, and it can keep detecting collision for 2 seconds then it shuts itself off.

Can I be a bother and ask for an example scene to see the script in action (& how objects might be set up)? I can’t get the original poster’s web demo to work.

1 Like

hey, im trying to implement this in a 2d environment, but i cant seem to get the laser to move in the right direction, any tips? :confused:

Thanks everyone for the great script. In the spirit of returning the favor for the next one to come along and find this, here is my contribution:

I’ve added beam splitters. They bounce as you’d expect but also pass through acting just like a real beam splitter. The way it works is at the hit point we reflect then instantiate another copy of the laser with the attached script. This new laser is aligned with the inbound path. That new beam continues to raycast and bounce or split since it’s just running another copy of the same script.

I also wanted to be able to move objects in realtime and have the laser react accordingly, so I changed the looping mechanism and added an update frequency so you can tune it based on the number of bounces/splits expected (if it’s bogging down).



using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[RequireComponent(typeof(LineRenderer))]
public class BouncingLaser : MonoBehaviour
{
    public float updateFrequency = 0.1f;
    public int laserDistance;
    public string bounceTag;
    public string splitTag;
    public string spawnedBeamTag;
    public int maxBounce;
    public int maxSplit;
    private float timer = 0;
    private LineRenderer mLineRenderer;

    // Use this for initialization
    void Start()
    {
        timer = 0;
        mLineRenderer = gameObject.GetComponent<LineRenderer>();
        StartCoroutine(RedrawLaser());
    }

    // Update is called once per frame
    void Update()
    {
        if (gameObject.tag != spawnedBeamTag)
        {
            if (timer >= updateFrequency)
            {
                timer = 0;
                //Debug.Log("Redrawing laser");
                foreach (GameObject laserSplit in GameObject.FindGameObjectsWithTag(spawnedBeamTag))
                    Destroy(laserSplit);

                StartCoroutine(RedrawLaser());
            }
            timer += Time.deltaTime;
        }
    }

    IEnumerator RedrawLaser()
    {
        //Debug.Log("Running");
        int laserSplit = 1; //How many times it got split
        int laserReflected = 1; //How many times it got reflected
        int vertexCounter = 1; //How many line segments are there
        bool loopActive = true; //Is the reflecting loop active?

        Vector3 laserDirection = transform.forward; //direction of the next laser
        Vector3 lastLaserPosition = transform.localPosition; //origin of the next laser

        mLineRenderer.SetVertexCount(1);
        mLineRenderer.SetPosition(0, transform.position);
        RaycastHit hit;

        while (loopActive)
        {
            //Debug.Log("Physics.Raycast(" + lastLaserPosition + ", " + laserDirection + ", out hit , " + laserDistance + ")");
            if (Physics.Raycast(lastLaserPosition, laserDirection, out hit, laserDistance) && ((hit.transform.gameObject.tag == bounceTag) || (hit.transform.gameObject.tag == splitTag)))
            {
                //Debug.Log("Bounce");
                laserReflected++;
                vertexCounter += 3;
                mLineRenderer.SetVertexCount(vertexCounter);
                mLineRenderer.SetPosition(vertexCounter - 3, Vector3.MoveTowards(hit.point, lastLaserPosition, 0.01f));
                mLineRenderer.SetPosition(vertexCounter - 2, hit.point);
                mLineRenderer.SetPosition(vertexCounter - 1, hit.point);
                mLineRenderer.SetWidth(.01f, .01f);
                lastLaserPosition = hit.point;
                Vector3 prevDirection = laserDirection;
                laserDirection = Vector3.Reflect(laserDirection, hit.normal);
               
                if (hit.transform.gameObject.tag == splitTag)
                {
                    //Debug.Log("Split");
                    if (laserSplit >= maxSplit)
                    {
                        Debug.Log("Max split reached.");
                    }
                    else
                    {
                        //Debug.Log("Splitting...");
                        laserSplit++;
                        Object go = Instantiate(gameObject, hit.point, Quaternion.LookRotation(prevDirection));
                        go.name = spawnedBeamTag;
                        ((GameObject)go).tag = spawnedBeamTag;
                    }
                }
            }
            else
            {
                //Debug.Log("No Bounce");
                laserReflected++;
                vertexCounter++;
                mLineRenderer.SetVertexCount(vertexCounter);
                Vector3 lastPos = lastLaserPosition + (laserDirection.normalized * laserDistance);
                //Debug.Log("InitialPos " + lastLaserPosition + " Last Pos" + lastPos);
                mLineRenderer.SetPosition(vertexCounter - 1, lastLaserPosition + (laserDirection.normalized * laserDistance));

                loopActive = false;
            }
            if (laserReflected > maxBounce)
                loopActive = false;
        }
       
        yield return new WaitForEndOfFrame();
    }
}

Thanks alot for the post guys, It was really helpful. Just to add @RobNewton , If you wanna update player dynamically with gameObject.

// Use this for initialization
    void Start()
    {
        timer = 0;
    }

    // Update is called once per frame
    void Update()
    {
        if (gameObject.tag != spawnedBeamTag) {
            if (timer >= updateFrequency) {
                timer = 0;
                //Debug.Log("Redrawing laser");
                foreach (GameObject laserSplit in GameObject.FindGameObjectsWithTag(spawnedBeamTag))
                    Destroy (laserSplit);

                StartCoroutine (RedrawLaser ());
            }
            timer += Time.deltaTime;
        } else {
            mLineRenderer = gameObject.GetComponent<LineRenderer>();
            StartCoroutine(RedrawLaser());
        }
    }

Sorry to bump a old thread, but using Mdsraayan’s version:

Script works perfectly! I wanted it for a 2d game so I replaced the raycast with a 2draycast and all the vector3s to vector2s. Now, when I rotate the mirror as the game is played, the bouncing laser (not the part from the projector) kind of flickers in and out of existence. I am happy to post my code, but I was wondering if anyone already converted this to 2d?

Would post a screenshot, but screenshot wouldn’t show the flickering. I’d need a video and I don’t think I have any screen capture software at the moment.

Hey Vasir_,

Well, I fixed the problem of the ray’s flickering.
The problem actually is that the laser collide with the mirror that reflected it, it get stuck inside the mirror basically.
So just add some points in the last laser position like this:

lastLaserPosition = hit.point + (laserDirection*10);

I hope this is useful.

:p:eyes::roll_eyes::smile::);):sunglasses::(:face_with_spiral_eyes::hushed::rage::sweat_smile:

1 Like

I cant get this script to split the laser more than three times, does anyone know why?

sometime not working for some angle. no reflection in 2D.
may be lastLaserPosition fail.

Nice! but the only thing wrong is when a split hit’s it’s it self it creates in infinite loop that crashes unity.

yes I am having a same issue .Did you solved it ?

Wanted to share my own 2d version.

let me know any optimizations you may find:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Laser : MonoBehaviour
{
    public int maxReflectionCount = 5;
    public int maxSplitCount = 5;
    public float maxStepDistance = 100;

    private void OnDrawGizmos()
    {
        if (!Application.isPlaying)
        {
            return;
        }
        DrawPredictedReflection(this.transform.position, this.transform.up, maxReflectionCount, maxSplitCount);
    }

    void DrawPredictedReflection(Vector2 position, Vector2 direction, int reflectionsRemaining, int splitsRemaining)
    {
        var gizmoHue = (reflectionsRemaining / (this.maxReflectionCount + 1f));
        Gizmos.color = Color.HSVToRGB(gizmoHue, 1, 1);
        RaycastHit2D hit2D = Physics2D.Raycast(position, direction, maxStepDistance);

        if (hit2D) //did we hit somthing?
        {
            Gizmos.DrawLine(position, hit2D.point);
            Gizmos.DrawWireSphere(hit2D.point, 0.25f);

            if (hit2D.transform.gameObject.tag == "Receiver")
            {
                Debug.Log("Receiver hit");
            }
            if (hit2D.transform.gameObject.tag == "Mirror") //mirror hit. set new pos where hit. reflect angle and make that new direction
            {
                Debug.Log("Mirror Hit");
                direction = Vector2.Reflect(direction, hit2D.normal);
                position = hit2D.point + direction * 0.01f;

                if (reflectionsRemaining > 0)
                    DrawPredictedReflection(position, direction, --reflectionsRemaining, splitsRemaining);
            }
            if (hit2D.transform.gameObject.tag == "Splitter") //reflect and go ahead
            {

                Debug.Log("Splitter hit");
                if (splitsRemaining > 0)//go ahead
                {
                    Debug.Log("Splitting");
                    Vector2 splitPosition = new Vector2();
                    Vector2 findOppBegin = hit2D.point + direction * 1f;
                    RaycastHit2D[] findOppHit = Physics2D.RaycastAll(findOppBegin, -direction);
                    for ( int i = 0;  i <= findOppHit.Length; i++) //findOppHit[i].transform.gameObject != hit2D.transform.gameObject
                    {
                        if (findOppHit[i].transform.gameObject == hit2D.transform.gameObject)
                        {
                            splitPosition = findOppHit[i].point + direction * 0.01f;
                            break;
                        }
                    }

                    DrawPredictedReflection(splitPosition, direction, reflectionsRemaining, --splitsRemaining);
                }
                
            
                direction = Vector2.Reflect(direction, hit2D.normal);
                position = hit2D.point + direction * 0.01f;
                if (reflectionsRemaining > 0)//reflect too
                {
                    DrawPredictedReflection(position, direction, --reflectionsRemaining, splitsRemaining);
                }
            }
        }
    }
}

PLEASE HELP ME!
my goal is create a line from point A to point B, in point B (hit.point) i need to create a new line (or colorize a section in the same line) from point B to point C, but maintein de line before A-B. the new line need to give the color from material hit. like image: