unity中你进入unity水在,还是看见水的,这个如果弄?

在unity3d中水能做的跟真的一样吗 比如用手拍的时候 水滴散出去_百度知道
在unity3d中水能做的跟真的一样吗 比如用手拍的时候 水滴散出去
我有更好的答案
首先Unity中自带的Water (Pro Only)和Water (Basic)包都是材质(shader),是视觉上的水面表现。要想完成你这个任务,需要流体物理模拟(Fluid Physics 或 Liquid Physics)。可以去 Assets Store 上看看 Fluvio 这个插件,有免费版和示例工程。但是一般简单的也可以自己实现,因为游戏里大多不会出现全部的流体表现情况(全模拟是很耗费硬件资源的)。你可以自定义一个粒子发生器,它的效果是水溅开的运动,然后检测到与水面碰撞时调用它就可以了。
采纳率:83%
来自团队:
为您推荐:
其他类似问题
unity3d的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。论文发表、论文指导
周一至周五
9:00&22:00
Unity3D第一人称视角游戏者水下视觉效果的实现
  摘要:本文简单的阐述了使用Unity3D游戏引擎自带的第一人称控制器创建的第一人称游戏者在水面之下行进时,如何让游戏画面呈现出和其在水面之上时不同的视觉效果。中国论文网 /8/view-3776051.htm  关键词:Unity3D;游戏引擎;第一人称视角;水下效果  中图分类号:TP39 文献标识码:A 文章编号: (2012) 15-0000-02  Unity3D是一款由Unity Technologies开发的一款游戏引擎,是一种便捷易用的交互式图形化的综合型游戏开发工具。它可以很方便的让非计算机专业的开发人员创建自己的三维电脑游戏、建筑的可视化游历、以及实时交互的三维动画作品。该游戏引擎支持多种系统平台,它开发的作品可以很方便的在PC、MAC等计算机平台上运行,也可以在Android和iOS等移动设备平台上运行,还可直接发布到网页。该游戏引擎还对常用的三维建模软件如3DMAX、Maya有着非常好的兼容性,可以很方便的把在上述三维建模软件当中建造好的各种三维模型导入到游戏场景之中。因此,该开发平台应用非常广泛,特别是在艺术设计领域,更是受到广大开发人员的推崇。  该引擎在其内置的Standard Assets资源包的Prefabs项下,提供了一个名为Firsr Person Controller(第一人称控制器)的预制对象。开发人员在游戏场景中加入此预制对象,即可很方便的创建出一个第一人称视角的游戏者,然后配合键盘上的W、A、S、D按键和鼠标,即可控制游戏者的运动方向和视角。这样一来,开发人员可以几乎不用写任何程序代码,即可实现一个三维游戏场景的可视化游历。  在该游戏引擎内置的Pro Standard Assets资源包的Water项下,还提供了一个名为DayLight Water的预制对象,通过在场景中导入该对象,我们可以很方便的在场景中创建出一个水体系统。其覆盖面积大小和水面的视觉效果,可以通过在Inspector窗口中指定DayLight Water对象的一系列参数来指定。建立好水体系统后,我们操纵使用系统内置的第一人称控制器创建的游戏者走到水面之下时,就会发现,游戏玩家无论是在水面之上还是在水面之下,表现出来的视觉效果都是一样的。为什么是这样的呢?原来,Unity引擎内置的“DayLight Water”对象只是一个指定了位置、面积和高度的具有水面属性材质的一个平面。这个平面之上和平面之下,环境都是一样的,因此无论游戏者在水面之上还是水面之下,屏幕上呈现的第一人称视角游戏画面的视觉效果都是一样的。而现实中,我们在水下的视角看上去的画面是和在水面之上的视角看上去是有区别的,一般来说,水下看到的场景会有水本身的颜色,而且看起来会有些模糊。  那么,我们如何才能让游戏者在水面之上和在水面之下时看到的画面呈现出不同效果呢?首先,我们来分析一下DayLight Water对象的基本属性:在Hierarchy窗口中我们选定游戏场景中的DayLight Water对象后,在Inspector窗口可以看到其一系列的属性,其中在Trasform属性当中有一个名为Position的项,包括X、Y和Z三个数值。其中Y值就是指水平面距离游戏场景的地形系统中的基本地平面的高度。在场景的地形之中,只要距离基本地平面的高度小于Y值得低洼地面和放置其上的三维模型,就会淹没在水面之下,反之高于此数值的高原、山地和放置其上的三维模型则会露出在水面之上。Unity引擎中的第一人称控制器对象,是沿着地面运动的,因此当我们运行游戏时用W、A、S、D键操纵游戏者走到高度值低于水面的Y值得地面时,游戏者就会沉入水中。  接下来我们再分析一下Firsr Person Controller(第一人称控制器)预制对象。当我们在Hierarchy窗口中展开Firsr Person Controller对象后,我们发现原来Firsr Person Controller是由两个子对象构成的,其中一个子对象是一个竖立的胶囊形物体,名为“Graphics”,而另一个则是名为“MainCamera”的摄像机对象。事实上,我们用W、A、S、D键操纵第一人称游戏者运动的时候,事实上是在操纵“Graphics”对象在运动,而摄像机“MainCamera”是绑定在“Graphics”物体上并随之运动的,我们从画面上看到的第一人称场景,就是“MainCamera”摄像机视角的场景。这样,我们只需要对Firsr Person Controller的摄像机子对象“MainCamera”进行设定,使之在水面之下时让其视角中的画面呈现出类似人们在水下看到的场景效果,就可以达到我们本文所讨论的目的了。  首先,我们看看如何使摄像机视角的画面呈现类似在水下的感觉。我们可以先尝试给画面染上类似水下的淡蓝绿色:在Hierarchy窗口中,我们选中“MainCamera”摄像机对象,然后在屏幕上方的Component菜单下选择Image Effects子菜单,并选中其中的Color Correction项,此时,Inspector窗口中就会多出一组名为“Color Correction Effect”的参数,我们对其中的R、G、B值做一下微调,比方说把R设置为-0.4,这样,我们切换到摄像视图去看的话,就会发现画面颜色变暗并被染上淡淡的蓝绿色了。接下来,我们再尝试使画面变模糊:我们从刚才的Image Effects子菜单中选择“Blur”项,并在Inspector窗口中调整“Blur Effect”的有关参数,比方说把Iterations设置为1,Blur Spread设置为0.3.这样,我们再切换到摄像机视图去看,就会发现画面已经呈现略带模糊的蓝绿色,很像在水下的感觉了。上述工作完成之后,我们运行一下游戏场景,就会发现,此时无论第一人称游戏者是在水面之上还是水面之下,画面呈现的都是水面之下的感觉。这样显然还是不符合我们的最终目的。因此,我们还需要写一段JavaScript程序代码,让这个第一人称摄像机来判断当前所处的位置是否再水面之下,并根据需要来开启“Color Correction Effect”和“Blur Effect”。   接下来,我们在Hierarchy窗口中点选Firsr Person Controller的摄像机子对象“MainCamera”,在Inspector窗口中检视其相关属性和参数,会发现它也有一组Trasform属性,对应的Position的项也同样包括X、Y和Z三个数值。而且切换到摄像机视图并运行游戏场景时,会发现这三个数值是随着我们操纵第一人称游戏者在画面上运动而不断改变的。当第一人称游戏者走到水面之下以后,Y值就小于水面的Y值了。因此,我们只需要检测“MainCamera”的Y值是否大于水面的Y值,即可判定游戏者是在水面之上还是在水面之下了。我们可以创建以下JavaScript程序代码:  var Camara_1 : GameObject;  var Under : float;  function Start(){  Camara_1 = GameObject.Find("/First Person Controller/Main Camera");  }  function Update () {  if(transform.position.y < Under)  {  Camara_1.GetComponent("ColorCorrectionEffect").enabled = true;  Camara_1.GetComponent("BlurEffect").enabled = true;  }  if(transform.position.y > Under)  {  Camara_1.GetComponent("ColorCorrectionEffect").enabled = false;  Camara_1.GetComponent("BlurEffect").enabled = false;  }  }  此段程序代码中,我们设定当该摄像机所在位置的高度低于“Hight”变量的数值时开启“Color Correction Effect”和“Blur Effect”效果,高于此数值时则会关掉上述效果。我们将此段程序代码命名为“UnderWater.js”并保存  最后,我们再次在Hierarchy窗口中点选Firsr Person Controller的摄像机子对象“MainCamera”,然后把刚才创建好的程序代码文件拖放到“MainCamera”项上面。此时,在Inspector窗口中我们即可看到一个名为“UnderWater”的组件,展开该组件,可以看到该组件包含一个名为“Hight”的变量。我们点选此变量,然后把之前我们在“DayLightWater”对象中设定的水面高度值输入进去,游戏运行时,当摄像机低于水面高度时就会开启“Color Correction Effect”和“Blur Effect”效果,高于此数值时则会关掉上述效果。这样,我们就实现了第一人称游戏者在水面之上和水面之下游戏画面显示的不同视觉效果了。  参考文献:  [1]《Unity游戏开发实战》,米歇尔·梅纳德著,机械工业出版社  [2]《Unity3D游戏开发》,宣雨松著,人民邮电出版社
