在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>();
完事。