通过重写OnPopulateMesh方法,自定义顶点信息,实现曲线绘制,具体实现思路如下图所示:
画一条线段,a 为起始点,b为终点,方向如蓝色箭头所示,由a指向b,黄色矩形框表示最终绘制的线段宽度,线段最终由四个顶点决定,这里分别记为a左、a右,b左,b右。
想要计算线段的顶点坐标需要借助于法线方向,使向量ab与 vector3.forward形成一个平面,则 向量(a左,a右) 垂直于此平面,而 向量(a左,a右)的长度为曲线的宽度,设宽度为W,
则有:
Vector3 normal = Vector3.Cross( (b-a).normalized,Vector3.forward).normalized;
a左 = a + normal*w/2;
a右 = a - normal*w/2;
b左 = b + normal*w/2;
b右 = b - normal*w/2;
一、实现基本绘制逻辑,使用鼠标全屏绘制
1、新建image,大小设置为全屏填充
2、新建脚本 DrawCurveByMouse,脚本内容如下文章来源:https://www.toymoban.com/news/detail-770261.html
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DrawCurveByMouse : MaskableGraphic
{
/// <summary>
/// 存储整条线的所有顶点信息
/// </summary>
private List<UIVertex[]> vertexQuadList = new List<UIVertex[]>();
protected override void OnPopulateMesh(VertexHelper vh)
{
base.OnPopulateMesh(vh);
vh.Clear();
for (int i = 0; i < vertexQuadList.Count; i++)
{
vh.AddUIVertexQuad(vertexQuadList[i]);
}
}
/// <summary>
/// 记录上一个点的坐标信息
/// </summary>
private Vector3 lastPoint;
private Vector3 lastLeft;
private Vector3 lastRight;
/// <summary>
/// 宽度
/// </summary>
public float halfWidth;
public float smoothStep = 10;
/// <summary>
/// 是否为新曲线
/// </summary>
private bool isNewLine = false;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
isNewLine = true;
lastPoint = ScreenPointToLocalPointInRectangle(Input.mousePosition);
}
if (Input.GetMouseButton(0))
{
DrawLine(ScreenPointToLocalPointInRectangle(Input.mousePosition));
}
}
/// <summary>
/// 屏幕左边转换为矩形框中的本地坐标
/// </summary>
private Vector3 ScreenPointToLocalPointInRectangle(Vector2 screenPoint)
{
Vector2 localPoint = Vector2.zero;
switch (canvas.renderMode)
{
case RenderMode.WorldSpace:
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, canvas.worldCamera,
out localPoint);
break;
case RenderMode.ScreenSpaceCamera:
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, canvas.worldCamera,
out localPoint);
break;
case RenderMode.ScreenSpaceOverlay:
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, null,
out localPoint);
break;
}
return localPoint;
}
private void DrawLine(Vector3 curr)
{
Vector3 dir = curr - lastPoint;
if (dir.magnitude < smoothStep) return;
Vector3 normal = Vector3.Cross(dir.normalized, Vector3.forward).normalized;
if (isNewLine)
{
lastLeft = lastPoint + normal * halfWidth;
lastRight = lastPoint - normal * halfWidth;
isNewLine = false;
}
Vector3 currLeft = curr + normal * halfWidth;
Vector3 currRight = curr - normal * halfWidth;
// Vector3 currLeft = curr + dir.normalized* width;
// Vector3 currRight = curr - dir.normalized* width;
UIVertex[] quad = new UIVertex[4];
quad[0] = new UIVertex();
quad[0].position = lastLeft;
quad[0].color = color;
quad[1] = new UIVertex();
quad[1].position = lastRight;
quad[1].color = color;
quad[2] = new UIVertex();
quad[2].position = currRight;
quad[2].color = color;
quad[3] = new UIVertex();
quad[3].position = currLeft;
quad[3].color = color;
vertexQuadList.Add(quad);
lastLeft = currLeft;
lastRight = currRight;
SetAllDirty();
}
}
文章来源地址https://www.toymoban.com/news/detail-770261.html
二、将绘制范围限制到矩形中,仍使用鼠标绘制
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// 在矩形框中绘制曲线
/// </summary>
public class DrawCurveInRect : MaskableGraphic, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler,
IPointerExitHandler
{
/// <summary>
/// 存储整条线的所有顶点信息
/// </summary>
private List<UIVertex[]> vertexQuadList = new List<UIVertex[]>();
protected override void OnPopulateMesh(VertexHelper vh)
{
base.OnPopulateMesh(vh);
vh.Clear();
for (int i = 0; i < vertexQuadList.Count; i++)
{
vh.AddUIVertexQuad(vertexQuadList[i]);
}
}
/// <summary>
/// 记录上一个点的坐标信息
/// </summary>
private Vector3 lastPoint;
private Vector3 lastLeft;
private Vector3 lastRight;
/// <summary>
/// 宽度
/// </summary>
public float halfWidth = 2;
public float smoothStep = 10;
/// <summary>
/// 是否为新曲线
/// </summary>
private bool isNewLine = false;
private bool isEnter = false;
private bool isDrawing = false;
private void Update()
{
if (isEnter && isDrawing)
{
DrawLine(ScreenPointToLocalPointInRectangle(Input.mousePosition));
}
}
/// <summary>
/// 屏幕左边转换为矩形框中的本地坐标
/// </summary>
private Vector3 ScreenPointToLocalPointInRectangle(Vector2 screenPoint)
{
Vector2 localPoint = Vector2.zero;
switch (canvas.renderMode)
{
case RenderMode.WorldSpace:
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, canvas.worldCamera,
out localPoint);
break;
case RenderMode.ScreenSpaceCamera:
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, canvas.worldCamera,
out localPoint);
break;
case RenderMode.ScreenSpaceOverlay:
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, null,
out localPoint);
break;
}
return localPoint;
}
private void DrawLine(Vector3 curr)
{
Vector3 dir = curr - lastPoint;
if (dir.magnitude < smoothStep) return;
Vector3 normal = Vector3.Cross(dir.normalized, Vector3.forward).normalized;
if (isNewLine)
{
lastLeft = lastPoint + normal * halfWidth;
lastRight = lastPoint - normal * halfWidth;
isNewLine = false;
}
Vector3 currLeft = curr + normal * halfWidth;
Vector3 currRight = curr - normal * halfWidth;
// Vector3 currLeft = curr + dir.normalized* width;
// Vector3 currRight = curr - dir.normalized* width;
UIVertex[] quad = new UIVertex[4];
quad[0] = new UIVertex();
quad[0].position = lastLeft;
quad[0].color = color;
quad[1] = new UIVertex();
quad[1].position = lastRight;
quad[1].color = color;
quad[2] = new UIVertex();
quad[2].position = currRight;
quad[2].color = color;
quad[3] = new UIVertex();
quad[3].position = currLeft;
quad[3].color = color;
vertexQuadList.Add(quad);
lastLeft = currLeft;
lastRight = currRight;
SetAllDirty();
}
public void OnPointerDown(PointerEventData eventData)
{
isNewLine = true;
isDrawing = true;
lastPoint = ScreenPointToLocalPointInRectangle(eventData.position);
}
public void OnPointerUp(PointerEventData eventData)
{
isDrawing = false;
}
public void OnPointerEnter(PointerEventData eventData)
{
isEnter = true;
}
public void OnPointerExit(PointerEventData eventData)
{
isEnter = false;
isDrawing = false;
}
}
三、通过绘制函数曲线
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;
/// <summary>
/// 通过函数绘制曲线
/// </summary>
public class FunctionCurve : MaskableGraphic
{
/// <summary>
/// 存储整条线的所有顶点信息
/// </summary>
private List<UIVertex[]> curveQuads = new List<UIVertex[]>();
[Header("X轴的取值范围,")] public Vector2 rangeX;
[Header("Y轴的取值范围,")] public Vector2 rangeY;
/// <summary>
/// 宽度
/// </summary>
public float halfWidth = 2;
[Header("控制曲线平滑度的步长")] public float smoothStep;
private List<UIVertex[]> pointQuads = new List<UIVertex[]>();
[Header("取样点的颜色")] public Color simpleColor;
[Header("取样点在轴上的值")] public float[] simples;
/// <summary>
/// 宽度
/// </summary>
public float simpleHalfWidth = 2;
protected override void OnPopulateMesh(VertexHelper vh)
{
base.OnPopulateMesh(vh);
vh.Clear();
for (int i = 0; i < curveQuads.Count; i++)
{
vh.AddUIVertexQuad(curveQuads[i]);
}
for (int i = 0; i < pointQuads.Count; i++)
{
vh.AddUIVertexQuad(pointQuads[i]);
}
}
public List<UIVertex[]> CreatCurve()
{
List<UIVertex[]> vertexQuadList = new List<UIVertex[]>();
//1、通过步长计算采样点数
//限制步长,步长应大于单个像素代表的数值;
float minStep = (rangeX.y - rangeX.x) / rectTransform.rect.width; //最小步长
if (smoothStep < minStep)
{
smoothStep = minStep;
}
int count = (int)((rangeX.y - rangeX.x) / smoothStep); //采样数
List<Vector2> points = new List<Vector2>();
float x = 0;
float y = 0;
float x_rate = rectTransform.rect.width / (rangeX.y - rangeX.x);
float y_rate = rectTransform.rect.height / (rangeY.y - rangeY.x);
for (int i = 0; i < count; i++)
{
//2、计算每个采样点的值
x = rangeX.x + smoothStep * i;
y = GetY(x);
//3、将采样点数值转换为坐标值
points.Add(new Vector2(x * x_rate, y * y_rate));
}
for (int i = 1; i < count; i++)
{
if (points[i].y > rectTransform.rect.height) break;
vertexQuadList.Add(GetQuad(points[i - 1], points[i]));
}
return vertexQuadList;
}
public List<UIVertex[]> CreatSimplePoints()
{
List<UIVertex[]> vertexQuadList = new List<UIVertex[]>();
float x_rate = rectTransform.rect.width / (rangeX.y - rangeX.x);
float y_rate = rectTransform.rect.height / (rangeY.y - rangeY.x);
for (int i = 0; i < simples.Length; i++)
{
if (simples[i] >= rangeX.x && simples[i] <= rangeX.y)
{
vertexQuadList.Add(GetSimplePointQuad(new Vector2(simples[i] * x_rate, GetY(simples[i]) * y_rate)));
}
}
return vertexQuadList;
}
private UIVertex[] GetQuad(Vector3 lastPoint, Vector3 curr)
{
Vector3 normal = Vector3.Cross((curr - lastPoint).normalized, Vector3.forward).normalized;
Vector3 lastLeft = lastPoint + normal * halfWidth;
Vector3 lastRight = lastPoint - normal * halfWidth;
Vector3 currLeft = curr + normal * halfWidth;
Vector3 currRight = curr - normal * halfWidth;
UIVertex[] quad = new UIVertex[4];
quad[0] = new UIVertex();
quad[0].position = lastLeft;
quad[0].color = color;
quad[1] = new UIVertex();
quad[1].position = lastRight;
quad[1].color = color;
quad[2] = new UIVertex();
quad[2].position = currRight;
quad[2].color = color;
quad[3] = new UIVertex();
quad[3].position = currLeft;
quad[3].color = color;
return quad;
}
private UIVertex[] GetSimplePointQuad(Vector3 point)
{
UIVertex[] quad = new UIVertex[4];
quad[0] = new UIVertex();
quad[0].position = point + new Vector3(-simpleHalfWidth, simpleHalfWidth);;
quad[0].color = simpleColor;
quad[1] = new UIVertex();
quad[1].position = point + new Vector3(simpleHalfWidth, simpleHalfWidth);;
quad[1].color = simpleColor;
quad[2] = new UIVertex();
quad[2].position = point + new Vector3(simpleHalfWidth, -simpleHalfWidth);;
quad[2].color = simpleColor;
quad[3] = new UIVertex();
quad[3].position = point + new Vector3(-simpleHalfWidth, -simpleHalfWidth);;
quad[3].color = simpleColor;
return quad;
}
public float GetY(float x)
{
return Mathf.Pow(x, 2);
return x;
}
[ContextMenu("Test")]
public void Test()
{
curveQuads = CreatCurve();
pointQuads = CreatSimplePoints();
SetAllDirty();
}
}
到了这里,关于unity在UGUI界面上绘制曲线的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!