一条线的故事(基于LineRenderer的绘制封装)

在Unity开发中使用LineRenderer绘制线段的一般步骤:
1.创建一个物体
2.添加组件LineRenderer
3.写脚本指定LineRenderer在通过指定的LineRenderer来绘制线段
其实如果只花一条线,好像也没有那么复杂哈。但是如果我有一个需求需要画100条,这种操作就太变态了吧。。。
也许有聪明的同学会说,那我可以做成预制体啊,每个预制体上就有一个画线的对象。这样显然比上面那种方法要简便了太多太多。
但是问题也来了,如果我还需要管理这些线段的增删改查,是不是还要在写一个Manager来管理所有的线段。
干脆一点,直接我来写一个LineManager来管理工程中的所有画线功能吧。。。

从Line对象开始吧。首先我们要想,一个最基本的线段管理要具备哪些方法或属性呢。。。
别想了,我来抛砖引玉吧,看下面的ILine接口,当然如果你们感觉还可以加其他的属性或方法也可以哦,自己试着实现吧。。。

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();
    }
}

是不是和LineRenderer中的属性和方法差不多。是的,因为LineRenderer的方法和属性已经很全面了,这里比LineRenerer 多的方法也就AddPosition、RemovePosition、Clear、Dispose。其它的没有LineRenderer全面,如果后期有需要可以向接口中添加属性和方法,这里只是把最常用的给罗列出来。

那么我们把线段的接口给写出来了,下面开始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();
    }
}

对的,目前LineManager的功能确实是比较少的,基本上都是用来处理创建和删除的,哦!还有一个计数lineCount,后期开发过程中,如果需要更复杂的管理功能,可以在慢慢加嘛。

那么现在我们把线段和管理者的模板已经给写出来了,接下来就是实现这两个接口,然后就大功告成了。

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);
        }
    }
}

哦 对了,Line的回收只回收了LineRenderer 做了一个LineRenderer的简单的对象池LineRendererPool
是不是想问为什么回收LineRenderer而不是回收Line? 因为 我怕一些小可爱在Line回收之后还保留着Line的引用,然后因为不知道,再用这个已经回收的引用,做一些操作,有可能会影响通过对象池创建的新Line,操碎了心啊。。。

不会吧,不会还让写怎么用吧。 好吧。 我还是写一写怎么创建一条线吧!!!
看仔细了,发大招了。。。。。。。。。。。。
Line line = LineManager.instance.CreateLine<Line>();
完事。

猜你喜欢

转载自blog.csdn.net/weixin_42498461/article/details/130644382