如何使用Unity创造动态的2D水体效果
1个回答
2015-04-13 · 知道合伙人数码行家
huanglenzhi
知道合伙人数码行家
向TA提问 私信TA
知道合伙人数码行家
采纳数:117538
获赞数:517174
长期从事计算机组装,维护,网络组建及管理。对计算机硬件、操作系统安装、典型网络设备具有详细认知。
向TA提问 私信TA
关注
展开全部
作者:Alex Rose
在本篇教程中,我们将使用简单的物理机制模拟一个动态的2D水体。我们将使用一个线性渲染器、网格渲染器,触发器以及粒子的混合体来创造这一水体效果,最终得到可运用于你下款游戏的水纹和水花。这里包含了Unity样本源,但你应该能够使用任何游戏引擎以相同的原理执行类似的操作。
设置水体管理器
我们将使用Unity的一个线性渲染器来渲染我们的水体表面,并使用这些节点来展现持续的波纹。
登录/注册后可看大图
uYfYBv.jpg (8.81 KB, 下载次数: 32)
下载附件 保存到相册
2014-4-7 09:44 上传
unity-water-linerenderer(from gamedevelopment)
我们将追踪每个节点的位置、速度和加速情况。为此,我们将会使用到阵列。所以在我们的类顶端将添加如下变量:
float[] xpositions;
float[] ypositions;
float[] velocities;
float[] accelerations;
LineRenderer Body;
LineRenderer将存储我们所有的节点,并概述我们的水体。我们仍需要水体本身,将使用Meshes来创造。我们将需要对象来托管这些网格。
GameObject[] meshobjects;
Mesh[] meshes;
我们还需要碰撞器以便事物可同水体互动:
GameObject[] colliders;
我们也存储了所有的常量:
const float springconstant = 0.02f;
const float damping = 0.04f;
const float spread = 0.05f;
const float z = -1f;
这些常量中的z是我们为水体设置的Z位移。我们将使用-1标注它,这样它就会呈现于我们的对象之前(游戏邦注:你可能想根据自己的需求将其调整为在对象之前或之后,那你就必须使用Z坐标来确定与之相关的精灵所在的位置)。
下一步,我们将保持一些值:
float baseheight;
float left;
float bottom;
这些就是水的维度。
我们将需要一些可以在编辑器中设置的公开变量。首先,我们将为水花使用粒子系统:
public GameObject splash:
接下来就是我们将用于线性渲染器的材料:
public Material mat:
此外,我们将为主要水体使用的网格类型如下:
public GameObject watermesh:
我们想要能够托管所有这些数据的游戏对象,令其作为管理器,产出我们游戏中的水体。为此,我们将编写SpawnWater()函数。
这个函数将采用水体左边、跑马度、顶点以及底部的输入:
public void SpawnWater(float Left, float Width, float Top, float Bottom)
{
(虽然这看似有所矛盾,但却有利于从左往右快速进行关卡设计)
创造节点
现在我们将找出自己需要多少节点:
int edgecount = Mathf.RoundToInt(Width) * 5;
int nodecount = edgecount + 1;
我们将针对每个单位宽度使用5个节点,以便呈现流畅的移动(你可以改变这一点以便平衡效率与流畅性)。我们由此可得到所有线段,然后需要在末端的节点 + 1。
我们要做的首件事就是以LineRenderer组件渲染水体:
Body = gameObject.AddComponent();
Body.material = mat;
Body.material.renderQueue = 1000;
Body.SetVertexCount(nodecount);
Body.SetWidth(0.1f, 0.1f);
我们在此还要做的是选择材料,并通过选择渲染队列中的位置而令其在水面之上渲染。我们设置正确的节点数据,将线段宽度设为0.1。
你可以根据自己所需的线段粗细来改变这一宽度。你可能注意到了SetWidth()需要两个参数,这是线段开始及末尾的宽度。我们希望该宽度恒定不变。
现在我们制作了节点,将初始化我们所有的顶级变量:
xpositions = new float[nodecount];
ypositions = new float[nodecount];
velocities = new float[nodecount];
accelerations = new float[nodecount];
meshobjects = new GameObject[edgecount];
meshes = new Mesh[edgecount];
colliders = new GameObject[edgecount];
baseheight = Top;
bottom = Bottom;
left = Left;
我们已经有了所有阵列,将控制我们的数据。
现在要设置我们阵列的值。我们将从节点开始:
for (int i = 0; i < nodecount; i++)
{
ypositions = Top;
xpositions = Left + Width * i / edgecount;
accelerations = 0;
velocities = 0;
Body.SetPosition(i, new Vector3(xpositions, ypositions, z));
}
在此,我们将所有Y位置设于水体之上,之后一起渐进增加所有节点。因为水面平静,我们的速度和加速值最初为0。
我们将把LineRenderer (Body)中的每个节点设为其正确的位置,以此完成这个循环。
创造网格
这正是它棘手的地方。
我们有自己的线段,但我们并没有水体本身。我们要使用网格来制作,如下所示:
for (int i = 0; i < edgecount; i++)
{
meshes = new Mesh();
现在,网格存储了一系列变量。首个变量相当简单:它包含了所有顶点(或转角)。
登录/注册后可看大图
uYfYBv.jpg (13.63 KB, 下载次数: 31)
下载附件 保存到相册
2014-4-7 09:44 上传
unity-water-Firstmesh(from gamedevelopment)
该图表显示了我们所需的网格片段的样子。第一个片段中的顶点被标注出来了。我们总共需要4个顶点。
Vector3[] Vertices = new Vector3[4];
Vertices[0] = new Vector3(xpositions, ypositions, z);
Vertices[1] = new Vector3(xpositions[i + 1], ypositions[i + 1], z);
Vertices[2] = new Vector3(xpositions, bottom, z);
Vertices[3] = new Vector3(xpositions[i+1], bottom, z);
现在如你所见,顶点0处于左上角,1处于右上角,2是左下角,3是右下角。我们之后要记住。
网格所需的第二个性能就是UV。网格拥有纹理,UV会选择我们想撷取的那部分纹理。在这种情况下,我们只想要左上角,右上角,右下角和右下角的纹理。
Vector2[] UVs = new Vector2[4];
UVs[0] = new Vector2(0, 1);
UVs[1] = new Vector2(1, 1);
UVs[2] = new Vector2(0, 0);
UVs[3] = new Vector2(1, 0);
现在我们又需要这些数据了。网格是由三角形组成的,我们知道任何四边形都是由两个三角形组成的,所以现在我们需要告诉网格它如何绘制这些三角形。
登录/注册后可看大图
uYfYBv.jpg (17.27 KB, 下载次数: 33)
下载附件 保存到相册
2014-4-7 09:44 上传
unity-water-Tris(from gamedevelopment)
看看含有节点顺序标注的转角。三角形A连接节点0,1,以及3,三角形B连接节点3,2,1。因此我们想制作一个包含6个整数的阵列:
int[] tris = new int[6] { 0, 1, 3, 3, 2, 0 };
这就创造了我们的四边形。现在我们要设置网格的值。
meshes.vertices = Vertices;
meshes.uv = UVs;
meshes.triangles = tris;
现在我们已经有了自己的网格,但我们没有在场景是渲染它们的游戏对象。所以我们将从包括一个网格渲染器和筛网过滤器的watermesh预制件来创造它们。
meshobjects = Instantiate(watermesh,Vector3.zero,Quaternion.identity) as GameObject;
meshobjects.GetComponent().mesh = meshes;
meshobjects.transform.parent = transform;
我们设置了网格,令其成为水体管理器的子项。
在本篇教程中,我们将使用简单的物理机制模拟一个动态的2D水体。我们将使用一个线性渲染器、网格渲染器,触发器以及粒子的混合体来创造这一水体效果,最终得到可运用于你下款游戏的水纹和水花。这里包含了Unity样本源,但你应该能够使用任何游戏引擎以相同的原理执行类似的操作。
设置水体管理器
我们将使用Unity的一个线性渲染器来渲染我们的水体表面,并使用这些节点来展现持续的波纹。
登录/注册后可看大图
uYfYBv.jpg (8.81 KB, 下载次数: 32)
下载附件 保存到相册
2014-4-7 09:44 上传
unity-water-linerenderer(from gamedevelopment)
我们将追踪每个节点的位置、速度和加速情况。为此,我们将会使用到阵列。所以在我们的类顶端将添加如下变量:
float[] xpositions;
float[] ypositions;
float[] velocities;
float[] accelerations;
LineRenderer Body;
LineRenderer将存储我们所有的节点,并概述我们的水体。我们仍需要水体本身,将使用Meshes来创造。我们将需要对象来托管这些网格。
GameObject[] meshobjects;
Mesh[] meshes;
我们还需要碰撞器以便事物可同水体互动:
GameObject[] colliders;
我们也存储了所有的常量:
const float springconstant = 0.02f;
const float damping = 0.04f;
const float spread = 0.05f;
const float z = -1f;
这些常量中的z是我们为水体设置的Z位移。我们将使用-1标注它,这样它就会呈现于我们的对象之前(游戏邦注:你可能想根据自己的需求将其调整为在对象之前或之后,那你就必须使用Z坐标来确定与之相关的精灵所在的位置)。
下一步,我们将保持一些值:
float baseheight;
float left;
float bottom;
这些就是水的维度。
我们将需要一些可以在编辑器中设置的公开变量。首先,我们将为水花使用粒子系统:
public GameObject splash:
接下来就是我们将用于线性渲染器的材料:
public Material mat:
此外,我们将为主要水体使用的网格类型如下:
public GameObject watermesh:
我们想要能够托管所有这些数据的游戏对象,令其作为管理器,产出我们游戏中的水体。为此,我们将编写SpawnWater()函数。
这个函数将采用水体左边、跑马度、顶点以及底部的输入:
public void SpawnWater(float Left, float Width, float Top, float Bottom)
{
(虽然这看似有所矛盾,但却有利于从左往右快速进行关卡设计)
创造节点
现在我们将找出自己需要多少节点:
int edgecount = Mathf.RoundToInt(Width) * 5;
int nodecount = edgecount + 1;
我们将针对每个单位宽度使用5个节点,以便呈现流畅的移动(你可以改变这一点以便平衡效率与流畅性)。我们由此可得到所有线段,然后需要在末端的节点 + 1。
我们要做的首件事就是以LineRenderer组件渲染水体:
Body = gameObject.AddComponent();
Body.material = mat;
Body.material.renderQueue = 1000;
Body.SetVertexCount(nodecount);
Body.SetWidth(0.1f, 0.1f);
我们在此还要做的是选择材料,并通过选择渲染队列中的位置而令其在水面之上渲染。我们设置正确的节点数据,将线段宽度设为0.1。
你可以根据自己所需的线段粗细来改变这一宽度。你可能注意到了SetWidth()需要两个参数,这是线段开始及末尾的宽度。我们希望该宽度恒定不变。
现在我们制作了节点,将初始化我们所有的顶级变量:
xpositions = new float[nodecount];
ypositions = new float[nodecount];
velocities = new float[nodecount];
accelerations = new float[nodecount];
meshobjects = new GameObject[edgecount];
meshes = new Mesh[edgecount];
colliders = new GameObject[edgecount];
baseheight = Top;
bottom = Bottom;
left = Left;
我们已经有了所有阵列,将控制我们的数据。
现在要设置我们阵列的值。我们将从节点开始:
for (int i = 0; i < nodecount; i++)
{
ypositions = Top;
xpositions = Left + Width * i / edgecount;
accelerations = 0;
velocities = 0;
Body.SetPosition(i, new Vector3(xpositions, ypositions, z));
}
在此,我们将所有Y位置设于水体之上,之后一起渐进增加所有节点。因为水面平静,我们的速度和加速值最初为0。
我们将把LineRenderer (Body)中的每个节点设为其正确的位置,以此完成这个循环。
创造网格
这正是它棘手的地方。
我们有自己的线段,但我们并没有水体本身。我们要使用网格来制作,如下所示:
for (int i = 0; i < edgecount; i++)
{
meshes = new Mesh();
现在,网格存储了一系列变量。首个变量相当简单:它包含了所有顶点(或转角)。
登录/注册后可看大图
uYfYBv.jpg (13.63 KB, 下载次数: 31)
下载附件 保存到相册
2014-4-7 09:44 上传
unity-water-Firstmesh(from gamedevelopment)
该图表显示了我们所需的网格片段的样子。第一个片段中的顶点被标注出来了。我们总共需要4个顶点。
Vector3[] Vertices = new Vector3[4];
Vertices[0] = new Vector3(xpositions, ypositions, z);
Vertices[1] = new Vector3(xpositions[i + 1], ypositions[i + 1], z);
Vertices[2] = new Vector3(xpositions, bottom, z);
Vertices[3] = new Vector3(xpositions[i+1], bottom, z);
现在如你所见,顶点0处于左上角,1处于右上角,2是左下角,3是右下角。我们之后要记住。
网格所需的第二个性能就是UV。网格拥有纹理,UV会选择我们想撷取的那部分纹理。在这种情况下,我们只想要左上角,右上角,右下角和右下角的纹理。
Vector2[] UVs = new Vector2[4];
UVs[0] = new Vector2(0, 1);
UVs[1] = new Vector2(1, 1);
UVs[2] = new Vector2(0, 0);
UVs[3] = new Vector2(1, 0);
现在我们又需要这些数据了。网格是由三角形组成的,我们知道任何四边形都是由两个三角形组成的,所以现在我们需要告诉网格它如何绘制这些三角形。
登录/注册后可看大图
uYfYBv.jpg (17.27 KB, 下载次数: 33)
下载附件 保存到相册
2014-4-7 09:44 上传
unity-water-Tris(from gamedevelopment)
看看含有节点顺序标注的转角。三角形A连接节点0,1,以及3,三角形B连接节点3,2,1。因此我们想制作一个包含6个整数的阵列:
int[] tris = new int[6] { 0, 1, 3, 3, 2, 0 };
这就创造了我们的四边形。现在我们要设置网格的值。
meshes.vertices = Vertices;
meshes.uv = UVs;
meshes.triangles = tris;
现在我们已经有了自己的网格,但我们没有在场景是渲染它们的游戏对象。所以我们将从包括一个网格渲染器和筛网过滤器的watermesh预制件来创造它们。
meshobjects = Instantiate(watermesh,Vector3.zero,Quaternion.identity) as GameObject;
meshobjects.GetComponent().mesh = meshes;
meshobjects.transform.parent = transform;
我们设置了网格,令其成为水体管理器的子项。
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询