转自:http://blog.sina.com.cn/s/blog_89d90b7c0102vpij.html
上一节学习了如何让顶点动起来,形成水的效果,那问题又来了,有物体掉入水中怎么办?这可不是直接掉在地上那么简单哦(说简单是因为直接使用Unity的刚体),是不是就得模拟漂浮物的效果呢?就好比下面这个效果:
简单的记录几个关键点:
1.先获取物体所包含的碰撞体,在碰撞体的范围内生成若干浮力盒,然后根据浮力盒的位置,给当前位置施加推力。
2.其中涉及到浮力的概念,当物体的浮力m_density小于水的浮力(设置1000)时,物体才会浮起来。
3.物体的阻力和角阻力,当物体在水中时,阻力相对较大,所以变化速度比较慢。
代码如下:
usingSystem;
usingUnityEngine;
usingSystem.Collections.Generic;
namespaceLin
{
[RequireComponent(typeof(Rigidbody))]
publicclassBuoyancySetting:MonoBehaviour
{
privateCreateMeshm_water;
privateTransformm_trans;
privateRigidbodym_rigidbody;
//原代码中碰撞体是集合
//可以获取子对象的碰撞体生成不规则的浮力盒
//比如残破的船只等
privateColliderm_collider;
publicfloatm_boxDensity=2;//浮力盒的密度
privateBoundsm_bounds;
privatefloatm_boxSize=0;//格子大小
privateVector3m_boxCount=Vector3.one;//XYZ轴各方向的格子个数
privateBuoyancyBox[]m_buoyancyBoxs;//浮力盒集合
privatestructBuoyancyBox
{
//浮力盒在物体坐标的位置
publicVector3Position;
//是否在边界
publicboolIsOnColliderEdge;
}
//物体密度,当物体密度小于水密度时才会浮起来
publicfloatm_density=750f;
//流动时的阻力和角阻力,阻力越大运动越慢,越不明显
publicfloatm_inWaterDrag=1;
publicfloatm_inWaterAngularDrag=1;
//物体初始化时的阻力和角阻力
privatefloatm_initDrag;
privatefloatm_initAngularDrag;
privatevoidStart()
{
m_trans=transform;
m_water=GameObject.Find("water").GetComponent();
m_rigidbody=GetComponent();
m_collider=GetComponent();
m_initDrag=m_rigidbody.drag;
m_initAngularDrag=m_rigidbody.angularDrag;
InitBoxs();
}
//初始化浮力盒
privatevoidInitBoxs()
{
QuaternionoriginalRotation=m_trans.rotation;
Vector3originalPosition=m_trans.position;
//旋转和位置归零
m_trans.rotation=Quaternion.identity;
m_trans.position=Vector3.zero;
//计算浮力盒大小
m_bounds.Encapsulate(m_collider.bounds);
m_boxSize=m_bounds.size.magnitude/m_boxDensity/2;
//XYZ各方向的【浮力盒个数】=边界长度/单位大小
m_boxCount.x=Mathf.RoundToInt(m_bounds.size.x/m_boxSize)+1;
m_boxCount.y=Mathf.RoundToInt(m_bounds.size.y/m_boxSize)+1;
m_boxCount.z=Mathf.RoundToInt(m_bounds.size.z/m_boxSize)+1;
m_buoyancyBoxs=SliceIntoVoxels().ToArray();
//还原物体位置
m_trans.rotation=originalRotation;
m_trans.position=originalPosition;
m_boxSize=Mathf.Pow(m_bounds.size.x*m_bounds.size.y*m_bounds.size.z/
(m_boxCount.x*m_boxCount.y*m_boxCount.z),1f/3f);
}
privateListSliceIntoVoxels()
{
ListboxList=newList((int)(m_boxCount.x*m_boxCount.y*m_boxCount.z));
for(intix=0;ix
{
for(intiy=0;iy
{
for(intiz=0;iz
{
floatx=m_bounds.min.x+m_bounds.size.x/m_boxCount.x*(0.5f+ix);
floaty=m_bounds.min.y+m_bounds.size.y/m_boxCount.y*(0.5f+iy);
floatz=m_bounds.min.z+m_bounds.size.z/m_boxCount.z*(0.5f+iz);
//获取当前每个格子的位置
Vector3point=newVector3(x,y,z);
//如果格子的位置在包围盒内,就添加到列表
if(ColliderTools.IsPointInsideCollider(m_collider,point))
{
BuoyancyBoxbuoyancyBox;
//转到物体坐标
buoyancyBox.Position=m_trans.InverseTransformPoint(point);
//在包围盒的边缘
buoyancyBox.IsOnColliderEdge=ColliderTools.IsPointAtColliderEdge(m_collider,point,m_boxSize);
boxList.Add(buoyancyBox);
}
}
}
}
returnboxList;
}
privatevoidFixedUpdate()
{
//浮力增量大于0时,说明物体在水里,那么速度和旋转的变换是很慢的
floatm_boxUpDelta=0;
//比容就是密度的倒数
//比容就是单位质量的体积
floatVFactor=1f/m_density;
//公式:重力G=g*质量m
//计算单位质量的体积所受重力,这里理解为单位体积所受的重力
floatunitVG=-Physics.gravity.y*VFactor;
//公式:体积=质量/密度
//计算当前物体在水中的体积
floatV=m_water._density*m_water._density*m_rigidbody.mass/m_water._density*Time.deltaTime;
//总施加力=单位体积的重力*总体积
//给每一个浮力盒的力
Vector3_singleForce=Vector3.up*(unitVG*V/m_buoyancyBoxs.Length);
//浮力盒个数
intboxLength=m_buoyancyBoxs.Length;
for(inti=0;i
{
//获取浮力盒在物体中的位置
Vector3point=m_buoyancyBoxs[i].Position;
//获取浮力盒在世界坐标的位置
Vector3wPoint=m_trans.TransformPoint(point);
//获取当前位置水面高度
floatwaterHeight=m_water.GetWaterLevel(wPoint.x,wPoint.z);
if(waterHeight!=float.NegativeInfinity&&(wPoint.y-m_boxSize/1f
{
//k=1浮力盒完全在水里面,k=0浮力盒完全在水外面
floatk=(waterHeight-wPoint.y)/(2f*m_boxSize)+0.5f;
if(k>1f)
k=1f;
elseif(k<0f)
k=0f;
//在水中浮力盒的个数
m_boxUpDelta+=k;
//计算最终施加给物体的力
Vector3lastForce=k*_singleForce;
//在水里面才需要添加一个力到刚体,使用质量
m_rigidbody.AddForceAtPosition(lastForce,wPoint,ForceMode.Impulse);
}
}
//当浮力盒在水里面的时候,阻力+1,阻力变大,物体下降就变慢
//当浮力盒都在水外面的时候,使用较小的阻力,物体下降就快
m_boxUpDelta/=boxLength;
m_rigidbody.drag=Mathf.Lerp(m_rigidbody.drag,m_boxUpDelta>0.0001f?m_initDrag+m_inWaterDrag:m_initDrag,15f*Time.deltaTime);
m_rigidbody.angularDrag=Mathf.Lerp(m_rigidbody.angularDrag,m_boxUpDelta>0.0001f?m_initAngularDrag+m_inWaterAngularDrag:m_initAngularDrag,15f*Time.deltaTime);
}
privatevoidOnDrawGizmos()
{
Gizmos.DrawIcon(transform.position,"DynamicWater/BuoyancyForce.png");
if(!Application.isEditor||m_buoyancyBoxs==null)
{
return;
}
Vector3gizmoSize=Vector3.one*m_boxSize;
Gizmos.color=newColor(Color.yellow.r,Color.yellow.g,Color.yellow.b,0.5f);
foreach(varpinm_buoyancyBoxs)
{
Gizmos.DrawCube(transform.TransformPoint(p.Position),gizmoSize);
}
Gizmos.color=Color.red;
Gizmos.DrawSphere(rigidbody.worldCenterOfMass,m_boxSize/2f);
}
}
publicstaticclassColliderTools
{
publicstaticboolIsPointInsideCollider(Collidercollider,Vector3point)
{
RaycastHithit;
#if!UNITY_FLASH
if(colliderisTerrainCollider)
{
if(!collider.Raycast(newRay(point,Vector3.up),outhit,collider.bounds.size.y))
{
returnfalse;
}
}
else
#endif
if(colliderisMeshCollider&&!((MeshCollider)collider).convex)
{
if(!IsPointInsideMeshCollider(collider,point))
{
returnfalse;
}
}
else
{
Vector3direction=collider.bounds.center-point;
floatdirectionMagnitude=direction.magnitude;
if(directionMagnitude>0.01f&&
collider.Raycast(newRay(point,direction.normalized),outhit,directionMagnitude))
{
returnfalse;
}
}
returntrue;
}
publicstaticboolIsPointInsideMeshCollider(Collidercollider,Vector3point)
{
Vector3[]directions={Vector3.up,Vector3.down,Vector3.left,Vector3.right,Vector3.forward,Vector3.back};
foreach(varrayindirections)
{
RaycastHithit;
if(collider.Raycast(newRay(point-ray*1000f,ray),outhit,1000f)==false)
{
returnfalse;
}
}
returntrue;
}
publicstaticboolIsPointAtColliderEdge(Collidercollider,Vector3point,floattolerance)
{
RaycastHithit;
tolerance*=0.71f;//Approximately1/sqrt(2)
Vector3direction=collider.bounds.center-point;
Vector3directionNormalized=direction.normalized;
boolresult=direction!=Vector3.zero&&
collider.Raycast(newRay(point-directionNormalized*tolerance,directionNormalized),
outhit,tolerance);
returnresult;
}
}
}
注:可以设置物体密度和阻力达到其他效果,比如
:
以上例子需要上一节内容才能运行:
http://blog.sina.com.cn/s/blog_89d90b7c0102vpa3.html
学习来源: