最近项目要实现一个做正弦运动的线条,我想到的方案是改变模型的顶点,利用函数y=aSin(bx+C) 让模型顶点的Y坐标,根据X坐标运动,这样就实现如下图的运动:
废话不多说,是骡子是马拿出来遛一遛,来看一张效果图
好,开始说如何实现:首先新建一个物体,为物体加上必要的组件,加上这些才能显示出来,不要问我为啥子。
GameObject cube = this.gameObject;//or new GameObject();
MeshFilter meshFilter = cube.AddComponent<MeshFilter>();
MeshRenderer renderer = cube.AddComponent<MeshRenderer>();
renderer.material = Resources.Load<Material>("Material/line");
renderer.shadowCastingMode = ShadowCastingMode.Off;
mesh = new Mesh();
mesh.name = "LangCube";
下面就是头牌,对Mesh的创建了,创建模型网上也有很多的资料,其主要的难点在于,顶点的创建,和三角形的建立,这里我们创建一个长方体,这个长方体在X轴上有很多的顶点,这里分享一些我的理解,抛砖迎玉。首先我们以一个正方体为例来讲讲顶点的创建和三角形的构建,正方体有六个面每个面4个点,则一共就有24个顶点,有人肯定要说“为啥是24个,明明是8个点,你当我数学没学好啊!”,因为我们这里不考虑顶点的公用情况,这样能更方便处理构建顶点,构建三角形。当然你考虑顶点公用也是可行的,在构建三角形的时候,注意相应赋值也可以。这里就简单粗暴的处理。
三角形的构建
正方体每一面有2个三角形,每个三角形有3个顶点,则共用36个顶点,同理不考虑顶点的公用,Mesh的triangles是一个int数字,这个int数组的每个数就是对顶点的一个索引,每3个数构成一个三角形,所以这个数组的大小一定是3的倍数。当你构建三角形的时候,需要考虑三角形的正反面,即法线的朝向。这里有一个左手定则,四指的指向为顶点的顺序,大拇指的方向就是三角形法线的朝向,即0—>1—>2,3—>2—>1。
网上也有很多正方体的构建,也可以参考,弄明白了这个之后我们就来创建多顶点的长方体。
创建顶点,我这里采用的方式毕竟简单粗暴了,直接就是每个面的顶点进行列举。
Vector3[] vecs = new Vector3[4 + 2 * xNum * 4 + 4];
int index = 0;
for (int i = 0; i < xNum; i++) //前
{
vecs[i * 2] = new Vector3(-xNum / 4f + i * 0.5f, -0.5f, -0.5f);
vecs[i * 2 + 1] = new Vector3(-xNum / 4f + i * 0.5f, 0.5f, -0.5f);
}
index = xNum * 2;
for (int i = 0; i < xNum; i++) //上
{
vecs[index + i * 2] = new Vector3(-xNum / 4f + i * 0.5f, 0.5f, -0.5f);
vecs[index + i * 2 + 1] = new Vector3(-xNum / 4f + i * 0.5f, 0.5f, 0.5f);
}
index = xNum * 4;
for (int i = 0; i < xNum; i++)//后
{
vecs[index + i * 2] = new Vector3(-xNum / 4f + i * 0.5f, 0.5f, 0.5f);
vecs[index + i * 2 + 1] = new Vector3(-xNum / 4f + i * 0.5f, -0.5f, 0.5f);
}
index = xNum * 6;
for (int i = 0; i < xNum; i++)//下
{
vecs[index + i * 2] = new Vector3(-xNum / 4f + i * 0.5f, -0.5f, 0.5f);
vecs[index + i * 2 + 1] = new Vector3(-xNum / 4f + i * 0.5f, -0.5f, -0.5f);
}
index = xNum * 8;
vecs[index++] = vecs[1];//底
vecs[index++] = vecs[0];
vecs[index++] = vecs[4 * xNum];
vecs[index++] = vecs[4 * xNum + 1];
vecs[index++] = vecs[2 * xNum - 1];//顶
vecs[index++] = vecs[6 * xNum - 2];
vecs[index++] = vecs[2 * xNum - 2];
vecs[index++] = vecs[6 * xNum - 1];
贴个图解释下方向,这个更加清晰易懂:
只要你写了第一个面,后面的按着顺序来就是了。接下里就是构建三角形,同理按着顺序来
int triangles_cout = 2 * (xNum - 1) * 4 * 3 + 4 * 3;
int side_triangles_count = (xNum - 1) * 3 * 2;
int[] triangles = new int[triangles_cout];
for (int i = 0; i < 4; i++)
{
int vi = i * xNum * 2; //上一个面后2个顶点不与下一个面前两个顶点构成面,这里要注意。
for (int j = 0; j < side_triangles_count; vi += 2)
{
//两个三角形组成一个面
triangles[i * side_triangles_count + j++] = vi;
triangles[i * side_triangles_count + j++] = vi + 1;
triangles[i * side_triangles_count + j++] = vi + 2;
triangles[i * side_triangles_count + j++] = vi + 3;
triangles[i * side_triangles_count + j++] = vi + 2;
triangles[i * side_triangles_count + j++] = vi + 1;
}
}
//底面和顶面的三角形
for (int i = side_triangles_count * 4, vi = 8 * xNum; i < triangles_cout; i += 6, vi += 4)
{
triangles[i] = vi;
triangles[i + 1] = vi + 1;
triangles[i + 2] = vi + 2;
triangles[i + 3] = vi + 3;
triangles[i + 4] = vi + 2;
triangles[i + 5] = vi + 1;
}
把上面创建的赋值给mesh的vectors,triangles就完成了创建。
mesh.vertices = vecs;
mesh.triangles = triangles;
//最后要记得重新计算一下法线,避免法线不对引起光照不对等等问题
mesh.RecalculateNormals();
//更新包围盒 摄像需要获取bounds,如果不重新计算那么会存在边界时不被渲染的问题
mesh.RecalculateBounds();
接下来就就是如何将将模型做正弦运动了。
void MarginWaveEntityXY()
{
Vector3[] vertices = vects.Clone() as Vector3[];
for (int i = 0; i < vertices.Length; i++)
{
//遍历所有顶点,改变y的值,使其值跟随x的值变化。
vertices[i].y += Mathf.Sin(vertices[i].x + delta);
}
mesh.vertices = vertices;
}
在Update中改变delta的值就可以实现开始图中的效果了。。。
是不是还是很简单的。这里就不贴全部的代码了,相信各位看官们在阅读本文之后,要实现这效果也是手到擒来的事。各位如果有更好的方法请留言。。。