Points in a line renderer detecting collision

Hello guys,

In the following attachment you can see the yellow line. It is made out of 10 gameobjects, the first object is attached to the gun and the last object is attached to a spear. I am trying to make the gameobjects that are between the first and the last one, collide with the rocks so they don’t go through the rocks or any other element in the environment. I would appreciate if anyone could point me in the right direction to achieve this. Below the picture is the code I created to move the points in the line and render the line:

using UnityEngine;
using System.Collections.Generic;

public class SpearLineDynamicNew : MonoBehaviour
{

    [Header("ONLY LAYERS WITH ROCKS")]
    public LayerMask onlyLayers;
    [Space]
    [Header("ONLY FOR TESTING PURPOSE")]
    public bool shoot = false;

    LineRenderer lineRend;

    Transform point0;
    Transform point1;
    Transform point2;
    Transform point3;
    Transform point4;
    Transform point5;
    Transform point6;
    Transform point7;
    Transform point8;
    Transform tipOfGunTrans;
    Transform spearTrans;

    List<Transform> linePointsList = new List<Transform>();// This list will handle added points: add/delete them

    public delegate void LostViewOfSpear(Transform homeObj);
    public static event LostViewOfSpear OnNewGuide;

    bool canUpdate = false;


    void Start()
    {
        point0 = GetComponent<Transform>();//This is the firstPoint which contains the script and will be attached to the gun tip
        point1 = GameObject.Find("LinePoint1").GetComponent<Transform>();
        point2 = GameObject.Find("LinePoint2").GetComponent<Transform>();
        point3 = GameObject.Find("LinePoint3").GetComponent<Transform>();
        point4 = GameObject.Find("LinePoint4").GetComponent<Transform>();
        point5 = GameObject.Find("LinePoint5").GetComponent<Transform>();
        point6 = GameObject.Find("LinePoint6").GetComponent<Transform>();
        point7 = GameObject.Find("LinePoint7").GetComponent<Transform>();
        point8 = GameObject.Find("LastPoint").GetComponent<Transform>();//Last point to be attached to spearTrans

        lineRend = GetComponent<LineRenderer>();

        linePointsList.Add(point0);
        linePointsList.Add(point1);
        linePointsList.Add(point2);
        linePointsList.Add(point3);
        linePointsList.Add(point4);
        linePointsList.Add(point5);
        linePointsList.Add(point6);
        linePointsList.Add(point7);
        linePointsList.Add(point8);

        if (lineRend != null)
        {
            SetupLineRend();
        }
    }


    void SetupLineRend()//prepare the line and hide it when scene starts
    {
        lineRend.positionCount = 9;
        for(int i = 0; i < linePointsList.Count; i++)
        {
            lineRend.SetPosition(i, linePointsList[i].position);
        }
        lineRend.enabled = false;
        canUpdate = false;
    }


    void OnEnable()
    {
        SpearScript.OnSpearIsOut += OnSpearIsOut;
    }

    void OnDisable()
    {
        SpearScript.OnSpearIsOut -= OnSpearIsOut;
    }

    void OnSpearIsOut(bool isOut)//If spear is out shows line otherwise hide the line and resets it
    {
        if (isOut)
        {
            InitLineRend();
        }
        else
        {
            if (lineRend != null)
            {
                SetupLineRend();
            }
        }
    }


    void LateUpdate()
    {
        if (!GameManagerScript.GmScript.gameIsPausedByEngagementGM)
        {
            if (canUpdate)
            {
                UpdateLine();
            }
        }
    }


    void UpdateLine()
    {
        for (int i = 0; i < linePointsList.Count - 1; i++)
        {
            linePointsList[i].LookAt(spearTrans.position);
        }

        lineRend.SetPosition(0, point0.position);
        lineRend.SetPosition(8, point8.position);

        for (int i = 1; i < linePointsList.Count - 1; i++)
        {
            Vector3 targetPos = (linePointsList[i - 1].position + linePointsList[i + 1].position) * 0.5f;
            linePointsList[i].position = Vector3.Lerp(linePointsList[i].position, targetPos, Time.deltaTime * 30.0f);
            lineRend.SetPosition(i, linePointsList[i].position);
        }

        for (int i = linePointsList.Count - 2; i > 0; i--)
        {
            if (Vector3.Distance(spearTrans.position, linePointsList[i].position) < 1.0f)
            {
                OnNewGuide?.Invoke(linePointsList[i - 1]);
            }
        }
    }


    void InitLineRend()
    {
        tipOfGunTrans = GameObject.FindGameObjectWithTag("TipOfGun").GetComponent<Transform>();
        spearTrans = GameObject.FindGameObjectWithTag("Spear").GetComponent<Transform>();

        point0.position = tipOfGunTrans.position;
        point0.rotation = tipOfGunTrans.rotation;
        point0.SetParent(tipOfGunTrans, true);

        point8.position = spearTrans.position;
        point8.SetParent(spearTrans, true);
        for (int i = 1; i < linePointsList.Count - 1; i++)
        {
            linePointsList[i].position =  point0.position;
            lineRend.SetPosition(i, linePointsList[i].position);
        }
        OnNewGuide?.Invoke(linePointsList[linePointsList.Count - 2]);
        canUpdate = true;//So update can work
        lineRend.enabled = true;
    }

}

Regards,
Carlos

1 Like

First let me note that as a certified scuba diver, COOL! I like where you’re going with this!

Second of all, I think your big win for this will be to raycast from your spear tip forward for each segment of your spear trajectory, seeing at each one if you hit something.

The cool part about Raycast is that it can give you a RaycastHit object, and that object will contain the precise point in space your raycast hit a collider, which would give you a way to stop it even on a thin little piece of coral, assuming the coral has a collider obviously.

Finally, let me offer you this tip: please become familiar with arrays and/or lists. By coding this with hard-wired ten points, it makes it very difficult to insert something like a raycast at each step. It would be miles of copy/pasted code, and that is BAD.

When you become familiar with how arrays/lists work, the logic for your trajectory would change to a loop.

First, at script start, make a List of points that will go into your LineRenderer

And here is the main loop:

  • clear the list of points in a temporary list
  • start a Vector3 at your spear tip
  • calculate one forward chunk of motion
  • raycast that chunk
  • if you hit something, you’re done: put the hit point in the temp list and quit
  • if you don’t hit something, re-cast from the new destination, however far forward you want a chunk to be
  • adjust spear trajectory downward however you would like
  • if you travel “far enough” distance-wise (max range), quit
  • go back to the step marked + above

When you’re done:

  • transfer the count of line points in your temp array to the LineRenderer
  • transfer the actual line points to the LineRenderer

@Kurt-Dekker is absolutely right. An additional thought is that this is very much like warp systems in VR. You’d also have a line that is affected by gravity and where you need to do a piecewise collision check.

Some code to do a Raycast along a List of Vector3:

public static bool Raycast(List<Vector3> points, out RaycastHit hitInfo, int layerMask = DefaultRaycastLayers)
{
   for (int i = 1; i < point.Count; i++)
   {
      Vector3 origin = points[i - 1];
      Vector3 direction = points[i] - origin;
      if (Physics.Raycast(origin, direction.normalized,  out hitInfo, direction.magnitude, layerMask)) return true;
   }
   hitInfo = new RaycastHit();
   return false;
}
1 Like