The story of a line (drawing package based on LineRenderer)

The general steps for using LineRenderer to draw line segments in Unity development:
1. Create an object
2. Add component LineRenderer
3. Write a script to specify LineRenderer to draw line segments through the specified LineRenderer.
In fact, if you only spend one line, it seems not that complicated. But if I have a requirement and need to draw 100 items, this operation would be too perverted. . .
Maybe some smart students will say, then I can make it into a prefab, and each prefab has an object for drawing lines. This is obviously much simpler than the above method.
But the question also arises. If I still need to manage the addition, deletion, modification and checking of these line segments, do I need to write a Manager to manage all line segments?
Let me simply write a LineManager to manage all line drawing functions in the project. . .

Let's start with the Line object. First of all, we have to think about what methods or attributes a most basic line segment management must have. . .
Don't think about it, let me give you some ideas. Take a look at the ILine interface below. Of course, if you feel you can add other attributes or methods, you can try to implement it yourself. . .

using UnityEngine;

namespace S
{
    public interface ILine
    {
        ILineManager lineManager { get; }
        /// <summary>
        /// 材质
        /// </summary>
        Material material { get; set; }
        /// <summary>
        /// 开始颜色
        /// </summary>
        Color startColor { get; set; }
        /// <summary>
        /// 结束颜色
        /// </summary>
        Color endColor { get; set; }
        /// <summary>
        /// 开始宽度
        /// </summary>
        float startWidth { get; set; }
        /// <summary>
        /// 结束宽度
        /// </summary>
        float endWidth { get; set; }
        /// <summary>
        /// 是否使用世界坐标
        /// </summary>
        bool useWorldSpace { get; set; }

        /// <summary>
        /// 是否循环
        /// </summary>
        bool loop { get; set; }

        /// <summary>
        /// 坐标点的数量
        /// </summary>
        int positionCount { get; set; }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="lineManager">管理者</param>
        void Init(ILineManager lineManager);

        /// <summary>
        /// 增加一个坐标
        /// </summary>
        void AddPosition(Vector3 position);

        /// <summary>
        /// 减少一个坐标
        /// </summary>
        /// <param name="index">索引</param>
        void RemovePosition(int index);

        /// <summary>
        /// 设置一个坐标
        /// </summary>
        /// <param name="index">索引</param>
        /// <param name="position">坐标</param>
        void SetPosition(int index,Vector3 position);
        
        /// <summary>
        /// 设置多个坐标
        /// </summary>
        /// <param name="positions">坐标数组</param>
        void SetPositions(Vector3[] positions);

        /// <summary>
        /// 得到一个坐标
        /// </summary>
        /// <param name="index">索引</param>
        /// <returns></returns>
        Vector3 GetPosition(int index);

        /// <summary>
        /// 得到所有的坐标
        /// </summary>
        /// <returns></returns>
        Vector3[] GetPositions();

        /// <summary>
        /// 清空绘制内容
        /// </summary>
        void Clear();

        /// <summary>
        /// 释放对象
        /// </summary>
        void Dispose();
    }
}

Is it similar to the properties and methods in LineRenderer? Yes, because the methods and properties of LineRenderer are already very comprehensive. There are more methods than LineRenerer, namely AddPosition, RemovePosition, Clear, and Dispose. Others are not as comprehensive as LineRenderer. If necessary later, you can add properties and methods to the interface. Here we only list the most commonly used ones.

Then we have written the interface of the line segment, and then we will start writing the LineManager.

using System.Collections.Generic;
using UnityEngine;

namespace S
{
    public interface ILineManager
    {
        public GameObject gameObject { get; }
        public Transform transform { get; }
        int lineCount { get; }

        /// <summary>
        /// 创建一条ILine
        /// </summary>
        /// <returns></returns>
        T CreateLine<T>()where T:ILine,new();

        /// <summary>
        /// 管理器中是否存在指定ILine
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        bool HasLine(ILine line);

        /// <summary>
        /// 删除一条ILine
        /// </summary>
        /// <param name="line">目标ILine</param>
        void DeleteLine(ILine line);

        /// <summary>
        /// 删除一些ILine
        /// </summary>
        void DeleteLines(ILine[] lines);

        /// <summary>
        /// 删除一些ILine
        /// </summary>
        /// <param name="lines"></param>
        void DeleteLines(List<ILine> lines);

        /// <summary>
        /// 删除所有ILine
        /// </summary>
        void DeleteAll();
    }
}

Yes, the current LineManager functions are indeed relatively few, and they are basically used to handle creation and deletion, oh! There is also a count lineCount. During the later development process, if more complex management functions are needed, you can add them slowly.

So now that we have written the templates for line segments and managers, the next step is to implement these two interfaces, and then we are done.

Line:

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

namespace S
{
    public static class LineRendererPool
    {
        private static Queue<LineRenderer> queue= new Queue<LineRenderer>();
        public static int Count => queue.Count;
        public static int maxCount = 10;

        public static LineRenderer Dequeue()
        {
            LineRenderer lr = null;
            while (lr==null&&Count>0)
            {
                lr = queue.Dequeue();
            }
            if (lr==null)lr = new GameObject("Line").AddComponent<LineRenderer>();
            return lr;
        }

