思路-绘制空心矩形
拓展UGUI的Graphic类,实现拖拽接口。
开始拖拽时记录鼠标位置,
使用拖拽中的鼠标位置和记录的位置,计算矩形顶点,绘制矩形。
两个三角形合并为一个矩形,作为空心矩形的一条边,四个边合并为空心矩形。
示例-绘制空心矩形
csharp
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class TestDraw : Graphic, IBeginDragHandler, IDragHandler, IEndDragHandler
{
UIVertex[] rectangle1 = new UIVertex[4];
UIVertex[] rectangle2 = new UIVertex[4];
UIVertex[] rectangle3 = new UIVertex[4];
UIVertex[] rectangle4 = new UIVertex[4];
[SerializeField] float width = 5f;
Vector3 lastPoint;
protected override void Awake()
{
Init(rectangle1);
Init(rectangle2);
Init(rectangle3);
Init(rectangle4);
void Init(UIVertex[] uIVertices)
{
var length = uIVertices.Length;
for (int i = 0; i < length; i++)
uIVertices[i] = new UIVertex();
}
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
vh.AddUIVertexQuad(rectangle1);
vh.AddUIVertexQuad(rectangle2);
vh.AddUIVertexQuad(rectangle3);
vh.AddUIVertexQuad(rectangle4);
}
public void ClearDraw()
{
Clear(rectangle1);
Clear(rectangle2);
Clear(rectangle3);
Clear(rectangle4);
void Clear(UIVertex[] uIVertices)
{
var length = uIVertices.Length;
for (int i = 0; i < length; i++)
uIVertices[i].position = Vector3.zero;
}
SetVerticesDirty();
}
public void OnBeginDrag(PointerEventData eventData)
{
lastPoint = ScreenPointToLocalPoint(rectTransform, eventData.position);
}
public void OnDrag(PointerEventData eventData)
{
Vector3 point = ScreenPointToLocalPoint(rectTransform, eventData.position);
if (lastPoint.x < point.x && lastPoint.y < point.y)//起点在左下角
{
//水平方向投影向量
var horizontalNormal = Vector3.Project(point - lastPoint, Vector3.right);
SetRectangleVertex(point - horizontalNormal, point, lastPoint + horizontalNormal, lastPoint);
SetRectangleColor(color);
SetVerticesDirty();
}
else if (lastPoint.x > point.x && lastPoint.y > point.y)//起点在右上角
{
var horizontalNormal = Vector3.Project(point - lastPoint, Vector3.right);
SetRectangleVertex(lastPoint + horizontalNormal, lastPoint, point - horizontalNormal, point);
SetRectangleColor(color);
SetVerticesDirty();
}
else if (lastPoint.x > point.x && lastPoint.y < point.y)//起点在右下角
{
var horizontalNormal = Vector3.Project(point - lastPoint, Vector3.right);
SetRectangleVertex(point, point - horizontalNormal, lastPoint, lastPoint + horizontalNormal);
SetRectangleColor(color);
SetVerticesDirty();
}
else if (lastPoint.x < point.x && lastPoint.y > point.y)//起点在左上角
{
var horizontalNormal = Vector3.Project(point - lastPoint, Vector3.right);
SetRectangleVertex(lastPoint, lastPoint + horizontalNormal, point, point - horizontalNormal);
SetRectangleColor(color);
SetVerticesDirty();
}
}
public void OnEndDrag(PointerEventData eventData)
{
lastPoint = Vector3.zero;
}
void SetRectangleVertex(Vector3 topLeftUp, Vector3 topRigthUp, Vector3 bottomRightDown, Vector3 bottomLeftDown)
{
//top矩形
rectangle1[0].position = topLeftUp;
rectangle1[1].position = topRigthUp;
rectangle1[2].position = topRigthUp - Vector3.up * width;
rectangle1[3].position = topLeftUp - Vector3.up * width;
//bottom矩形
rectangle2[0].position = bottomRightDown;
rectangle2[1].position = bottomLeftDown;
rectangle2[2].position = bottomLeftDown + Vector3.up * width;
rectangle2[3].position = bottomRightDown + Vector3.up * width;
//left矩形
rectangle3[0].position = topLeftUp - Vector3.up * width;
rectangle3[1].position = topLeftUp - Vector3.up * width + Vector3.right * width;
rectangle3[2].position = bottomLeftDown + Vector3.up * width + Vector3.right * width;
rectangle3[3].position = bottomLeftDown + Vector3.up * width;
//right矩形
rectangle4[0].position = topRigthUp - Vector3.up * width;
rectangle4[1].position = bottomRightDown + Vector3.up * width;
rectangle4[2].position = bottomRightDown + Vector3.up * width - Vector3.right * width;
rectangle4[3].position = topRigthUp - Vector3.up * width - Vector3.right * width;
}
void SetRectangleColor(Color color)
{
var length = rectangle1.Length;
for (int i = 0; i < length; i++)
rectangle1[i].color = color;
length = rectangle2.Length;
for (int i = 0; i < length; i++)
rectangle2[i].color = color;
length = rectangle3.Length;
for (int i = 0; i < length; i++)
rectangle3[i].color = color;
length = rectangle4.Length;
for (int i = 0; i < length; i++)
rectangle4[i].color = color;
}
Vector2 ScreenPointToLocalPoint(RectTransform rect, Vector2 mousePoint)
{
Vector2 result = Vector2.zero;
switch (canvas.renderMode)
{
case RenderMode.ScreenSpaceOverlay:
RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, mousePoint, null, out result);
break;
case RenderMode.ScreenSpaceCamera:
RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, mousePoint, canvas.worldCamera, out result);
break;
case RenderMode.WorldSpace:
RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, mousePoint, canvas.worldCamera, out result);
break;
}
return result;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
ClearDraw();
}
}
场景结构

TestDraw对象挂载TestDraw脚本,该对象需要CanvsRenderer脚本,大小为屏幕大小
运行结果

上方为矩形网格
运行,拖拽鼠标显示矩形。
按下空格键,清除矩形。
知识点
三角形的绘制需要三个顶点,三角形使用顺时针的顺序进行绘制。
其他几何图形的绘制思路
线:多个矩形组合而成
箭头:一个三角形,一个矩形组合而成。
空心椭圆:获取两个圆上的顶点,按照顺序绘制三角形。
椭圆绘制思路:已知圆心,半径
将圆划分多份,利用三角函数和弧度获取x坐标,y坐标。
将x,y坐标缩放,可得到椭圆上的点。