0
\$\begingroup\$

I have a script for a laser that can refract off mirrors, and the object of the game is to refract the lasers into the Receivers. Once the first room full of different lasers has connected with their corresponding receiver, the door will open.

I used a OnCollisionEnter in the Reciever script before I found out that LineRenderer cannot be detected by this.

So then to test it I compared the tag like this:

    if (gameObject.tag == "BLazer")
    {
         if (hit.collider.tag=="BTarget")
         {
                GameObject BR11 = GameObject.Find("BlueReceiver11");
                Receiver Receiver = BR11.GetComponent<Receiver>();
                string TEST= hit.collider.name;
                Receiver.HIT = true;

                break;
         }

Which works, however this would mean I would have to type out a check and bring in the objects for every single Receiver in the game in a script.

There has to be a better way than this.

I'll include the code here for the full scripts; ignore the random bools and ints, they're all for testing.

LASER SCRIPT

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

   [RequireComponent(typeof(LineRenderer))]
   public class Lazer : MonoBehaviour
   {
      public int reflections;
      public float maxLength;

    public static Lazer instance;

    private LineRenderer lineRenderer;
    private Ray ray;
    private RaycastHit hit;
    private Vector3 direction;
    public bool BL;
    public bool YL;
    public bool RL;
    public bool BL11;
    public bool BL12;
    public bool RL12;

    public int B;
    public int Y;
    public int R;

    private void Start()
    {
        instance = this;

    }

    private void Awake()
    {
        lineRenderer = GetComponent<LineRenderer>();
    }

    private void Update()
    {
        ray = new Ray(transform.position, transform.forward);

        lineRenderer.positionCount = 1;
        lineRenderer.SetPosition(0, transform.position);
        float remainingLength = maxLength;

        for (int i = 0; i < reflections; i++)
        {
            if (Physics.Raycast(ray.origin, ray.direction, out hit, remainingLength))
            {
                //Debug.Log("Transform Tag is: " + gameObject.tag);
                lineRenderer.positionCount += 1;
                lineRenderer.SetPosition(lineRenderer.positionCount - 1, hit.point);
                remainingLength -= Vector3.Distance(ray.origin,hit.point);
                ray = new Ray(hit.point, Vector3.Reflect(ray.direction, hit.normal));

                //BLUE LASER
                if (gameObject.tag == "BLazer")
                {
                    if (hit.collider.tag=="BTarget")
                    {
                        GameObject BR11 = GameObject.Find("BlueReceiver11");
                        Receiver Receiver = BR11.GetComponent<Receiver>();
                        string TEST= hit.collider.name;
                        Receiver.HIT = true;

                        break;
                    }
                    else
                    {
                        //BL = false;
                        Debug.Log("Blue false");
                        if (hit.collider.tag != "Mirror")
                        {
                            break;
                        }
                    }
                }

                //YELLOW LASER
                else if (gameObject.tag == "YLazer")
                {
                    if (hit.collider.tag == "YTarget")
                    {
                        if (YL == false)
                        {
                            Y++;
                            YL = true;

                            Debug.Log("Yellow hit Yellow! - " + Y);
                        }
                        break;

                    }
                    else
                    {
                        if (YL)
                        {
                            Y--;
                            YL = false;
                            Debug.Log("Yellow false - "+Y );
                        }


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

                //RED LASER
                else if (gameObject.tag == "RLazer")
                {
                    if (hit.collider.tag == "RTarget")
                    {
                        Debug.Log("Red hit Red! - " + gameObject.tag + " hit " + hit.collider.tag);
                        RL = true;
                        break;
                    }
                    else {
                        RL = false;
                        Debug.Log("Red false");
                        if (hit.collider.tag != "Mirror")
                        {
                            break;
                        }
                    }  
                } 

            }
            else
            {

                lineRenderer.positionCount += 1;
                lineRenderer.SetPosition(lineRenderer.positionCount - 1, ray.origin + ray.direction * remainingLength);
            }
        }
    }

 }

My (pretty empty now) Receiver class

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

public class Receiver : MonoBehaviour
{
    public bool HIT;

    private void Update()
    {
        if (HIT) {
            Debug.Log("IT WORKED - " +gameObject.name);
        }
    }

Room 1 Script

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

public class Room1 : MonoBehaviour
{
    public GameObject Door1;
    public GameObject RedReceiver11;
    public GameObject RedReceiver12;
    public GameObject YellowReceiver11;
    public GameObject YellowReceiver12;
    public GameObject BlueReceiver11;
    public GameObject BlueReceiver22;

    public GameObject gdgfdg;


    public bool RR11, RR12, YR11, YR12, BR11, BR12;
    /*
    void Start()
    {
        GameObject thePlayer = GameObject.Find("First Person Player");
        PlayerMovement playerScript = thePlayer.GetComponent<PlayerMovement>();
        Debug.Log(playerScript.jumpHeight);
    }
    */
    private void TEST() {

        //RedReceiver11
    }


    void Update()
    {
        //If all lasers hitting recievers then raise the door
       if (RR11 && RR12 && YR11 && YR12 && BR11 && BR12)
        {
            Debug.Log("R1 DONE");
            if (Door1.transform.position.y < 8)
            {
                Door1.transform.Translate(Vector3.up * Time.deltaTime, Space.World);
            }

        }
       //if not, keep it close or lower it
        else {

            if (Door1.transform.position.y > 2)
            {
                Door1.transform.Translate(Vector3.down * Time.deltaTime, Space.World);
            }
        }

    }
}
\$\endgroup\$
  • \$\begingroup\$ Is each room its own scene, with level loads in between? Or are they all part of one scene/level? \$\endgroup\$ – DMGregory May 13 at 1:48
  • \$\begingroup\$ @DMGregory All one scene. Super Inelegant. Game Jam style baby \$\endgroup\$ – ConnnK May 13 at 21:13
  • \$\begingroup\$ Haha, I know that feel 😆 \$\endgroup\$ – DMGregory May 13 at 21:15
  • \$\begingroup\$ Oh wow that looks so cool, nice one! \$\endgroup\$ – ConnnK May 13 at 21:45
0
\$\begingroup\$

I would move your door opening logic to the receiver. On your receiver prefab, have a public member variable of either your laser object (if you know it) or a layer mask that you assign in the inspector.

Then in your collider OnCollision() check to see if the colliding object is the correct laser. If it is, then open the door.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ As i said at the start of this post i already tried this and this does not work because LineRenderer doesnt have collisions and if i gave it collision it wouldnt be able to refract \$\endgroup\$ – ConnnK May 13 at 21:05
0
\$\begingroup\$

First, let's make an enumeration to keep track of our different laser colours. This way you don't have to create a new tag for every one, and you can use your tags for other things. Plus, no string comparisons! 😉

public enum LaserColor {
    Blue,
    Yellow,
    Red
}

Next, let's abstract the behaviour of turning a switch on/off. This might look weird right now, but it's going to help us avoid making a custom script for each different room. Instead, we can set up any action to be fired by any laser receiver, or any group of laser receivers, using this same building block. (You can even go as far as chaining these switches up like transistors to make a mini computer, like folks do with Redstone in Minecraft!)

public abstract class ToggleSwitch : MonoBehaviour {

    // Set up an event we can use to wire this to our door controls.
    [System.Serializable]
    public class SwitchEvent : UnityEvent<bool, ToggleSwitch> { }
    public SwitchEvent OnToggle;

    // This lets us fire a signal when we're first set, no matter whether we're turning on or off.
    bool _hasBeenSet;

    [SerializeField] // Show this in the inspector, to help debugging puzzles.
    bool _activated;  
    public bool activated {
        // Let outside scripts read our active status, but not set it.
        get { return _hasBeenSet ? _activated : false; }

        // Whenever our activation state changes, automatically fire our event to listeners.
        protected set {
            if (_hasBeenSet && value == _activated)
                return;

            _activated = value;
            _hasBeenSet = true;
            if (OnToggle != null)
                OnToggle.Invoke(value, this);
        }
    }
}

Your receiver is one kind of switch, that responds to lasers:

public class LaserSwitch : ToggleSwitch {
    // What color does this receiver respond to?
    public LaserColor color;

    // Use this to check whether no lasers hit us for a whole frame.
    bool _activatedThisFrame;

    // When we're enabled, let all our listeners know that we exist.
    // (This helps when we want to check whether "all" have been hit)
    void OnEnable() {
        _activatedThisFrame = false;
        activated = false;
    }

    // When we're disabled, let all our listeners know we're off.
    void OnDisable() {
        activated = false;
    }

    // Expose a function for our laser to call.    
    public void HitByLaser(LaserColor color) {
        // If it's the wrong laser colour, abort!
        if (color != this.color)
            return;

        // Set our active status.    
        _activatedThisFrame = true;
        activated = true;
    }

    // After all the normal updates have run, check if anything hit us.
    void LateUpdate() {
        // If nothing hit us this frame, de-activate.
        activated = _activatedThisFrame;

        // Reset the flag to check again next frame.
        _activatedThisFrame = false;
    }
}

Here's how that looks in our Inspector. You can see we get to freely control which script this laser switch activates by populating one or more listeners in the UnityEvent widget - that lets us set up lots of different room puzzles with one script.

Laser Switch inspector

Here I have it sending its activation events to a "GatherSwitch" - that's another type of switch that will listen to multiple other switches, and activate or deactivate depending on the activation values of all its contributors. Here's that script:

public class GatherSwitch : ToggleSwitch
{
    // Allow switching between...
    public enum Mode {
        All, // Hit all receivers to activate this item.
        Any  // Hit any receiver to activate this item.
    }
    public Mode mode;

    // Keep track of all switches wired to this one.
    List<ToggleSwitch> _toggles = new List<ToggleSwitch>();

    // This caching lets us use the TrueForAll / Exists methods without allocations.
    static bool IsToggled(ToggleSwitch toggle) { return toggle.activated; }
    static readonly System.Predicate<ToggleSwitch> ACTIVE = IsToggled;

    public void OnChildToggled(bool activated, ToggleSwitch sender) {
        // Automatically include any receivers that send an event to us.
        if (_toggles.Contains(sender) == false)
            _toggles.Add(sender);

        switch (mode) {
            case Mode.All:
                this.activated = _toggles.TrueForAll(ACTIVE);
                break;
            case Mode.Any:
                this.activated = _toggles.Exists(ACTIVE);
                break;
        }
    }
}

That looks like this:

Gather Switch inspector

Here I have the door control GatherSwitch listening to all the switches wired to it, and activating only when All of its contributor switches are active. When that happens, it calls a method on the door like this:

public void OpenClose(bool open, ToggleSwitch source = null) { ... }

(Giving a default value there lets you also call this method as just OpenClose(true); if you want control its state with scripts other than switches)

Finally, our laser can just get the LaserSwitch receiver it hit with GetComponent<LaserSwitch>(), and call HitByLaser() to log its hit.

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

    public LaserColor color;

    public int maxReflections = 8;
    public float maxLength = 50;

    LineRenderer _line;
    // We'll cache a buffer of points, so we can set them all at once for efficiency.
    Vector3[] _linePoints;

    void Start() {
        _line = GetComponent<LineRenderer>();
        _linePoints = new Vector3[maxReflections + 1];
    }

    void Update() {
        var ray = new Ray(transform.position, transform.forward);

        _linePoints[0] = ray.origin;

        float remainingLength = maxLength;

        int segmentCount = 0;
        for (int i = 1; i <= maxReflections; i++) {
            segmentCount++;

            // If we didn't hit anything, 
            // place our last point at the end of the remaining length and abort.
            if (!Physics.Raycast(ray, out RaycastHit hit, remainingLength)) {
                _linePoints[i] = ray.GetPoint(remainingLength);
                break;
            }

            // Otherwise, put a point at the location of the hit.
            _linePoints[i] = hit.point;

            // If we hit a laser switch receiver, call that switch's OnHitByLaser method.
            var receiver = hit.collider.GetComponent<LaserSwitch>();
            if (receiver != null)
                receiver.HitByLaser(color);

            // If we didn't hit a mirror, stop here.
            if (hit.collider.CompareTag("Mirror") == false)
                break;

            // Otherwise, reflect our ray and fire again.
            remainingLength -= hit.distance;
            ray = new Ray(hit.point, Vector3.Reflect(ray.direction, hit.normal));
        }

        // Set all our points in one call. More efficient than doing it one by one.
        _line.positionCount = segmentCount + 1;
        _line.SetPositions(_linePoints);
    }
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ As an exercise for the reader: add a "Not" switch, and you've got a set of logic gates powerful enough to encode any algorithm without loops. That's a huge set of possible puzzles you can make with just a tiny set of reusable components. 😊 \$\endgroup\$ – DMGregory May 13 at 4:17
0
\$\begingroup\$

I forgot to come back to this, and the above answer is a great one. If anyone is looking for this in the future and is stuck, what I did is super inelegant and very non-scalable, but you're welcome to go for it if you're looking for a quick and dirty solution.

Laser Changes

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

[RequireComponent(typeof(LineRenderer))]
public class Lazer : MonoBehaviour
{
    public int reflections;
    public float maxLength;

    public static Lazer instance;

    private LineRenderer lineRenderer;
    private Ray ray;
    private RaycastHit hit;
    private Vector3 direction;


    private void Start()
    {
        instance = this;

    }

    private void Awake()
    {
        lineRenderer = GetComponent<LineRenderer>();
    }

    private void Update()
    {
        ray = new Ray(transform.position, transform.forward);

        lineRenderer.positionCount = 1;
        lineRenderer.SetPosition(0, transform.position);
        float remainingLength = maxLength;

        for (int i = 0; i < reflections; i++)
        {
            if (Physics.Raycast(ray.origin, ray.direction, out hit, remainingLength))
            {
                //Debug.Log("Transform Tag is: " + gameObject.tag);
                lineRenderer.positionCount += 1;
                lineRenderer.SetPosition(lineRenderer.positionCount - 1, hit.point);
                remainingLength -= Vector3.Distance(ray.origin,hit.point);
                ray = new Ray(hit.point, Vector3.Reflect(ray.direction, hit.normal));

                //Level 1
                GameObject BR1 = GameObject.Find("BR1");
                Receiver Receiver1 = BR1.GetComponent<Receiver>();

                //Level 2
                GameObject YR2 = GameObject.Find("YR2");
                Receiver Receiver2 = YR2.GetComponent<Receiver>();

                //Level 3
                GameObject RR3 = GameObject.Find("RR3");
                Receiver Receiver3 = RR3.GetComponent<Receiver>();

                //Level 4
                GameObject BR4 = GameObject.Find("BR4");
                Receiver Receiver4A = BR4.GetComponent<Receiver>();
                GameObject YR4 = GameObject.Find("YR4");
                Receiver Receiver4B = YR4.GetComponent<Receiver>();

                //BLUE LASER LEVEL 3
                if (gameObject.name == "BL1")
                {
                    if (hit.collider.tag=="BTarget")
                    {
                        Receiver1.HIT = true;
                        break;
                    }
                    else
                    {
                        Receiver1.HIT = false;
                        if (hit.collider.tag != "Mirror")
                        {
                            break;
                        }
                    }
                }

                //YELLOW LASER LEVEL 2
                else if (gameObject.name == "YL2")
                {
                    if (hit.collider.tag == "YTarget")
                    {
                        Receiver2.HIT = true;
                        break;
                    }
                    else
                    {
                        Receiver2.HIT = false;
                        if (hit.collider.tag != "Mirror")
                        {
                            break;
                        }
                    }
                }

                //RED LASER LEVEL 3
                else if (gameObject.name == "RL3")
                {
                    if (hit.collider.tag == "RTarget")
                    {
                        Receiver3.HIT = true;
                        break;
                    }
                    else
                    {
                        Receiver3.HIT = false;
                        if (hit.collider.tag != "Mirror")
                        {
                            break;
                        }
                    }
                }


                //BLUE AND YELLOW LASER - LEVEL 4
                else if (gameObject.name == "BL4")
                {
                    if (hit.collider.tag == "BTarget")
                    {
                        Receiver4A.HIT = true;
                        break;
                    }
                    else
                    {
                        Receiver4A.HIT = false;
                        if (hit.collider.tag != "Mirror")
                        {
                            break;
                        }
                    }
                }

                else if (gameObject.name == "YL4")
                {
                    if (hit.collider.tag == "YTarget")
                    {
                        Receiver4B.HIT = true;
                        break;
                    }
                    else
                    {
                        Receiver4B.HIT = false;
                        if (hit.collider.tag != "Mirror")
                        {
                            break;
                        }
                    }
                }




                else if (gameObject.tag == "BLazer"|| gameObject.tag == "YLazer" || gameObject.tag == "RLazer")
                {
                    if (hit.collider.tag != "Mirror")
                    {
                        break;
                    }

                }

            }
            else
            {

                lineRenderer.positionCount += 1;
                lineRenderer.SetPosition(lineRenderer.positionCount - 1, ray.origin + ray.direction * remainingLength);
            }
        }
    }

}

And then I used a GameManager for the levels:

Game Manager

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

public class GM : MonoBehaviour
{
    public GameObject Gate1;
    public GameObject DoorL1;
    public GameObject BR1; //Level 1 Receiver
    public bool R1;

    public GameObject Gate2;
    public GameObject DoorL2;
    public GameObject YR2; //Level 2 Receiver
    public bool R2;

    public GameObject Gate3;
    public GameObject DoorL3;
    public GameObject RR3; //Level 3 Receiver
    public bool R3;

    public GameObject Gate4;
    public GameObject DoorL4A;
    public GameObject DoorL4B;
    public GameObject BR4; //Level 4 Receiver Blue
    public GameObject YR4; //Level 4 Receiver Yellow
    public bool R4;
    private bool L1Played;
    private bool L2Played;
    private bool L3Played;
    private bool L4Played;

    void Update()
    {

        //ROOM 1 

        Receiver R1Script = BR1.GetComponent<Receiver>();
        //If all lasers hitting recievers then raise the door

        if (R1Script.HIT)
        {
            Debug.Log("R1 DONE");
            if (Gate1.transform.position.y < 8)
            {
                Gate1.transform.Translate(Vector3.up * Time.deltaTime, Space.World);
                if (L1Played == false)
                {
                    Gate1.GetComponent<AudioSource>().Play();
                    L1Played = true;
                }
                //FindObjectOfType<AudioManager>().Play("LevelComplete");
                DoorL1.SetActive(false);
            }

        }
       //if not, keep it close or lower it
        else {

            if (Gate1.transform.position.y > 2)
            {
                Gate1.transform.Translate(Vector3.down * Time.deltaTime, Space.World);
                DoorL1.SetActive(true);
            }
        }



        //ROOM 2

        Receiver R2Script = YR2.GetComponent<Receiver>();
        //If all lasers hitting recievers then raise the door

        if (R2Script.HIT)
        {
            Debug.Log("R2 DONE");
            if (Gate2.transform.position.y < 8)
            {
                Gate2.transform.Translate(Vector3.up * Time.deltaTime, Space.World);
                //FindObjectOfType<AudioManager>().Play("LevelComplete");
                if (L2Played == false)
                {
                    Gate2.GetComponent<AudioSource>().Play();
                    L2Played = true;
                }
                DoorL2.SetActive(false);
            }

        }
        //if not, keep it close or lower it
        else
        {

            if (Gate2.transform.position.y > 2)
            {
                Gate2.transform.Translate(Vector3.down * Time.deltaTime, Space.World);
                DoorL2.SetActive(true);
            }
        }

        //ROOM 3

        Receiver R3Script = RR3.GetComponent<Receiver>();
        //If all lasers hitting recievers then raise the door

        if (R3Script.HIT)
        {
            Debug.Log("R3 DONE");
            if (Gate3.transform.position.y < 8)
            {
                Gate3.transform.Translate(Vector3.up * Time.deltaTime, Space.World);
                //FindObjectOfType<AudioManager>().Play("LevelComplete");
                if (L3Played == false)
                {
                    Gate3.GetComponent<AudioSource>().Play();
                    L3Played = true;
                }
                DoorL3.SetActive(false);
            }

        }
        //if not, keep it close or lower it
        else
        {

            if (Gate3.transform.position.y > 2)
            {
                Gate3.transform.Translate(Vector3.down * Time.deltaTime, Space.World);
                DoorL3.SetActive(true);
            }
        }


        //ROOM 4

        Receiver R4ScriptA = BR4.GetComponent<Receiver>();
        Receiver R4ScriptB = YR4.GetComponent<Receiver>();
        //If all lasers hitting recievers then raise the door

        if (R4ScriptA.HIT && R4ScriptB.HIT)
        {
            Debug.Log("R4 DONE");
            if (Gate4.transform.position.y < 8)
            {
                Gate4.transform.Translate(Vector3.up * Time.deltaTime, Space.World);
                //FindObjectOfType<AudioManager>().Play("LevelComplete");
                if (L4Played == false)
                {
                    Gate4.GetComponent<AudioSource>().Play();
                    L4Played = true;
                }
                DoorL4A.SetActive(false);
                DoorL4B.SetActive(false);
            }

        }
        //if not, keep it close or lower it
        else
        {

            if (Gate4.transform.position.y > 2)
            {
                Gate4.transform.Translate(Vector3.down * Time.deltaTime, Space.World);
                DoorL4A.SetActive(false);
                DoorL4B.SetActive(false);
            }
        }
    }
}
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.