Mesh, 即模型。一般情况下是不会在 Unity 里边来创建的,除非有特殊需求,比如说一个纯粹跟随玩家鼠标,表现一种 “东拉西扯” 效果等奇怪的功能。
动态改变 Mesh 暂且不谈,毕竟现在的主题主要是谈创建。那么创建一个什么样的 Mesh 呢?
很久以前,我记得见过一个叫 “白猫计划” 之类的日式网游,就是使用一个在屏幕上,可以动态跟随玩家手指进行移动、改变形状的圆圈,来操控角色进行移动的 —— 也就是说,他们把移动按钮都做了一个特殊效果。
如图:
我觉得那东西估计就是这么来的,所以这儿我就想仿照一下,做这么一个东西来试试。
首先,新建一个场景,确定一下坐标。因为毕竟这个 “圆” 相对屏幕来说,只需要一个片而已,所以我直接建立了一个 2D 的场景。然后新建一个空物体,默认坐标就在 (0,0,0) 点 —— 也不需要去改它,只要确定处于相机范围内就可以了。另外坐标则是 Y 轴向上,X 轴向右,Z 轴面向屏幕里边,因为是 2D 场景,所以在这儿 Z 轴基本上就可以忽略了(不过当然不能完全忽略,对我们创建的模型还是有一定影响的,后面会解释)。
然后,作为一个圆,那么我们还必须确定这个圆,究竟需要有多少个顶点?不过实际上这个问题是没什么定论的,从理论上来说,只要你能让这些顶点围成一个圈,那么就足够了 (当然如果非得用几十个顶点画,那么跟几百个顶点画的,视觉上完全不是一个档次)。因为一般来说,一个圆有 360 度,随意这儿我就直接选用 361 个顶点了。
为什么是 361 个顶点呢?想一想,在 Unity 中,构成多边形的基础是什么?—— 三角形。那么就必须得三个顶点才能构成一个三角形,如果是 360 个顶点的话,怎么能把这个圆分成完全密封的多个三角形呢?
所以多出来的那一个顶点,是作为这个圆的原圆心存在,起一个链接各个边上顶点的作用。如图:
我的计划就是:首先从中心点到 Y 轴正上方的一条边,作为这个圆的起始点,然后每隔一度 (因为这儿 360 个顶点差不多刚好是 360 度),将这条边旋转一度,这样,每次旋转后,都会获得下一个顶点的位置。如图所示:
最终转过 360 度,从理论上来说,就会形成一个完美的圆了。
Ok,原理就是这样,那么接下来是代码。
首先,定义一个半径,以控制圆的大小:
public float m_radius = 5f; |
然后是 MeshRender、MeshFileter 两个 Unity 渲染 Mesh 必备组件,以及盛放顶点、三角形索引的列表 (这些算是最低的要求了)、材质:
private MeshRenderer m_renderer; | |
private MeshFilter m_meshFilter; | |
public Material m_material; | |
private List<Vector3> m_vertexList = new List<Vector3>(); | |
private List<int> m_triangles = new List<int>(); |
然后,在 Start 初始化中,首先判断或者初始化 Meshrender 及 MeshFileter, 并赋予相应的材质,避免创建出来的 Mesh 材质丢失:
m_renderer = GetComponent<MeshRenderer>(); | |
if (!m_renderer) | |
m_renderer = gameObject.AddComponent<MeshRenderer>(); | |
m_renderer.material = m_material; | |
m_meshFilter = GetComponent<MeshFilter>(); | |
if (!m_meshFilter) | |
m_meshFilter = gameObject.AddComponent<MeshFilter>(); |
接下来,就是创建 Mesh 的关键代码了:
// 在这儿,首先创建一个新的 Mesh,并将其赋给 MeshFilter 组件 | |
m_mesh = new Mesh(); | |
m_mesh.name = "Button"; | |
m_meshFilter.mesh = m_mesh; | |
// 初始化圆心 | |
m_vertexList.Add(transform.position); | |
// 从正上方,以 Z 轴旋转 360 度,构造 360 个正确位置的顶点 | |
for (int i = 0; i < 361; i++) | |
{ | |
// 这儿我们构造了一个四元数进行旋转 | |
m_vertexList.Add(transform.position + Quaternion.Euler(0, 0, i) * Vector3.up * m_radius); | |
} | |
// 创建三角形索引 | |
for (int i = 1; i < 361; i++) | |
{ | |
m_triangles.Add(0); | |
m_triangles.Add(i); | |
m_triangles.Add(i + 1); | |
} | |
// 将顶点及三角形索引赋给 Mesh,构造完成 | |
m_mesh.SetVertices(m_vertexList); | |
m_mesh.SetTriangles(m_triangles, 0); |
就是这样,接下来切入 Unity,点击运行。
不过运行之后,发现似乎屏幕上啥都没有啊?
实际上,不是什么都没有,这个圆早已创建出来了,不信的话,可以进入 Scene 场景,点击左上方的 “2D” 关闭 2D 模式,朝着相机的前方移动一点,就会发现,这个圆确实是出来了,如图:
不过,视角一换回相机前方,这个圆就消失了?
为什么?
实际上,这是因为我们绘制的模型只有一个片,我们按照顺时针旋转来绘制三角形,所以自然而然,那么也就只有一个 “正面”,即面向模型自身的 Z 轴正方向的面了。而 Mesh 的背面,是不会渲染出来的。那么怎么办呢?解决的办法很多,比如换双面 Shader 啊、或者干脆把模型转个 180 度换过来都可以解决。
不过我采用的方法是:反向绘制三角形。
回到代码,将设置三角形索引的循环稍微改一下:
for (int i = 361; i > 0; i--) | |
{ | |
m_triangles.Add(0); | |
m_triangles.Add(i); | |
m_triangles.Add(i - 1); | |
} |
这样的话,就是反向绘制了。
然后回到场景,再次运行,就可以顺利看见了。如图:
最后,本来打算将动态改变顶点,做点好玩的效果出来的方法也一并写一写的,结果发现就写这么些东西,时间又是 10 点多了..... 现在太晚了,决定还是下次吧。
就是这样。