        public static void EnQueue(LineRenderer lr)
        {
            if (lr == null) return;
            if (queue.Contains(lr)) return;
            if (Count>=maxCount)
            {
                UnityEngine.Object.Destroy(lr.gameObject);
                return;
            }
            queue.Enqueue(lr);
        }
    }

    public class Line : ILine
    {
        protected LineRenderer lineRenderer { get; private set; }
        public ILineManager lineManager { get; private set; }

        public Material material
        {
            get => lineRenderer.material;
            set => lineRenderer.material = value;
        }

        public Color startColor
        {
            get => lineRenderer.startColor;
            set => lineRenderer.startColor = value;
        }

        public Color endColor
        {
            get => lineRenderer.endColor;
            set => lineRenderer.endColor = value;
        }

        public float startWidth
        {
            get => lineRenderer.startWidth;
            set => lineRenderer.startWidth = value;
        }

        public float endWidth
        {
            get => lineRenderer.endWidth;
            set => lineRenderer.endWidth = value;
        }

        public bool useWorldSpace
        {
            get => lineRenderer.useWorldSpace;
            set => lineRenderer.useWorldSpace = value;
        }

        public bool loop
        {
            get => lineRenderer.loop;
            set => lineRenderer.loop = value;
        }

        public int positionCount
        {
            get => lineRenderer.positionCount;
            set => lineRenderer.positionCount = value;
        }

        public void Init(ILineManager lineManager)
        {
            if (lineManager==null)throw new Exception("Line Init 失败,LineManager不可为null");
            this.lineManager = lineManager;
            lineRenderer = LineRendererPool.Dequeue();
            lineRenderer.transform.SetParent(lineManager.transform);
            lineRenderer.transform.localScale=Vector3.one;
            lineRenderer.transform.localPosition=Vector3.zero;
            Clear();
            Inited();
        }
        
        protected virtual void Inited()
        {
        }

        public void AddPosition(Vector3 position)
        {
            int index = positionCount;
            positionCount++;
            SetPosition(index,position);
        }

        public void RemovePosition(int index)
        {
            if (index < 0 || index >= positionCount) return;
            if (index == positionCount - 1)
            {
                positionCount--;
                return;
            }
            for (int i = index+1; i < positionCount; i++)
            {
                SetPosition(i-1,GetPosition(i));
            }
            positionCount--;
        }

        public void SetPosition(int index, Vector3 position)
        {
            lineRenderer.SetPosition(index,position);
        }

        public void SetPositions(Vector3[] positions)
        {
            lineRenderer.SetPositions(positions);
        }

        public Vector3 GetPosition(int index)
        {
            return lineRenderer.GetPosition(index);
        }

        public Vector3[] GetPositions()
        {
            Vector3[] points=new Vector3[lineRenderer.positionCount];
            lineRenderer.GetPositions(points);
            return points;
        }

        public void Clear()
        {
            if (lineRenderer == null) return;
            positionCount = 0;
        }

        void ILine.Dispose()
        {
            Clear();
            LineRendererPool.EnQueue(lineRenderer);
            lineRenderer = null;
            lineManager = null;
        }
    }
}

LineManager:

using System.Collections.Generic;
using UnityEngine;

namespace S
{
    public class LineManager : MonoBehaviour,ILineManager
    {
        private static LineManager _instance;

        public static LineManager instance
        {
            get
            {
                if (_instance==null)
                {
                    _instance = FindObjectOfType<LineManager>();
                    if (_instance==null)
                    {
                        _instance=new GameObject("LineManager").AddComponent<LineManager>();
                        DontDestroyOnLoad(_instance.gameObject);
                    }
                }
                return _instance;
            }
        }

        public int lineCount => lines.Count;
        private List<ILine> lines= new List<ILine>();
        
        public T CreateLine<T>()where T:ILine,new()
        {
            T line = new T();
            line.Init(this);
            lines.Add(line);
            return line;
        }

        public bool HasLine(ILine line)
        {
            if (line==null||lineCount == 0) return false;
            return lines.Contains(line);
        }

        public void DeleteLine(ILine line)
        {
            if (!HasLine(line)) return;
            lines.Remove(line);
            line.Dispose();
        }

        public void DeleteLines(ILine[] lines)
        {
            int Count = lines == null ? 0 : lines.Length;
            if (Count == 0) return;
            for (int i = 0; i < Count; i++)
            {
                DeleteLine(lines[i]);
            }
        }

        public void DeleteLines(List<ILine> lines)
        {
            int Count = lines == null ? 0 : lines.Count;
            if (Count == 0) return;
            for (int i = 0; i < Count; i++)
            {
                DeleteLine(lines[i]);
            }
        }

        public void DeleteAll()
        {
            DeleteLines(lines);
        }
    }
}

Oh, by the way, Line recycling only recycles LineRenderer. I made a simple object pool LineRendererPool for LineRenderer. Do you
want to ask why LineRenderer is recycled instead of Line? Because I'm afraid that some cute little ones will still retain the reference to Line after the Line is recycled. Then, because I don't know, they will use this recycled reference to do some operations, which may affect the new Line created through the object pool. I'm worried. ah. . .

No, I can’t tell you how to use it. All right. Let me write about how to create a line! ! !
Look carefully, it's a big move. . . . . . . . . . . .
Line line = LineManager.instance.CreateLine<Line>();
Done.

Guess you like

Origin blog.csdn.net/weixin_42498461/article/details/130644382