M.Strachan
M.Strachan

Reputation: 135

Reflect Laser off collider

I'm trying to accurately reflect a LaserBeam off a wall/collider. The Laser Beam is fired from a LaserGun and is small, measuring around 2.

So I have currently set my LaserBeam as a 3D Capsule and coloured it red. Its pretty ugly, but I'm not too concerned just now.

Whenever the LaserBeam collides with an Object/Wall it doesn't rebound as I would like. I suppose it would have to be completely absorbed by the wall/collider so that the direction would be pointing the correct way.

The image in the website below should explain what I'm trying to achieve. http://answers.unity3d.com/questions/631311/making-an-object-bounce-off-a-wall-the-same-way-li.html (If the LaserBeam was an Arrow. And the arrow is the InDirection and once the Arrow has collided with the wall, it would be in the direction of the Result).

I have set both the Drag and AngularDrag to 0. In my Capsule material. Dynamic and Static Friction is set to 0. Bounciness is 1. FrictionCombine is Min and BounceCombine is Max.

I'm wondering if I have to change the 3D Capsule to a LineRenderer?

Any help would be greatly appreciated.

Upvotes: 0

Views: 1143

Answers (2)

Thomas Hilbert
Thomas Hilbert

Reputation: 3629

This script detects collisions solely using Raycasts. It first does a Raycast at its (the lasers) tip, testing if it collides with anything. If it does, it spawns a reflected version of itself. Also it does q Raycast at its end, to test if it completely penetrated a collider. If so, it destroys itself. The result is a pretty realistic laser beam that even "bends" on collision while keeping its total length.

The script assumes

  • that the laser beam has a CapsuleCollider which points in Y-direction and that's centered at (0, 0, 0)
  • that there is no Rigidbody attached to it
  • that you specified the Layers you want your laser to bounce off of in wallLayers
  • that the laser beam itself is in a Layer not included in wallLayers
  • that your walls are big enough to completely contain the laser beam (this is only relevant because otherwise it will look weird, but work nonetheless)

The script will

  • travel along its object's positive Y axis by speedOfLight units per second
  • reliably detect collisions even at very (!) high travel speeds
  • freely travel in 3D space, so if you want it to stick to a plain you'll have to add logic for that

Test it out, it's fun:

using UnityEngine;

[RequireComponent(typeof(CapsuleCollider))]
public class LaserBeam : MonoBehaviour
{
    public LayerMask wallLayers;
    public float speedOfLight = 1f;

    private Vector3 reflectionTestPointLocal, completeIntrusionTestPointLocal;
    private float beamLengthLocal;
    private bool didSpawnReflection;

    void Awake()
    {
        CapsuleCollider capsuleCollider = GetComponent<CapsuleCollider>();
        beamLengthLocal = capsuleCollider.height;
        reflectionTestPointLocal = Vector3.up * beamLengthLocal * .5f;
        completeIntrusionTestPointLocal = -Vector3.up * beamLengthLocal * .5f;
    }

    void Update()
    {
        float stepLength = speedOfLight * Time.deltaTime;

        Travel(stepLength);
    }

    private void Travel(float stepLength)
    {
        Vector3 step = transform.up * stepLength;

        if (!didSpawnReflection)
        {
            RaycastHit hit;
            if (Physics.Raycast(transform.TransformPoint(reflectionTestPointLocal), step, out hit, stepLength, wallLayers))
            {
                SpawnReflection(hit.point, hit.normal, stepLength - hit.distance);
                didSpawnReflection = true;
            }
        }
        if (didSpawnReflection)
        {
            RaycastHit hit;
            if (Physics.Raycast(transform.TransformPoint(completeIntrusionTestPointLocal), step, out hit, stepLength, wallLayers))
            {
                Destroy(gameObject);
            }
        }

        transform.position += step;
    }

    private void SpawnReflection(Vector3 pointOfReflection, Vector3 reflectionNormal, float intrusion)
    {
        float impactAngle = Vector3.Angle(-transform.up, reflectionNormal);
        Vector3 impactTangent = Vector3.Cross(-transform.up, reflectionNormal);

        Quaternion reflectedRotation = Quaternion.AngleAxis(180 + impactAngle * 2, impactTangent) * transform.rotation;

        LaserBeam reflectedBeam = Instantiate(this);
        reflectedBeam.gameObject.name = gameObject.name;
        reflectedBeam.transform.parent = transform.parent;
        reflectedBeam.transform.localScale = transform.localScale;
        reflectedBeam.transform.rotation = reflectedRotation;
        reflectedBeam.transform.position = pointOfReflection - reflectedBeam.transform.TransformVector(Vector3.up * beamLengthLocal * .5f);

        reflectedBeam.Travel(intrusion);
    }
}

Upvotes: 1

Kardux
Kardux

Reputation: 2157

Have you tried using a TrailRenderer component?

This way you would have your "Laser" set up as a small sphere (which size will be your lasers width): on this object, you'll have the following componennts:

  • Transform (kinda obvious)
  • SphereCollider (you can set its size and give it a bouncy physic material)
  • RigidBody (set both drags to zero, use gravity to false and freeze all rotations)
  • TrailRenderer (here you can adjust the visual of your laser: width, material, length (using the Time property), ...)

To "fire" a laser you simply have to set this object as a prefab and instantiate it at a given position, afterward simply set the RigidBody velocity according to the desired speed/direction :)

One benefit of this method is you can easily register to OnCollision[...] events and use them to your needs.

Hope this helps,

Upvotes: 0

Related Questions