-1
\$\begingroup\$
public class BallShooter : MonoBehaviour{

public LineRenderer laserRenderer;
public int laserDistance = 800;
public int numberReflectMax = 5;
public int countLaser = 1;
public Vector3 pos = new Vector3();
public Vector3 directLaser = new Vector3();
private bool loopActive = true;
public LayerMask layerMask;
public GameObject bottomShootPoint;


void Update()
{
    Laser();

}
private void Start()
{

}
void Laser()
{
    loopActive = true;
    int countLaser = 1;

    var mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    pos = transform.position;
    directLaser = Camera.main.ScreenToWorldPoint(Input.mousePosition) - pos;
    laserRenderer.positionCount = countLaser;
    laserRenderer.SetPosition(0, pos);
    //float angle = Mathf.Atan2(directLaser.y, directLaser.x) * 100 / Mathf.PI;

    while (loopActive)
    {
        RaycastHit2D hit = Physics2D.Raycast(pos, directLaser, laserDistance, layerMask);

        if (hit)
        {
            countLaser++;
            laserRenderer.positionCount = countLaser;

            if (hit.collider.CompareTag("Wall"))
                directLaser = Vector2.Reflect(directLaser, hit.normal);

            pos = (Vector2)directLaser.normalized + hit.point;
            laserRenderer.SetPosition(countLaser - 1, hit.point);
        }
        else
        {
            countLaser++;
            laserRenderer.positionCount = countLaser;
            laserRenderer.SetPosition(countLaser - 1, pos + (directLaser.normalized * laserDistance));
            loopActive = false;
        }
        if (countLaser > numberReflectMax)
        {
            loopActive = false;
        }

        if (mousePos.y < bottomShootPoint.transform.position.y)
            laserRenderer.enabled = false;
        else laserRenderer.enabled = true;
    }
}

Game view LineRenderer GameObject

\$\endgroup\$
4
  • 1
    \$\begingroup\$ What is the total countLaser in your example? \$\endgroup\$
    – Zibelas
    Apr 2 at 13:27
  • 1
    \$\begingroup\$ How would you expect it to look? How is"New Material 1" configured? \$\endgroup\$
    – Philipp
    Apr 2 at 13:49
  • \$\begingroup\$ @Zibelas countLaser = 1 always \$\endgroup\$
    – John
    Apr 2 at 20:26
  • \$\begingroup\$ @Philipp i.stack.imgur.com/0MBhb.png \$\endgroup\$
    – John
    Apr 2 at 20:29

1 Answer 1

2
\$\begingroup\$

Here's an attempt to reproduce your situation:

Distorted dots

Notice how the circles develop a kink as we move from the free end of the line toward one of the sharp corners?

That happens because, by default, the line generates triangles that look like this:

Line wireframe

See how we get just two vertices at each "position", and wonky trapezoid segments joining them? Affine texture mapping does not handle these trapezoids well, and has to put a kink along one diagonal where the two triangle halves meet.

A quick fix is to increase the "Corner Vertices" parameter on your LineRenderer. This tells Unity to insert extra vertices around sharp corners to round them out, so you get neat rectangular segments in the straight parts, followed by an elbow bend that rounds the corner:

Increasing Corner Vertices

This gives correct, undistorted rendering along the straight segments, but can still distort things at the corners. To avoid that, you can use a separate line renderer or quad for each straight-line segment.

Better dots

Here's an example of a script that generates and positions quads for each line segment, ensuring that each segment always contains a whole number of dots: ending at a dot exactly where the next segment starts.

public class DottedLine : MonoBehaviour
{
    [Tooltip("Material for the line - make sure texture is set to repeat.")]
    [SerializeField] Material _lineMaterial;

    [Tooltip("Width of the line, in local units.")]
    [SerializeField] float _lineWidth = 0.5f;

    // Changing width should force a re-draw before next frame.
    public float LineWidth {
        get => _lineWidth;
        set {
            _lineWidth = value;
            _isDirty = true;
        }
    }
 
    // Handle updating visuals when changing Inspector properties in editor.
    void OnValidate() {
        if (_quads == null || _quads.Count == 0)
            return;

        if (_lineWidth != _quads[0].transform.localScale.y) {
            _isDirty = true;
        }

        if ( _quads[0].sharedMaterial != _lineMaterial) {
            foreach (var quad in _quads)
                quad.sharedMaterial = _lineMaterial;
        }
    }

    // Corner points of the line.
    List<Vector3> _points;

