我们在创建可视UI组件时,可以通过继承Graphic来实现一些UI上的绘制功能。官方案例 中通过重写了 OnPopulateMesh 函数实现了一个绘制彩色四边形的效果。
利用该方法,结合一些对地形数据的处理,我们可以制作像下图这样的地形剖面图效果:
主要思路
Graphic 是UGUI的核心组件,附件画面的显示。关于其原理,可以参考大佬博客,这里我们只谈谈几个小细节与关键步骤。
首先, UIVertex是用来管理UI顶点的一个结构体,我们可以将我们计算所得的顶点的位置信息存在这个结构体中(它是以像素为单位的)。
其次,右上角的剖面图上显示的变化的线其实是由诸多小矩形组成。我们在计算好顶点位置,给UIVertex赋值完之后,便可调用VertexHelper.AddUIVertexQuad(UIVertexs) 来将一个矩形加入到渲染中。
最后,我们需要调用刷新函数,SetAllDirty() 是分别设置Layout布局、Vertices顶点和Material材质为Dirty。我们这里只需要调用SetVerticesDirty()即可(每次修改参数都要调用其刷新)。
这边提供一个顶点数据设置的函数实现,另外的坐标轴、刻度线、坐标轴箭头等的实现方式类似。
[Header("坐标轴显示属性")]
public float width = 200;
public float height = 200;
public float lineWidth = 5;
public Vector2 offset = new Vector2(2, 2);
[Header("坐标轴数据属性")]
public Vector2 axleMaxValue = new Vector2(100, 100);
public Vector2 axleMinValue = Vector3.zero;
public List<Vector2[]> datas = new List<Vector2[]>();
/// <summary>
/// 设置顶点属性
/// </summary>
private void SetData(VertexHelper vh)
{
foreach (var data in datas)
{
Vector2[] pixelPoints = new Vector2[data.Length];
for (int i = 0; i < data.Length; i++)
{
pixelPoints[i].x = (data[i].x - axleMinValue.x) / axleMaxValue.x * width;
pixelPoints[i].y = (data[i].y - axleMinValue.y) / axleMaxValue.y * height;
pixelPoints[i] += offset;
}
UIVertex[] verts = new UIVertex[4];
for (int i = 0; i < verts.Length; i++)
{
verts[i].color = Color.blue;
}
for (int i = 0; i < pixelPoints.Length - 1; i++)
{
SetVerts(pixelPoints[i], pixelPoints[i + 1], lineWidth, verts);
vh.AddUIVertexQuad(verts);
}
// 设置UI顶点数据
void SetVerts(Vector2 _start, Vector2 _end, float _width, UIVertex[] _verts)
{
Vector2[] tmp = GetRect(_start, _end, _width);
_verts[0].position = tmp[0];
_verts[1].position = tmp[1];
_verts[2].position = tmp[3];
_verts[3].position = tmp[2];
}
// 获取两点组成的矩形边框, 起始点,终止点,宽度
Vector2[] GetRect(Vector2 _start, Vector2 _end, float _width)
{
Vector2[] rect = new Vector2[4];
Vector2 dir = GetHorizontalDir(_end - _start); // 获取水平向右的向量
rect[0] = _start + dir * _width;
rect[1] = _start - dir * _width;
rect[2] = _end + dir * _width;
rect[3] = _end - dir * _width;
return rect;
}
}
}
至于数据的获取,我这边是利用两点间的距离和方向,每隔一小段距离取一个点,获取其在地表的高度。以距离和高度组成vector2数组作为参数赋值给datas。下面是个获取data的函数实现。
/// <summary>
/// 获取起点到终点的高度分析
/// </summary>
/// <param name="start">起点</param>
/// <param name="end">终点</param>
/// <returns>x为对应点到起点的距离,y为对应点的高度</returns>
public Vector2[] GetHeights(Vector3 start,Vector3 end)
{
float dis = Vector3.Distance(start, end);
Vector3 dir = (end - start).normalized;
int count = (int)dis / 5;
Vector2[] result = new Vector2[count + 1];
for (int i = 0; i < count; i++)
{
Vector3 pos = TerrainUtility.GetPositionOnTerrain(start + dir * i * 5);
result[i] = new Vector2(i * 5, pos.y);
}
result[count] = new Vector2(dis, TerrainUtility.GetPositionOnTerrain(end).y);
return result;
}
就酱。