前言

为了给程序化河流加入浮力系统,前面搞了个浮力插件粗略研究了一下。也写了一篇简要的文章总结了下:

就是爱折腾:NaughtyWaterBuoyancy浮力插件解析​zhuanlan.zhihu.com

实际测试后,发现这个插件并不能直接用到之前河流的网格上。效果上最明显的是这个水效果不受水面倾斜的影响,如图。当水往下流时,物体还在原处漂浮,显然违反自然规律。不过这个问题很好解决:直接给物体在水流方向再加一个力水流的推力就行了。后期还遇到了一个问题,由于这个水系统需要漂浮物碰撞体设置为Trigger类型。所以当漂浮物碰到河岸边界时会冲出去(物体的碰撞类型是trigger),对于这个问题,我的解决办法是通过自己写些方法来限制漂浮物的运动。

水平面倾斜,物体还是再原处浮动

解决问题

首先附上之前创建程序化河流的文章的链接,可以先看下这篇:

就是爱折腾:Unity程序化河流生成​zhuanlan.zhihu.com

为了解决第一个问题,我首先将浮力插件的核心功能脚本,也就是FloatingObject.cs和WaterVolume.cs提取出来。然后对WaterVolume.cs进行大幅简化,去掉了网格顶点分析的部分。接着开始对FloatingObject.cs的核心部分FixUpdate进行修改:

  1. 水面法线和水面高度计算的简化。

原始插件计算水面的法线和水面的高度使用的水面网格数据来计算的,这里偷懒(考虑到河流网格的顶点不会太多),直接从体元的正上方一定距离向下发射射线,得到RaycastHit,这里面就已经包含了碰撞点和碰撞点的法线信息了。这样WaterVolume.cs中的很多代码就都可以去掉了。

//hit中就包含了法线和碰撞点
RaycastHit hit = this.GetHitInfoOnWater (worldPoint);
float waterLevel = hit.point.y;//从水面上方垂直向下发射射线,得到和水面的交点public RaycastHit GetHitInfoOnWater (Vector3 worldPoint) {Vector3 _originUP = worldPoint + Vector3.up * 1000;Ray _rayDown = new Ray (_originUP, Vector3.down);RaycastHit hit;if (Physics.Raycast (_rayDown, out hit, Mathf.Infinity,1<<LayerMask.NameToLayer("Water"))) {return hit;}return hit;}

2.给物体添加推力。

在创建河流的网格的时候,我们给每个河流网格都添加了一个RiverInstance。用来存储河流的基本信息。