    // Child quads spanning pairs of points.
    List<MeshRenderer> _quads;

    // Container for per-segment properties.
    MaterialPropertyBlock _properties;

    // ID of the texture repeat parameter for fast lookups.
    int _scaleRepeatId;

    // Dirty flag indicates when properties have been changed
    // and quads need to be re-arranged to draw the line.
    bool _isDirty;

    /// <summary>
    /// Erases all points and segments from the line.
    /// </summary>
    public void Clear() {
        _points.Clear();
        foreach(var quad in _quads)
            quad.gameObject.SetActive(false);
    }

    /// <summary>
    /// Sets a corner point on the line, adding default points in-between.
    /// </summary>
    /// <param name="index">Index of this corner (zero-based).</param>
    /// <param name="point">Local coordinates of this corner.</param>
    public void SetPoint(int index, Vector3 point) {
        while(index >= _points.Count)
            _points.Add(Vector3.zero);
        
        _points[index] = point;
        _isDirty = true;
    }

    void Awake() {
        _points = new();
        _quads = new();
        _properties = new();
        _scaleRepeatId = Shader.PropertyToID("_MainTex_ST");
    }

    // Before rendering a frame, check if we need to reposition child quads.
    void LateUpdate()
    {
        // Early-out if nothing has changed.
        if (!_isDirty || _points.Count < 2) return;

        Vector3 scale = new Vector3(_lineWidth, _lineWidth, _lineWidth);
        Vector4 textureRepeat = new Vector4(1, 1, 0, 0);

        // Iterate over each consecutive pair of points to draw a quad
        // for the straight-line segment joining them.
        var lastPoint = _points[0];
        for (int nextPointIndex = 1; nextPointIndex < _points.Count; nextPointIndex++) {
            var nextPoint = _points[nextPointIndex];

            // Get or create a quad to render the line segment
            // joining lastPoint to nextPoint.
            MeshRenderer quad;
            if (_quads.Count >= nextPointIndex) {
                quad = _quads[nextPointIndex - 1];
                quad.gameObject.SetActive(true);
            } else {
                var primitive = GameObject.CreatePrimitive(PrimitiveType.Quad);
                primitive.transform.parent = transform;
                primitive.TryGetComponent(out quad);
                quad.sharedMaterial = _lineMaterial;
                _quads.Add(quad);
            }

            // Compute and apply transform so that the quad spans the gap
            // between lastPoint and nextPoint (overhanging by half the line
            // width on each side, to always show a full dot, never cut off).
            var average = (lastPoint + nextPoint) * 0.5f;
            var offset = nextPoint - lastPoint;

            float angle = Mathf.Atan2(offset.y, offset.x) * Mathf.Rad2Deg;
            Quaternion orientation = Quaternion.Euler(0, 0, angle);

            quad.transform.SetLocalPositionAndRotation(average, orientation);

            scale.x = offset.magnitude + _lineWidth;                        
            quad.transform.localScale = scale;
        
            // Calculate how many dots will best fit in this line segment width.
            // Change the material properties to repeat the texture x times.
            textureRepeat.x = Mathf.Round(scale.x/_lineWidth);
            _properties.SetVector(_scaleRepeatId, textureRepeat);
            quad.SetPropertyBlock(_properties);

            // The end of one segment is the start of the next.
            lastPoint = nextPoint;
        }

        // No need to re-position quads again until some of our
        // properties get changed.
        _isDirty = false;
    }
}

Here's this script in action:

Quad per segment

You can use it similar to a LineRenderer, calling SetPoint(index, position) to place corners on the line, and Clear() to erase the line (in case you need to make it shorter or hide it).

\$\endgroup\$
4
  • \$\begingroup\$ Yes, that's right, I found such a solution to the problem, but now another small problem has arisen, small artifacts appear at the point of intersection with the wall, could you tell me please how I can fix them? i.stack.imgur.com/9RgG7.jpg \$\endgroup\$
    – John
    Apr 2 at 20:23
  • \$\begingroup\$ See the last sentence of my answer: "To avoid that, you can use a separate line renderer or quad for each straight-line segment." \$\endgroup\$
    – LudoProf
    Apr 3 at 0:03
  • \$\begingroup\$ I don't quite understand what it means, separate line render or quad for each straight-line segment, explain it to me, please \$\endgroup\$
    – John
    Apr 3 at 6:16
  • \$\begingroup\$ @John I've added an example script that does this. \$\endgroup\$
    – LudoProf
    Apr 5 at 19:47

You must log in to answer this question.

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