转载请注明来源。原文地址:
【xzbu】郑重声明:本网站资源、信息来源于网络,完全免费共享,仅供学习和研究使用,版权和著作权归原作者所有,如有不愿意被转载的情况,请通知我们删除已转载的信息。
xzbu发布此信息目的在于传播更多信息,与本网站立场无关。xzbu不保证该信息(包括但不限于文字、数据及图表)准确性、真实性、完整性等。求大神帮忙看一下,初学u3d,这个水为什么是粉色的。【unity3d吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:87,571贴子:
求大神帮忙看一下,初学u3d,这个水为什么是粉色的。收藏
从Environment中导入到Terrain中,水是粉色的,如何改,
SiKi学院上有大量的项目案例课程,可以让学员随时随地学习!通过案例来从入门到深入的学习Unity!
右下角,改这个
这是材质丢失了吧
因为你导入的包不完整,里面的材质贴图没有倒进来,你换一个水材质,fx里面的其他水
你的材质丢失了,你可以先去了解什么是材质什么是贴图,shader等你对unity的基本操作基本知识点都理解后再去看吧
《Unity 5.X从入门到精通》 楼主 我也在学习, 比你进度稍微快一点点,我看到2D 的那章了,加个好友啊,学习当中可以相互交流一下,互帮互助
登录百度帐号如何使用Unity创造动态的2D水体效果
发布时间: 09:44:40
Tags:,,,,
作者:Alex Rose
在本篇教程中,我们将使用简单的物理机制模拟一个动态的2D水体。我们将使用一个线性渲染器、网格渲染器,触发器以及粒子的混合体来创造这一水体效果,最终得到可运用于你下款游戏的水纹和水花。这里包含了Unity样本源,但你应该能够使用任何游戏引擎以相同的原理执行类似的操作。
设置水体管理器
我们将使用Unity的一个线性渲染器来渲染我们的水体表面,并使用这些节点来展现持续的波纹。
unity-water-linerenderer(from gamedevelopment)
我们将追踪每个节点的位置、速度和加速情况。为此,我们将会使用到阵列。所以在我们的类顶端将添加如下变量:
LineRenderer B
LineRenderer将存储我们所有的节点,并概述我们的水体。我们仍需要水体本身,将使用Meshes来创造。我们将需要对象来托管这些网格。
GameObject[]
我们还需要碰撞器以便事物可同水体互动:
GameObject[]
我们也存储了所有的常量:
const float springconstant = 0.02f;
const float damping = 0.04f;
const float spread = 0.05f;
const float z = -1f;
这些常量中的z是我们为水体设置的Z位移。我们将使用-1标注它,这样它就会呈现于我们的对象之前(游戏邦注:你可能想根据自己的需求将其调整为在对象之前或之后,那你就必须使用Z坐标来确定与之相关的精灵所在的位置)。
下一步,我们将保持一些值:
这些就是水的维度。
我们将需要一些可以在编辑器中设置的公开变量。首先,我们将为水花使用粒子系统:
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&LineRenderer&();
Body.material =
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 = T
bottom = B
我们已经有了所有阵列,将控制我们的数据。
现在要设置我们阵列的值。我们将从节点开始:
for (int i = 0; i & i++)
ypositions[i] = T
xpositions[i] = Left + Width * i /
accelerations[i] = 0;
velocities[i] = 0;
Body.SetPosition(i, new Vector3(xpositions[i], ypositions[i], z));
在此,我们将所有Y位置设于水体之上,之后一起渐进增加所有节点。因为水面平静,我们的速度和加速值最初为0。
我们将把LineRenderer (Body)中的每个节点设为其正确的位置,以此完成这个循环。
这正是它棘手的地方。
我们有自己的线段,但我们并没有水体本身。我们要使用网格来制作,如下所示:
for (int i = 0; i & i++)
meshes[i] = new Mesh();
现在,网格存储了一系列变量。首个变量相当简单:它包含了所有顶点(或转角)。
unity-water-Firstmesh(from gamedevelopment)
该图表显示了我们所需的网格片段的样子。第一个片段中的顶点被标注出来了。我们总共需要4个顶点。
Vector3[] Vertices = new Vector3[4];
Vertices[0] = new Vector3(xpositions[i], ypositions[i], z);
Vertices[1] = new Vector3(xpositions[i + 1], ypositions[i + 1], z);
Vertices[2] = new Vector3(xpositions[i], 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);
现在我们又需要这些数据了。网格是由三角形组成的,我们知道任何四边形都是由两个三角形组成的,所以现在我们需要告诉网格它如何绘制这些三角形。
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[i].vertices = V
meshes[i].uv = UVs;
meshes[i].triangles =
现在我们已经有了自己的网格,但我们没有在场景是渲染它们的游戏对象。所以我们将从包括一个网格渲染器和筛网过滤器的watermesh预制件来创造它们。
meshobjects[i] = Instantiate(watermesh,Vector3.zero,Quaternion.identity) as GameO
meshobjects[i].GetComponent&MeshFilter&().mesh = meshes[i];
meshobjects[i].transform.parent =
我们设置了网格,令其成为水体管理器的子项。
创造碰撞效果
现在我们还需要自己的碰撞器:
colliders[i] = new GameObject();
colliders[i].name = &#8220;Trigger&#8221;;
colliders[i].AddComponent&BoxCollider2D&();
colliders[i].transform.parent =
colliders[i].transform.position = new Vector3(Left + Width * (i + 0.5f) / edgecount, Top &#f, 0);
colliders[i].transform.localScale = new Vector3(Width / edgecount, 1, 1);
colliders[i].GetComponent&BoxCollider2D&().isTrigger =
colliders[i].AddComponent&WaterDetector&();
至此,我们制作了方形碰撞器,给它们一个名称,以便它们会在场景中显得更整洁一点,并且再次制作水体管理器的每个子项。我们将它们的位置设置于两个节点之点,设置好大小,并为其添加了WaterDetector类。
现在我们拥有自己的网格,我们需要一个函数随着水体移动进行更新:
void UpdateMeshes()
for (int i = 0; i & meshes.L i++)
Vector3[] Vertices = new Vector3[4];
Vertices[0] = new Vector3(xpositions[i], ypositions[i], z);
Vertices[1] = new Vector3(xpositions[i+1], ypositions[i+1], z);
Vertices[2] = new Vector3(xpositions[i], bottom, z);
Vertices[3] = new Vector3(xpositions[i+1], bottom, z);
meshes[i].vertices = V
你可能注意到了这个函数只使用了我们之前编写的代码。唯一的区别在于这次我们并不需要设置三角形的UV,因为这些仍然保持不变。
我们的下一步任务是让水体本身运行。我们将使用FixedUpdate()递增地来调整它们。
void FixedUpdate()
执行物理机制
首先,我们将把Hooke定律写Euler方法结合在一起找到新坐标、加速和速度。
Hooke定律是F=kx,这里的F是指由水流产生的力(记住,我们将把水体表面模拟为水流),k是指水流的常量,x则是位移。我们的位移将成为每个节点的y坐标减去节点的基本高度。
下一步,我们将添加一个与力的速度成比例的阻尼因素来削弱力。
for (int i = 0; i & xpositions.L i++)
float force = springconstant * (ypositions[i] &#8211; baseheight) + velocities[i]*
accelerations[i] = -
ypositions[i] += velocities[i];
velocities[i] += accelerations[i];
Body.SetPosition(i, new Vector3(xpositions[i], ypositions[i], z));
Euler方法很简单,我们只要向速度添加加速,向每帧坐标增加速度。
注:我只是假设每个节点的质量为1,但你可能会想用:
accelerations[i] = -force/
现在我们将创造波传播。以下节点是根据Michael Hoffman的教程调整而来的:
float[] leftDeltas = new float[xpositions.Length];
float[] rightDeltas = new float[xpositions.Length];
在此,我们要创造两个阵列。针对每个节点,我们将检查之前节点的高度,以及当前节点的高度,并将二者差别放入leftDeltas。
之后,我们将检查后续节点的高度与当前检查节点的高度,并将二者的差别放入rightDeltas(我们将乘以一个传播常量来增加所有值)。
for (int j = 0; j & 8; j++)
for (int i = 0; i & xpositions.L i++)
if (i & 0)
leftDeltas[i] = spread * (ypositions[i] &#8211; ypositions[i-1]);
velocities[i - 1] += leftDeltas[i];
if (i & xpositions.Length &#8211; 1)
rightDeltas[i] = spread * (ypositions[i] &#8211; ypositions[i + 1]);
velocities[i + 1] += rightDeltas[i];
当我们集齐所有的高度数据时,我们最后就可以派上用场了。我们无法查看到最右端的节点右侧,或者最大左端的节点左侧,因此基条件就是i & 0以及i & xpositions.Length &#8211; 1。
因此,要注意我们在一个循环中包含整片代码,并运行它8次。这是因为我们想以少量而多次的时间运行这一过程,而不是进行一次大型运算,因为这会削弱流动性。
现在我们已经有了流动的水体,下一步就需要让它溅起水花!
为此,我们要增加一个称为Splash()的函数,它会检查水花的X坐标,以及它所击中的任何物体的速度。将其设置为公开状态,这样我们可以在之后的碰撞器中调用它。
public void Splash(float xpos, float velocity)
首先,我们应该确保特定的坐标位于我们水体的范围之内:
if (xpos &= xpositions[0] && xpos &= xpositions[xpositions.Length-1])
然后我们将调整xpos,让它出现在相对于水体起点的位置上:
xpos -= xpositions[0];
下一步,我们将找到它所接触的节点。我们可以这样计算:
int index = Mathf.RoundToInt((xpositions.Length-1)*(xpos / (xpositions[xpositions.Length-1] &#8211; xpositions[0])));
这就是它的运行方式:
1.我们选取相对于水体左侧边缘位置的水花位置(xpos)。
2.我们将相对于水体左侧边缘的的右侧位置进行划分。
3.这让我们知道了水花所在的位置。例如,位于水体四分之三处的水花的值就是0.75。
4.我们将把这一数字乘以边缘的数量,这就可以得到我们水花最接近的节点。
velocities[index] =
现在我们要设置击中水面的物体的速度,令其与节点速度一致,以样节点就会被该物体拖入深处。
Particle-System(from gamedevelopment)
注:你可以根据自己的需求改变这条线段。例如,你可以将其速度添加到当前速度,或者使用动量而非速度,并除以你节点的质量。
现在,我们想制作一个将产生水花的粒子系统。我们早点定义,将其称为“splash”。要确保不要让它与Splash()相混淆。
首先,我们要设置水花的参,以便调整物体的速度:
float lifetime = 0.93f + Mathf.Abs(velocity)*0.07f;
splash.GetComponent&ParticleSystem&().startSpeed = 8+2*Mathf.Pow(Mathf.Abs(velocity),0.5f);
splash.GetComponent&ParticleSystem&().startSpeed = 9 + 2 * Mathf.Pow(Mathf.Abs(velocity), 0.5f);
splash.GetComponent&ParticleSystem&().startLifetime =
在此,我们要选取粒子,设置它们的生命周期,以免他们击中水面就快速消失,并且根据它们速度的直角设置速度(为小小的水花增加一个常量)。
你可能会看着代码心想,“为什么要两次设置startSpeed?”你这样想没有错,问题在于,我们使用一个起始速度设置为“两个常量间的随机数”这种粒子系统(Shuriken)。不幸的是,我们并没有太多以脚本访问Shuriken的途径 ,所以为了获得这一行为,我们必须两次设置这个值。
现在,我将添加一个你可能想或者不想从脚本中忽略的线段:
Vector3 position = new Vector3(xpositions[index],ypositions[index]-0.35f,5);
Quaternion rotation = Quaternion.LookRotation(new Vector3(xpositions[Mathf.FloorToInt(xpositions.Length / 2)], baseheight + 8, 5) &#8211; position);
Shuriken粒子击中你的物体时不会被破坏,所以如果你想确保它们不会在你的物体面前着陆,你可以采用两种对策:
1.令其固定在背景(你可以通过将Z坐标设为5来实现)
2.令粒子系统倾斜,令其总是指向你水体的中心&#8212;&#8212;这样,粒子就不会飞溅到水面。
第二行代码位居坐标的中间点,向上移一点点,并指向粒子发射器。如果你要使用真正宽阔的水体,你可能就不需要这种行为。如果你的水体只是房间中的一个小水池,你可能就会想使用它。所以,你可以根据自己的需要抛弃关于旋转的代码。
GameObject splish = Instantiate(splash,position,rotation) as GameO
Destroy(splish, lifetime+0.3f);
现在,我们得制作水花,并让它在粒子应该消失之后的片刻再消失。为什么要在之后片刻呢?因为我们的粒子系统会发送出一些连续的粒子阵,所以即使首批粒子只会持续到Time.time + lifetime,我们最终的粒子阵也仍然会存留一小会儿。
没错,我们终于完工了,不是吗?
错了!我们必须检测我们的对象,否则一切都是徒劳的!
记得我们之前向所有碰撞器添加脚本的情况吗?还记得WaterDetector吗?
我们现在就要把它制作出来!我们在其中只需要一个函数:
void OnTriggerEnter2D(Collider2D Hit)
使用OnTriggerEnter2D()我们可以规定2D刚体进入水体时所发生的情况。如果我们通过了Collider2D的一个参数,就可以找到更多关于该物体的信息:
if (Hit.rigidbody2D != null)
我们只需要包含rigidbody2D的物体:
transform.parent.GetComponent&Water&().Splash(transform.position.x, Hit.rigidbody2D.velocity.y*Hit.rigidbody2D.mass / 40f);
现在,我们所有的碰撞器都是水体管理器的子项。所以我们只需要从它们的母体撷取Water组件并从碰撞器的位置调用Splash()。
记住,我说过如果你想让它更具物理准确性,就可以传递速度或动量。这里就需要你传递一者。如果你将对象的Y速度与其质量相乘,就可以得到它的动量。如果你只想使用它的速度,就要从该行代码中去除质量。
最后,你将从某处调用SpawnWater(),如下所示:
void Start()
SpawnWater(-10,20,0,-10);
现在我们完成了!现在任何含有一个碰撞器并击中水面的rigidbody2D都会创造一个水花,并且波纹还能正确移动。
Splash2(from gamedevelopment)
作为一个额外操作,我还在SpawnWater()之上添加了几行代码。
gameObject.AddComponent&BoxCollider2D&();
gameObject.GetComponent&BoxCollider2D&().center = new Vector2(Left + Width / 2, (Top + Bottom) / 2);
gameObject.GetComponent&BoxCollider2D&().size = new Vector2(Width, Top &#8211; Bottom);
gameObject.GetComponent&BoxCollider2D&().isTrigger =
这几行代码会向水面本身添加一个方体碰撞器。你可以运用自己的知识,以此让物体漂浮在水面。
你将会制作一个称为OnTriggerStay2D()的函数,它有一个Collider2D Hit参数。之后,你可以使用我们之前使用的一个检查物体质量的弹性法则的调整版本,并为你的rigidbody2D添加一个力或速度以便令其漂浮在水面。
在本篇教程中,我们以一些简单的物理代码和一个线性渲染器、网格渲染器、触发器和粒子执行了用于2D游戏的简单模拟水体。也许你会添加波浪起伏的水体来作为自己下款平台游戏的障碍,准确让你的角色跳入水中或小心地穿过漂浮着的跳板,或者你可能想将它用于航海或冲浪游戏,甚至是一款只是需要玩家跳过水面的岩石的游戏。总之,祝你好运!(本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦)
Creating Dynamic 2D Water Effects in Unity
by Alex Rose
In this tutorial, we&#8217;re going to simulate a dynamic 2D body of water using simple physics. We will use a mixture of a line renderer, mesh renderers, triggers and particles to create our effect. The final result comes complete with waves and splashes, ready to add to your next game. A Unity (Unity3D) demo source is included, but you should be able to implement something similar using the same principles in any game engine.
Related Posts
Make a Splash With Dynamic 2D Water Effects
How to Create a Custom 2D Physics Engine: The Basics and Impulse Resolution
Adding Turbulence to a Particle System
End Result
Here&#8217;s what we&#8217;re going to end up with. You&#8217;ll need the Unity browser plugin to try it out.
Click to create a new object to drop into the water.
Setting Up Our Water Manager
In his tutorial, Michael Hoffman demonstrated how we can model the surface of water with a row of springs.
We&#8217;re going to render the top of our water using one of Unity&#8217;s line renderers, and use so many nodes that it appears as a continuous wave.
Create 2D Dynamic Water Effects in Unity (Unity3D)
We&#8217;ll have to keep track of the positions, velocities and accelerations of every node, though. To do that, we&#8217;re going to use arrays. So at the top of our class we&#8217;ll add these variables:
LineRenderer B
The LineRenderer will store all our nodes and outline our body of water. We still need the water itself, we&#8217;ll create this with Meshes. We&#8217;re going to need objects to hold these meshes too.
GameObject[]
We&#8217;re also going to need colliders so that things can interact with our water:
GameObject[]
And we&#8217;ll store all our constants as well:
const float springconstant = 0.02f;
const float damping = 0.04f;
const float spread = 0.05f;
const float z = -1f;
These constants are the same kind as Michael discussed, with the exception of z—this is our z-offset for our water. We&#8217;re going to use -1 for this so that it gets displayed in front of our objects. (You might want to change this depending on what you want to appear in fr you&#8217;re going to have to use the z-coordinate to determine where sprites sit relative to it.)
Next, we&#8217;re going to hold onto some values:
These are just the dimensions of the water.
We&#8217;re going to need some public variables we can set in the editor, too. First, the particle system we&#8217;re going to use for our splashes:
public GameObject splash:
Next, the material we&#8217;ll use for our line renderer (in case you want to reuse the script for acid, lava, chemicals, or anything else):
public Material mat:
Plus, the kind of mesh we&#8217;re going to use for the main body of water:
public GameObject watermesh:
These are all going to be based on prefabs, which are all included in the source files.
We want a game object that can hold all of this data, act as a manager, and spawn our body of water ingame to specification. To do that, we&#8217;ll write a function called SpawnWater().
This function will take inputs of the left side, the width, the top, and the bottom of the body of water.
public void SpawnWater(float Left, float Width, float Top, float Bottom)
(Though this seems inconsistent, it acts in the interest of quick level design when building from left to right).
Creating the Nodes
Now we&#8217;re going to find out how many nodes we need:
int edgecount = Mathf.RoundToInt(Width) * 5;
int nodecount = edgecount + 1;
We&#8217;re going to use five per unit width, to give us smooth motion that isn&#8217;t too demanding. (You can vary this to balance efficiency against smoothness.) This gives us all our lines, then we need the + 1 for the extra node on the end.
The first thing we&#8217;re going to do is render our body of water with the LineRenderer component:
Body = gameObject.AddComponent&LineRenderer&();
Body.material =
Body.material.renderQueue = 1000;
Body.SetVertexCount(nodecount);
Body.SetWidth(0.1f, 0.1f);
What we&#8217;ve also done here is select our material, and set it to render above the water by choosing its position in the render queue. We&#8217;ve set the correct number of nodes, and set the width of the line to 0.1.
You can vary this depending on how thick you want your line. You may have noticed that SetWidth()
these are the width at the start and the end of the line. We want that width to be constant.
Now that we&#8217;ve made our nodes, we&#8217;ll initialise all our top variables:
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 = T
bottom = B
So now we have all our arrays, and we&#8217;re holding on to our data.
Now to actually set the values of our arrays. We&#8217;ll start with the nodes:
for (int i = 0; i & i++)
ypositions[i] = T
xpositions[i] = Left + Width * i /
accelerations[i] = 0;
velocities[i] = 0;
Body.SetPosition(i, new Vector3(xpositions[i], ypositions[i], z));
Here, we set all the y-positions to be at the top of the water, and then incrementally add all the nodes side by side. Our velocities and accelerations are zero initially, as the water is still.
We finish the loop by setting each node in our LineRenderer (Body) to their correct position.
Creating the Meshes
Here&#8217;s where it gets tricky.
We have our line, but we don&#8217;t have the water itself. And the way we can make this is using Meshes. We&#8217;ll start off by creating these:
for (int i = 0; i & i++)
meshes[i] = new Mesh();
Now, Meshes store a bunch of variables. The first variable is pretty simple: it contains all the vertices (or corners).
Create 2D Dynamic Water Effects in Unity (Unity3D)
The diagram shows what we want our mesh segments to look like. For the first segment, the vertices are highlighted. We want four in total.
Vector3[] Vertices = new Vector3[4];
Vertices[0] = new Vector3(xpositions[i], ypositions[i], z);
Vertices[1] = new Vector3(xpositions[i + 1], ypositions[i + 1], z);
Vertices[2] = new Vector3(xpositions[i], bottom, z);
Vertices[3] = new Vector3(xpositions[i+1], bottom, z);
Now, as you can see here, vertex 0 is the top-left, 1 is the top-right, 2 is the bottom-left, and 3 is the top-right. We&#8217;ll need to remember that for later.
The second property that meshes need is UVs. Meshes have textures, and the UVs choose which part of the textures we want to grab. In this case, we just want the top-left, top-right, bottom-left, and bottom-right corners of our texture.
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);
Now we need those numbers from before again. Meshes are made up of triangles, and we know that any quadrilateral can be made of two triangles, so now we need to tell the mesh how it should draw those triangles.
Create 2D Dynamic Water Effects in Unity (Unity3D)
Look at the corners with the node order labelled. Triangle A connects nodes 0, 1 and 3; Triangle B connects nodes 3, 2 and 0. Therefore, we want to make an array that contains six integers, reflecting exactly that:
int[] tris = new int[6] { 0, 1, 3, 3, 2, 0 };
This creates our quadrilateral. Now we set the mesh values.
meshes[i].vertices = V
meshes[i].uv = UVs;
meshes[i].triangles =
Now, we have our meshes, but we don&#8217;t have Game Objects to render them in the scene. So we&#8217;re going to create them from our watermesh prefab which contains a Mesh Renderer and Mesh Filter.
meshobjects[i] = Instantiate(watermesh,Vector3.zero,Quaternion.identity) as GameO
meshobjects[i].GetComponent&MeshFilter&().mesh = meshes[i];
meshobjects[i].transform.parent =
We set the mesh, and we set it to be the child of the water manager, to tidy things up.
Creating Our Collisions
Now we want our collider too:
colliders[i] = new GameObject();
colliders[i].name = &#8220;Trigger&#8221;;
colliders[i].AddComponent&BoxCollider2D&();
colliders[i].transform.parent =
colliders[i].transform.position = new Vector3(Left + Width * (i + 0.5f) / edgecount, Top &#f, 0);
colliders[i].transform.localScale = new Vector3(Width / edgecount, 1, 1);
colliders[i].GetComponent&BoxCollider2D&().isTrigger =
colliders[i].AddComponent&WaterDetector&();
Here, we&#8217;re making box colliders, giving them a name so they&#8217;re a bit tidier in the scene, and making them each children of the water manager again. We set their position to be halfway between the nodes, set their size, and add a WaterDetector class to them.
Now that we have our mesh, we need a function to update it as the water moves:
void UpdateMeshes()
for (int i = 0; i & meshes.L i++)
Vector3[] Vertices = new Vector3[4];
Vertices[0] = new Vector3(xpositions[i], ypositions[i], z);
Vertices[1] = new Vector3(xpositions[i+1], ypositions[i+1], z);
Vertices[2] = new Vector3(xpositions[i], bottom, z);
Vertices[3] = new Vector3(xpositions[i+1], bottom, z);
meshes[i].vertices = V
You might notice that this function just uses the code we wrote before. The only difference is that this time we don&#8217;t have to set the tris and UVs, because these stay the same.
Our next task is to make the water itself work. We&#8217;ll use FixedUpdate() to modify them all incrementally.
void FixedUpdate()
Implementing the Physics
First, we&#8217;re going to combine Hooke&#8217;s Law with the Euler method to find the new positions, accelerations and velocities.
So, Hooke&#8217;s Law is F=kx, where F is the force produced by a spring (remember, we&#8217;re modelling the surface of the water as a row of springs), k is the spring constant, and x is the displacement. Our displacement is simply going to be the y-position of each node minus the base height of the nodes.
Next, we add a damping factor proportional to the velocity of the force to dampen the force.
for (int i = 0; i & xpositions.L i++)
float force = springconstant * (ypositions[i] &#8211; baseheight) + velocities[i]*
accelerations[i] = -
ypositions[i] += velocities[i];
velocities[i] += accelerations[i];
Body.SetPosition(i, new Vector3(xpositions[i], ypositions[i], z));
The Eu we just add the acceleration to the velocity and the velocity to the position, every frame.
Note: I just assumed the mass of each node was 1 here, but you&#8217;ll want to use:
accelerations[i] = -force/
if you want a different mass for your nodes.
Tip: For precise physics, we would use Verlet integration, but because we&#8217;re adding damping, we can only use the Euler method, which is a lot quicker to calculate. Generally, though, the Euler method will exponentially introduce kinetic energy from nowhere into your physics system, so don&#8217;t use it for anything precise.
Now we&#8217;re going to create wave propagation. The following code is adapted from Michael Hoffman&#8217;s tutorial.
float[] leftDeltas = new float[xpositions.Length];
float[] rightDeltas = new float[xpositions.Length];
Here, we create two arrays. For each node, we&#8217;re going to check the height of the previous node against the height of the current node and put the difference into leftDeltas.
Then, we&#8217;ll check the height of the subsequent node against the height of the node we&#8217;re checking, and put that difference into rightDeltas. (We&#8217;ll also multiply all values by a spread constant).
for (int j = 0; j & 8; j++)
for (int i = 0; i & xpositions.L i++)
if (i & 0)
leftDeltas[i] = spread * (ypositions[i] &#8211; ypositions[i-1]);
velocities[i - 1] += leftDeltas[i];
if (i & xpositions.Length &#8211; 1)
rightDeltas[i] = spread * (ypositions[i] &#8211; ypositions[i + 1]);
velocities[i + 1] += rightDeltas[i];
We can change the velocities based on the height difference immediately, but we should only store the differences in positions at this point. If we changed the position of the first node straight off the bat, by the time we looked at the second node, the first node will have already moved, so that&#8217;ll ruin all our calculations.
for (int i = 0; i & xpositions.L i++)
if (i & 0)
ypositions[i-1] += leftDeltas[i];
if (i & xpositions.Length &#8211; 1)
ypositions[i + 1] += rightDeltas[i];
So once we&#8217;ve collected all our height data, we can apply it at the end. We can&#8217;t look to the right of the node at the far right, or to the left of the node at the far left, hence the conditions i
& 0 and i & xpositions.Length &#8211; 1.
Also, note that we contained this whole code in a loop, and ran it eight times. This is because we want to run this process in small doses multiple times, rather than one large calculation, which would be a lot less fluid.
Adding Splashes
Now we have water that flows, and it shows. Next, we need to be able to disturb the water!
For this, let&#8217;s add a function called Splash(), which will check the x-position of the splash, and the velocity of whatever is hitting it. It should be public so that we can call it from our colliders later.
public void Splash(float xpos, float velocity)
First, we need to make sure that the specified position is actually within the bounds of our water:
if (xpos &= xpositions[0] && xpos &= xpositions[xpositions.Length-1])
And then we&#8217;ll change xpos so it gives us the position relative to the start of the body of water:
xpos -= xpositions[0];
Next, we&#8217;re going to find out which node it&#8217;s touching. We can calculate that like this:
int index = Mathf.RoundToInt((xpositions.Length-1)*(xpos / (xpositions[xpositions.Length-1] &#8211; xpositions[0])));
So, here&#8217;s what going on here:
We take the position of the splash relative to the position of the left edge of the water (xpos).
We divide this by the position of the right edge relative to the position of the left edge of the water.
This gives us a fraction that tells us where the splash is. For instance, a splash three-quarters of the way along the body of water would give a value of 0.75.
We multiply this by the number of edges and round this number, which gives us the node our splash was closest to.
velocities[index] =
Now we set the velocity of the object that hit our water to that node&#8217;s velocity, so that it gets dragged down by the object.
Note: You could change this line to whatever suits you. For instance, you could add the velocity to its current velocity, or you could use momentum instead of velocity and divide by your node&#8217;s mass.
Create 2D Dynamic Water Effects in Unity (Unity3D)
Now we want to make a particle system that&#8217;ll produce the splash. We
it&#8217;s called &#8220;splash&#8221; (creatively enough). Be sure not to confuse it with Splash(). The one I&#8217;ll be using is included in the source files.
First, we want to set the parameters of the splash to change with the velocity of the object.
float lifetime = 0.93f + Mathf.Abs(velocity)*0.07f;
splash.GetComponent&ParticleSystem&().startSpeed = 8+2*Mathf.Pow(Mathf.Abs(velocity),0.5f);
splash.GetComponent&ParticleSystem&().startSpeed = 9 + 2 * Mathf.Pow(Mathf.Abs(velocity), 0.5f);
splash.GetComponent&ParticleSystem&().startLifetime =
Here, we&#8217;ve taken our particles, set their lifetime so they won&#8217;t die shortly after they hit the surface of the water, and set their speed to be based on the square of their velocity (plus a constant, for small splashes).
You may be looking at that code and thinking, &#8220;Why has he set the startSpeed twice?&#8221;, and you&#8217;d be right to wonder that. The problem is, we&#8217;re using a particle system (Shuriken, provided with the project) that has its start speed set to &#8220;random between two constants&#8221;. Unfortunately, we don&#8217;t have much access over Shuriken by scripts, so to get that behaviour to work we have to set the value twice.
Now I&#8217;m going to add a line that you may or may not want to omit from your script:
Vector3 position = new Vector3(xpositions[index],ypositions[index]-0.35f,5);
Quaternion rotation = Quaternion.LookRotation(new Vector3(xpositions[Mathf.FloorToInt(xpositions.Length / 2)], baseheight + 8, 5) &#8211; position);
Shuriken particles won&#8217;t be destroyed when they hit your objects, so if you want to make sure they aren&#8217;t going to land in front of your objects, you can take two measures:
Stick them in the background. (You can tell this by the z-position being 5).
Tilt the particle system to always point towards the center of your body of water—this way, the particles won&#8217;t splash onto the land.
The second line of code takes the midpoint of the positions, moves upwards a bit, and points the particle emitter towards it. I&#8217;ve included this behaviour in the demo. If you&#8217;re using a really wide body of water, you probably don&#8217;t want this behaviour. If your water is in a small pool inside a room, you may well want to use it. So, feel free to scrap that line about rotation.
GameObject splish = Instantiate(splash,position,rotation) as GameO
Destroy(splish, lifetime+0.3f);
Now, we make our splash, and tell it to die a little after the particles are due to die. Why a little afterwards? Because our particle system sends out a few sequential bursts of particles, so even though the first batch only last till Time.time + lifetime, our final bursts will still be around a little after that.
Yes! We&#8217;re finally done, right?
Collision Detection
Wrong! We need to detect our objects, or this was all for nothing!
Remember we added that script to all our colliders before? The one called WaterDetector?
Well we&#8217;re going to make it now! We only want one function in it:
void OnTriggerEnter2D(Collider2D Hit)
Using OnTriggerEnter2D(), we can specify what happens whenever a 2D Rigid Body enters our body of water. If we pass a parameter of Collider2D we can find more information about that object.
if (Hit.rigidbody2D != null)
We only want objects that contain a rigidbody2D.
transform.parent.GetComponent&Water&().Splash(transform.position.x, Hit.rigidbody2D.velocity.y*Hit.rigidbody2D.mass / 40f);
Now, all of our colliders are children of the water manager. So we just grab the Water component from their parent and call Splash(), from the position of the collider.
Remember again, I said you could either pass velocity or momentum, if you wanted it to be more physically accurate? Well here&#8217;s where you have to pass the right one. If you multiply the object&#8217;s y-velocity by its mass, you&#8217;ll have its momentum. If you just want to use its velocity, get rid of the mass from that line.
Finally, you&#8217;ll want to call SpawnWater() from somewhere. Let&#8217;s do it at launch:
void Start()
SpawnWater(-10,20,0,-10);
And now we&#8217;re done! Now any rigidbody2D with a collider that hits the water will create a splash, and the waves will move correctly.
Create 2D Dynamic Water Effects in Unity (Unity3D)
Bonus Exercise
As an extra bonus, I&#8217;ve added a few lines of code to the top of SpawnWater().
gameObject.AddComponent&BoxCollider2D&();
gameObject.GetComponent&BoxCollider2D&().center = new Vector2(Left + Width / 2, (Top + Bottom) / 2);
gameObject.GetComponent&BoxCollider2D&().size = new Vector2(Width, Top &#8211; Bottom);
gameObject.GetComponent&BoxCollider2D&().isTrigger =
These lines of code will add a box collider to the water itself. You can use this to make things float in your water, using what you&#8217;ve learnt.
You&#8217;ll want to make a function called OnTriggerStay2D() which takes a parameter of Collider2D Hit. Then, you can use a modified version of the spring formula we used before that checks the mass of the object, and add a force or velocity to your rigidbody2D to make it float in the water.
Make a Splash
In this tutorial, we implemented a simple water simulation for use in 2D games with simple physics code and a line renderer, mesh renderers, triggers and particles. Perhaps you will add wavy bodies of fluid water as an obstacle to your next platformer, ready for your characters to dive into or carefully cross with floating stepping stones, or maybe you could use this in a sailing or windsurfing game, or even a game where you simply skip rocks across the water from a sunny beach. Good luck!(source:)
CopyRight Since 2010 GamerBoom All rights reserved &&闽ICP备&号-1}

我要回帖

更多关于 虚拟机进入unity模式 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信