前言
本文以unity的StandardAssets中的water资源为基础,实现物体在水中的漂浮效果。
概述
1. 建立场景并添加水详见https://docs.unity3d.com/Manual/HOWTO-Water.html
2. 给水添加碰撞体组件,编写脚本现实物体出入水的检测。
3. 创建立方体预制体,添加刚体组件,碰撞体组件,编辑脚本处理入水后的浮力计算。
实现细节
1. 给水体添加碰撞体
需要注意的是要勾选 Is Trigger ,y方向的范围要控制好,不然可能会出现物体掉到水面一下太多会触发OnTriggerExit() (见脚本部分)。

2. 创建立方体
我创建了一个长宽高分别为1,质量为0.5的立方体,预期效果是在水中漂浮一半露出水面。之后可以通过脚本改变大小及质量验证效果。
3. 水的脚本
水的脚本主要实现OnTriggerEnter和OnTriggerExit两个方法,当其他碰撞体进入水中,触发OnTriggerEnter,将cube中的isInWater置为ture,OnTriggerExit相反。
4. 立方体脚本
立方体脚本的核心是calFloatage方法,计算立方体下表面与水面高度差,通过公式计算浮力,并通过Rigidbody.AddForce()方法施加浮力。
进入水中后将阻力系数调高。
需要注意的是物体完全进入水中后保持h = 立方体高度
5. 代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Floatage : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
/// <summary>
/// OnTriggerEnter is called when the Collider other enters the trigger.
/// </summary>
/// <param name="other">The other Collider involved in this collision.</param>
void OnTriggerEnter(Collider other)
{
if(other.gameObject.GetComponent<Cube>()){
other.gameObject.GetComponent<Cube>().setIsInWater(true);
}
}
/// <summary>
/// OnTriggerExit is called when the Collider other has stopped touching the trigger.
/// </summary>
/// <param name="other">The other Collider involved in this collision.</param>
void OnTriggerExit(Collider other)
{
if(other.gameObject.GetComponent<Cube>()){
other.gameObject.GetComponent<Cube>().setIsInWater(false);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Cube : MonoBehaviour {
private bool isInWater;
private GameObject water;
private float waterY;
private const float floatageForce = 0;
private const float density = 1;
private const float g = 9.8f;
private const float waterDrag = 5;
public bool getIsInWater(){
return isInWater;
}
public void setIsInWater(bool isInWater){
this.isInWater = isInWater;
}
// Use this for initialization
void Start () {
isInWater = false;
water = GameObject.FindWithTag("water");
}
// Update is called once per frame
void Update () {
}
/// <summary>
/// This function is called every fixed framerate frame, if the MonoBehaviour is enabled.
/// </summary>
void FixedUpdate()
{
if(isInWater){
//calculate floatage
calFloatage();
//change darg
GetComponent<Rigidbody>().drag = waterDrag;
}
}
//calculate and add floatage force to the box.
private void calFloatage(){
waterY = water.transform.position.y;
if(waterY>(transform.position.y-transform.localScale.y)){
float h = waterY-(transform.position.y-transform.localScale.y/2)>transform.localScale.y? transform.localScale.y:waterY-(transform.position.y-transform.localScale.y/2);
float floatageForce = density * g *transform.localScale.x*transform.localScale.z*h;
GetComponent<Rigidbody>().AddForce(0,floatageForce,0);
}
}
}
6. 效果
问题
1. 物体出现旋转的话要很久才能停下来,不知道调整什么阻力可以实现更加逼真的效果,或者手动实现。
已经解决。进入水中增大angular drag即可。
小弟第一次写博客,才开始学习unity,望大佬们多多指教!

2319

被折叠的 条评论
为什么被折叠?



