Mesh, 即模型。一般情况下是不会在 Unity 里边来创建的,除非有特殊需求,比如说一个纯粹跟随玩家鼠标,表现一种 “东拉西扯” 效果等奇怪的功能。

动态改变 Mesh 暂且不谈,毕竟现在的主题主要是谈创建。那么创建一个什么样的 Mesh 呢?

很久以前,我记得见过一个叫 “白猫计划” 之类的日式网游,就是使用一个在屏幕上,可以动态跟随玩家手指进行移动、改变形状的圆圈,来操控角色进行移动的 —— 也就是说,他们把移动按钮都做了一个特殊效果。

如图:

《白猫计划》

我觉得那东西估计就是这么来的,所以这儿我就想仿照一下,做这么一个东西来试试。

首先,新建一个场景,确定一下坐标。因为毕竟这个 “圆” 相对屏幕来说,只需要一个片而已,所以我直接建立了一个 2D 的场景。然后新建一个空物体,默认坐标就在 (0,0,0) 点 —— 也不需要去改它,只要确定处于相机范围内就可以了。另外坐标则是 Y 轴向上,X 轴向右,Z 轴面向屏幕里边,因为是 2D 场景,所以在这儿 Z 轴基本上就可以忽略了(不过当然不能完全忽略,对我们创建的模型还是有一定影响的,后面会解释)。

新场景

然后,作为一个圆,那么我们还必须确定这个圆,究竟需要有多少个顶点?不过实际上这个问题是没什么定论的,从理论上来说,只要你能让这些顶点围成一个圈,那么就足够了 (当然如果非得用几十个顶点画,那么跟几百个顶点画的,视觉上完全不是一个档次)。因为一般来说,一个圆有 360 度,随意这儿我就直接选用 361 个顶点了。

为什么是 361 个顶点呢?想一想,在 Unity 中,构成多边形的基础是什么?—— 三角形。那么就必须得三个顶点才能构成一个三角形,如果是 360 个顶点的话,怎么能把这个圆分成完全密封的多个三角形呢?

所以多出来的那一个顶点,是作为这个圆的原圆心存在,起一个链接各个边上顶点的作用。如图:

圆

我的计划就是:首先从中心点到 Y 轴正上方的一条边,作为这个圆的起始点,然后每隔一度 (因为这儿 360 个顶点差不多刚好是 360 度),将这条边旋转一度,这样,每次旋转后,都会获得下一个顶点的位置。如图所示:

边的旋转

最终转过 360 度,从理论上来说,就会形成一个完美的圆了。

Ok,原理就是这样,那么接下来是代码。

首先,定义一个半径,以控制圆的大小:

s
public float m_radius = 5f;

然后是 MeshRender、MeshFileter 两个 Unity 渲染 Mesh 必备组件,以及盛放顶点、三角形索引的列表 (这些算是最低的要求了)、材质:

s
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 材质丢失:

s
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 的关键代码了:

s
// 在这儿,首先创建一个新的 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 度换过来都可以解决。

不过我采用的方法是:反向绘制三角形。

回到代码,将设置三角形索引的循环稍微改一下:

s
for (int i = 361; i > 0; i--)
        {
            m_triangles.Add(0);
            m_triangles.Add(i);
            m_triangles.Add(i - 1);
        }

这样的话,就是反向绘制了。

然后回到场景,再次运行,就可以顺利看见了。如图:

最后的样子

最后,本来打算将动态改变顶点,做点好玩的效果出来的方法也一并写一写的,结果发现就写这么些东西,时间又是 10 点多了..... 现在太晚了,决定还是下次吧。

就是这样。