==============================RiverInstance.cs==============================
public class RiverSegInfo {public float LengthRadio;public float halfWidth;public Vector3 flowDir;public Vector3 center;
}
================================River.cs========================================
//记录河流宽度信息
m_riverSegmentInfos.Add (new RiverSegInfo () {LengthRadio = (float) i / _resultWayPoints.Count,//当前位置所处河流长度占河流总长度比率halfWidth = _halfRiverWidth,//当前位置和半宽flowDir = _vetexOffset.normalized,//水流流向center = _resultWayPoints[i]//河流路径中心}
);
//保存每条河流的必要数据
m_riverInstance.riverSegmentInfos = this.m_riverSegmentInfos;//保存每条河流的必要数据
m_riverInstance.riverUVWrapAmount = _uvWrap;//河流UVY大小
m_riverInstance.riverLength = _riverLength;//河流总长度
m_riverInstance.segmentAmount = _resultWayPoints.Count - 1;//河流总节点
m_riverInstance.riverDepth = riverDepth;//河流深度

其中就包括指定河流位置河流的流向信息flowDir ,河流网格UV.y的大小_uvWrap。由此,通过RaycastHit中的hit.texcoord.y信息就可以反推出体元当前所在位置的水流流向flowDir 。

==============================RiverInstance.cs======================================
//RiverInstance类方法
public RiverSegInfo GetFlowSegmentInfo (float UVY) {if (UVY > 0 && riverSegmentInfos.Count > 0) {float _lengthUVYRadio = UVY / riverUVWrapAmount;float _lengthRadioPerSegment = 1f / segmentAmount;foreach (var segInfo in riverSegmentInfos) {if (Mathf.Abs (segInfo.LengthRadio - _lengthUVYRadio) < _lengthRadioPerSegment * 2) {// Debug.Log(segInfo.LengthRadio + " " + _lengthUVYRadio + " " + segInfo.center);return segInfo;}}}return null;}
================================FloatObject.cs=================================
//由hit.textureCoord.y反推水流流向flowDirection
segmentInfo = river.GetFlowSegmentInfo (hit.textureCoord.y);
Vector3 flowDirection = segmentInfo.flowDir;

有了水流流向,配合该位置水面网格的法线我们就可以计算水流推力了。这里我使用(1.2 -Dot(N,y))作为因子来调整推力大小(水面倾斜度越大,推力越大,水完全水平时,依然会缓慢向水流方向前进),最终得到推力currentVoxelWaterForce。最后将推力与原来的浮力叠加。至此推力添加完成。物体开始沿着河流移动。

//这里1.2f - ndotUp是确保当物体处于完全水平的水面时也能向水流方向运动
currentVoxelWaterForce = (1.2f - ndotUp) * flowDirection * water.baseWaterPushForceRadio;
Quaternion surfaceRotation = Quaternion.FromToRotation (this.water.transform.up, (surfaceNormal + flowDirection).normalized);
//沉入水中越少,朝水面法线偏移越厉害,抖动的越厉害
surfaceRotation = Quaternion.Slerp (surfaceRotation, Quaternion.identity, submergedFactor);Vector3 finalVoxelForce = surfaceRotation * ((forceAtSingleVoxel + currentVoxelWaterForce) * submergedFactor);
//添加力
this.rigidbody.AddForceAtPosition (finalVoxelForce, worldPoint);

白线即为水流推力方向

3.解决物体冲出河流的问题。

物体冲出河流的问题是必然的,因为地形碰撞体其实已经完全没起作用了。这里我用两个方式来解决了一下(这两个方式都不是精确的,只是大概达到了效果)。

首先在物体的OnTriggerEnter被触发时,当检测到物体是与地形网格碰撞。我将给物体在碰撞的瞬间给物体设置一个反弹速度从而让物体反弹回来并继续往下游前进。如下图:当前物体运动速度为V,水流推力为W,于是给物体添加的力即为VW

protected virtual void OnTriggerEnter (Collider other) {//......this.rigidbody.velocity = (currentVoxelWaterForce - rigidbody.velocity) * boundRadio;}}

单纯这样还是不能解决问题,物体偶尔还是会钻出去。于是后面又加了一个操作:持续给物体添加一个拽向河流中心的力,力的大小根据物体偏离河流中心的大小来调整。通过挑取合适的因子,终于达到了可以接受的效果。

 //防止物体冲出河道
if(segmentInfox != null){//与河中心的偏移值
Vector3 offset = transform.position - (segmentInfox.center + river.riverDepth * Vector3.down);
///将物体拉回河流中心
this.rigidbody.AddForce(-offset *  centerDragForceRadio,ForceMode.Impulse);}

最后

通过上面的操作,河流的浮力系统终于改造完成了。最终结果:

Git:

https://gitee.com/jiuyueqiji123/NextGENProject/tree/master/NextGENProject/Assets/Samples/%E6%B2%B3%E6%B5%81%E6%B0%B4%E6%95%88%E6%9E%9C​gitee.com

最后贴下FloatObject.cs完整的FixUpdate代码,方便对照。

protected virtual void FixedUpdate () {if (this.water != null && this.voxels.Length > 0) {//将总共的浮力分到每个体元Vector3 forceAtSingleVoxel = this.CalculateMaxBuoyancyForce () / this.voxels.Length;Bounds bounds = this.collider.bounds;//每个体元的高度float voxelHeight = bounds.size.y * this.normalizedVoxelSize;float submergedVolume = 0f;RiverSegInfo segmentInfo = null;for (int i = 0; i < this.voxels.Length; i++) {Vector3 worldPoint = this.transform.TransformPoint (this.voxels[i]);//获取水深度RaycastHit hit = this.GetHitInfoOnWater (worldPoint);float waterLevel = hit.point.y;//体元的深度(体元底部到水面)float deepLevel = waterLevel - worldPoint.y + (voxelHeight / 2f);// 0 - 完全出了水面  1 - 完全沉入水中                 float submergedFactor = Mathf.Clamp (deepLevel / voxelHeight, 0f, 1f);submergedVolume += submergedFactor;//水面的法线Vector3 surfaceNormal = hit.normal;//水对物体的推力计算float ndotUp = Mathf.Clamp01 (Vector3.Dot (surfaceNormal, Vector3.up));segmentInfo = river.GetFlowSegmentInfo (hit.textureCoord.y);if (segmentInfo != null) {Vector3 flowDirection = segmentInfo.flowDir;//这里1.2f - ndotUp是确保当物体处于完全水平的水面时也能向水流方向运动currentVoxelWaterForce = (1.2f - ndotUp) * flowDirection * water.baseWaterPushForceRadio;Quaternion surfaceRotation = Quaternion.FromToRotation (this.water.transform.up, (surfaceNormal + flowDirection).normalized);//沉入水中越少,朝水面法线偏移越厉害,抖动的越厉害surfaceRotation = Quaternion.Slerp (surfaceRotation, Quaternion.identity, submergedFactor);Vector3 finalVoxelForce = surfaceRotation * ((forceAtSingleVoxel + currentVoxelWaterForce) * submergedFactor);//添加力this.rigidbody.AddForceAtPosition (finalVoxelForce, worldPoint);Debug.DrawLine (worldPoint, worldPoint + finalVoxelForce.normalized, Color.blue);Debug.DrawLine (worldPoint, worldPoint + flowDirection);}}submergedVolume /= this.voxels.Length; // 0 - object is fully out of the water, 1 - object is fully submergedthis.rigidbody.drag = Mathf.Lerp (this.initialDrag, this.dragInWater, submergedVolume);this.rigidbody.angularDrag = Mathf.Lerp (this.initialAngularDrag, this.angularDragInWater, submergedVolume);RaycastHit hitx = this.GetHitInfoOnWater (transform.position);RiverSegInfo segmentInfox = river.GetFlowSegmentInfo (hitx.textureCoord.y);//防止物体冲出河道if(segmentInfox != null){Vector3 offset = transform.position - (segmentInfox.center + river.riverDepth * Vector3.down);this.rigidbody.AddForce(-offset *  centerDragForceRadio,ForceMode.Impulse);Debug.DrawLine(transform.position,(segmentInfox.center + river.riverDepth * Vector3.down));}}}

水中浮力插件buoyancy_程序化河流后续——加入浮力系统相关推荐

  1. 水中浮力插件buoyancy_NaughtyWaterBuoyancy浮力插件解析

    为了给河流加浮力系统,于是我搞了个浮力插件来研究了下.插件是开源的,地址: dbrizov/NaughtyWaterBuoyancy​github.com 两个重要的脚本是WaterVolume.cs ...

  2. Linux编译mybatis,使用mybatis assembly插件打成tar包,在linux系统中运行服务-Go语言中文社区...

    使用mybatis assembly插件打成tar包,在linux系统中运行服务 assembly插件插件地址: 链接:https://pan.baidu.com/s/1i6bWPxF 密码:gad5 ...

  3. 使用Unity5开发手游,使用FMOD或wwise音频插件而不是自带的声音系统有哪些好处?

    使用Unity5开发手游,使用FMOD或wwise音频插件而不是自带的声音系统有哪些好处? https://www.zhihu.com/question/61882604 碰巧对Unity里的声音脚本 ...

  4. 后续升级鸿蒙系统,荣耀部分机型后续将支持升级为鸿蒙系统

    华为鸿蒙2.0发布.华为消费者业务软件部总裁王成录在发布会现场表示,华为的手机.可穿戴设备.平板很快也将应用鸿蒙系统. 在发布会现场,华为还发布了EMUI 11.记者从荣耀获悉,荣耀Magic UI ...

  5. 校园安防之高清IP摄像头全终端无插件直播视频流媒体服务EasyNVR校园监控系统方案

    校园安全逐渐成为社会关注热点,校园监控是目前保证校园安全与学生安全及事后查证的科技手段.如何有效维护校园秩序和安全,保证学校教学.科研工作的顺利进行,以及为同学们的生活和学习营造更好的安全环境,已成为 ...

  6. Nx 介绍: 基于插件的单一代码库(Monorepo)构建系统

    文章目录 前言 一.Nx 设计理念 二.Nx 核心概念 1. 项目图 - Project graph 2.元数据驱动 - Metadata driven 3. 任务图 - Task graph 4.受 ...

  7. mate30后续用鸿蒙系统,mate30可以升级鸿蒙不?升级后还能退回原系统吗?

    [分享交流] mate30可以升级鸿蒙不?升级后还能退回原系统吗? 16646 电梯直达 huafans01249991893 略有小成 发表于 2020-12-18 18:16:10 来自:HUAW ...

  8. mixin机制 vue_读?VuePress(四)插件机制

    前言 从 9 月份开始,vuepress 源码进行了重新设计和拆分.先是开了个 next 分支,后来又合并到 master 分支,为即将发布的 1.x 版本做准备. 最主要的变化是:大部分的全局功能都 ...

  9. .net 插件式开发学习总结

    .NET简谈插件系统开发模式 今天跟大家分享一下我们在日常开发中并不常用的开发模式"插件系统模式",什么叫插件从大一点的概念讲就是我们开发的软件是由很小的模块组成,每一块都能成功的 ...

最新文章

  1. 获取返回值作为变量_解决多线程间共享变量线程安全问题的大杀器——ThreadLocal...
  2. 分析轮子(二)- ,, (左移、右移、无符号右移)
  3. python爬虫人门(10)Scrapy框架之Downloader Middlewares
  4. 知识产权界福布斯排行榜公布:厉害了,我的中国!
  5. 如何系统的自学python 知乎-如何系统地自学 Python?
  6. Mybatis生成器插件扩展,定制方法生成,list参数生成
  7. Spring Data JPA 从入门到精通~查询方法的创建
  8. mysql 启动 配置文件,mysql启动服务配置文件编写
  9. 全网最全的AItium Designer 16下载资源与安装步骤
  10. fastadmin常规错误排查
  11. 计算机毕业设计(附源码)python-志愿者管理系统
  12. 首页banner广告图片轮换超炫效果代码
  13. MODBUS RTU 协议读卡器
  14. 185电缆的接法图解_三相电缆线的接法图解
  15. 平面直角系【坐标系旋转】、【点绕坐标系旋转】、【A点绕B点旋转】
  16. 【动漫风格迁移】基于AnimeGAN的安卓APP工具
  17. NOIP前的刷题记录
  18. 今日添加ubuntu7.10配置
  19. Leetcode 1134:阿姆斯特朗数(超详细的解法!!!)
  20. 利用蒲公英自动更新APP及其更新机制

热门文章

  1. 那些年黑了你的微软BUG
  2. 移动Web利器transformjs入门
  3. mount error 12 = Cannot allocate memory
  4. linux系统软Raid高可用配置
  5. 关于布局管理器FlowLayout的思考:如何让FlowLayout自动换行(3)
  6. spring配置mysql事务管理_Spring 数据库事务管理机制
  7. 人机交互大作业_为百亿级未来布局 徐工XG新一代高空作业设备全球首发
  8. mysql 5.6 64位解压版_MySQL 5.6 for Windows 解压缩版配置安装(win 10 64位亲测)附安装包下载链接...
  9. SpringSecurity-1-UserDetails接口
  10. 迷你世界无限迷你币体验服务器,迷你世界体验服无限迷你币