Unity HDRP 2023.2水系统实战:用C#脚本实现物体水面漂浮(附完整代码)
Unity HDRP 2023.2水系统实战用C#脚本实现物体水面漂浮附完整代码当你在Unity中构建一个开放水域场景时让物体随波浪自然起伏是提升沉浸感的关键。Unity 2023.2的HDRP水系统提供了强大的API支持本文将带你深入理解如何通过C#脚本实现高效的水面漂浮效果。1. 环境准备与基础配置在开始编写漂浮脚本前需要确保项目环境正确配置。创建一个新的HDRP项目建议使用Unity 2023.2或更高版本然后按照以下步骤设置水系统在Hierarchy面板右键 →Water→Ocean, Sea, or Lake选中场景中的Sky and Fog Volume添加Water Rendering组件在Project Settings → HDRP → Water中启用Script Interactions关键配置参数说明参数推荐值作用Script Interactions启用允许C#脚本查询水面高度Full Resolution视性能需求全分辨率精度更高但更耗性能Evaluate Ripples启用包含波纹效果更真实// 基础水面检测代码片段 public WaterSurface targetWater; WaterSearchParameters searchParams new WaterSearchParameters(); WaterSearchResult searchResult new WaterSearchResult(); void UpdateWaterSearch() { if(targetWater null) return; searchParams.startPosition searchResult.candidateLocation; searchParams.targetPosition transform.position; searchParams.error 0.01f; searchParams.maxIterations 8; }提示在性能敏感的场景中可以适当降低maxIterations和增大error值来优化性能2. 单物体漂浮实现原理实现单个物体漂浮的核心是每帧查询物体当前位置的水面高度然后调整物体的Y轴位置。Unity提供了FindWaterSurfaceHeight方法来获取精确的水面高度。完整单物体漂浮脚本using UnityEngine; using UnityEngine.Rendering.HighDefinition; [RequireComponent(typeof(Rigidbody))] public class SingleObjectFloater : MonoBehaviour { [Header(Water Reference)] public WaterSurface waterSurface; [Header(Floating Settings)] public float buoyancy 1.0f; public float damping 0.5f; public Vector3 floatingOffset Vector3.zero; private WaterSearchParameters searchParameters new WaterSearchParameters(); private WaterSearchResult searchResult new WaterSearchResult(); private Rigidbody rb; private Vector3 currentVelocity; void Start() { rb GetComponentRigidbody(); if(waterSurface null) Debug.LogError(Water Surface reference is missing!); } void FixedUpdate() { if(waterSurface null) return; // 设置搜索参数 searchParameters.startPosition searchResult.candidateLocation; searchParameters.targetPosition transform.position; searchParameters.error 0.01f; searchParameters.maxIterations 8; // 获取水面高度 if(waterSurface.FindWaterSurfaceHeight(searchParameters, out searchResult)) { float targetHeight searchResult.height floatingOffset.y; float currentHeight transform.position.y; // 计算浮力 float displacement targetHeight - currentHeight; float buoyantForce Mathf.Clamp(displacement, -1f, 1f) * buoyancy; // 应用力 Vector3 force Vector3.up * buoyantForce; rb.AddForce(force, ForceMode.Acceleration); // 阻尼效果 rb.velocity Vector3.SmoothDamp( rb.velocity, new Vector3(rb.velocity.x, 0, rb.velocity.z), ref currentVelocity, damping); } } }关键参数说明buoyancy控制浮力大小值越大物体浮得越高damping运动阻尼避免物体在水面过度晃动floatingOffset微调漂浮位置可用于实现半沉效果3. 多物体批量漂浮优化方案当场景中有大量漂浮物时逐个查询水面高度会导致性能问题。Unity 2023.2引入了WaterSimulationSearchJob可以利用Job System进行并行计算。多物体漂浮系统实现using System.Collections.Generic; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering.HighDefinition; public class MultiObjectFloater : MonoBehaviour { [Header(Water Reference)] public WaterSurface waterSurface; [Header(Floating Objects)] public ListTransform floatingObjects new ListTransform(); public float spacing 2.0f; // Job相关数据 private NativeArrayfloat3 targetPositions; private NativeArrayfloat heightResults; private NativeArrayfloat3 candidatePositions; private NativeArrayfloat errorResults; private NativeArrayint stepCounts; void Start() { InitializeFloatingObjects(); AllocateNativeArrays(); } void InitializeFloatingObjects() { // 示例在网格中生成漂浮物 for(int x 0; x 10; x) { for(int z 0; z 10; z) { var obj GameObject.CreatePrimitive(PrimitiveType.Cube); obj.transform.position new Vector3(x * spacing, 0, z * spacing); floatingObjects.Add(obj.transform); } } } void AllocateNativeArrays() { targetPositions new NativeArrayfloat3( floatingObjects.Count, Allocator.Persistent); heightResults new NativeArrayfloat( floatingObjects.Count, Allocator.Persistent); candidatePositions new NativeArrayfloat3( floatingObjects.Count, Allocator.Persistent); errorResults new NativeArrayfloat( floatingObjects.Count, Allocator.Persistent); stepCounts new NativeArrayint( floatingObjects.Count, Allocator.Persistent); } void Update() { if(waterSurface null) return; // 获取水面模拟数据 WaterSimSearchData simData new WaterSimSearchData(); if(!waterSurface.FillWaterSearchData(ref simData)) return; // 准备位置数据 for(int i 0; i floatingObjects.Count; i) { targetPositions[i] floatingObjects[i].position; } // 创建并调度Job var searchJob new WaterSimulationSearchJob() { simSearchData simData, targetPositionBuffer targetPositions, startPositionBuffer targetPositions, maxIterations 8, error 0.01f, heightBuffer heightResults, errorBuffer errorResults, candidateLocationBuffer candidatePositions, stepCountBuffer stepCounts }; JobHandle handle searchJob.Schedule( floatingObjects.Count, 32); // 每批处理32个 handle.Complete(); // 应用高度结果 for(int i 0; i floatingObjects.Count; i) { Vector3 pos floatingObjects[i].position; floatingObjects[i].position new Vector3( pos.x, heightResults[i], pos.z); } } void OnDestroy() { // 释放NativeArray内存 targetPositions.Dispose(); heightResults.Dispose(); candidatePositions.Dispose(); errorResults.Dispose(); stepCounts.Dispose(); } }性能优化技巧适当增加Schedule的batchSize参数如32或64对于静态物体可以降低更新频率如每2-3帧更新一次使用LOD系统远距离物体使用简化的漂浮计算4. 高级效果与疑难解决4.1 添加波浪阻力效果让漂浮物不仅上下运动还能随波浪水平移动// 在SingleObjectFloater脚本中添加 [Header(Wave Resistance)] public float waveInfluence 0.5f; public float rotationInfluence 0.3f; private Vector3 lastWaterNormal; void FixedUpdate() { // ...原有高度计算代码... // 添加波浪影响 Vector3 waterCurrent searchResult.candidateLocation - transform.position; rb.AddForce(waterCurrent * waveInfluence, ForceMode.Acceleration); // 模拟旋转效果 Quaternion targetRotation Quaternion.FromToRotation( Vector3.up, searchResult.normal); transform.rotation Quaternion.Slerp( transform.rotation, targetRotation, rotationInfluence); }4.2 常见问题排查问题1物体抖动或不稳定解决方案增加damping值或降低FixedUpdate的频率问题2漂浮物穿过水面检查物体是否有合适的Collider确保Rigidbody的mass值不过大问题3性能开销大对于远处物体使用简化的漂浮计算考虑使用Object Pooling重用漂浮物4.3 与物理系统的交互实现更真实的漂浮物理[Header(Advanced Physics)] public float submergedVolume 1.0f; public float waterDensity 1.0f; void FixedUpdate() { // ...原有代码... // 基于阿基米德原理的浮力计算 float submergedPercent Mathf.Clamp01( (targetHeight - transform.position.y) / transform.localScale.y); float displacedMass submergedPercent * submergedVolume * waterDensity; Vector3 archimedesForce Physics.gravity * -displacedMass; rb.AddForce(archimedesForce, ForceMode.Force); }5. 实战案例船只漂浮系统结合以上技术我们可以实现一个完整的船只漂浮系统public class BoatFloater : MonoBehaviour { public WaterSurface waterSurface; public Transform[] floatPoints; // 船体浮力点 public float floatStrength 1f; public float rotationSpeed 2f; private Rigidbody boatRigidbody; private float3[] targetPositions; private NativeArrayfloat heightResults; void Start() { boatRigidbody GetComponentRigidbody(); targetPositions new float3[floatPoints.Length]; heightResults new NativeArrayfloat( floatPoints.Length, Allocator.Persistent); } void FixedUpdate() { if(waterSurface null) return; WaterSimSearchData simData new WaterSimSearchData(); if(!waterSurface.FillWaterSearchData(ref simData)) return; // 准备所有浮力点的位置 for(int i 0; i floatPoints.Length; i) { targetPositions[i] floatPoints[i].position; } // 执行并行水面查询 var searchJob new WaterSimulationSearchJob() { simSearchData simData, targetPositionBuffer new NativeArrayfloat3( targetPositions, Allocator.TempJob), startPositionBuffer new NativeArrayfloat3( targetPositions, Allocator.TempJob), maxIterations 8, error 0.01f, heightBuffer heightResults, errorBuffer new NativeArrayfloat( floatPoints.Length, Allocator.TempJob), candidateLocationBuffer new NativeArrayfloat3( floatPoints.Length, Allocator.TempJob), stepCountBuffer new NativeArrayint( floatPoints.Length, Allocator.TempJob) }; JobHandle handle searchJob.Schedule( floatPoints.Length, 64); handle.Complete(); // 计算总浮力和旋转 Vector3 totalForce Vector3.zero; Vector3 totalTorque Vector3.zero; for(int i 0; i floatPoints.Length; i) { float floatDelta heightResults[i] - floatPoints[i].position.y; float forceFactor Mathf.Clamp(floatDelta, -1f, 1f) * floatStrength; Vector3 force floatPoints[i].up * forceFactor; totalForce force; // 计算旋转扭矩 Vector3 torqueDir Vector3.Cross( floatPoints[i].right, force.normalized); totalTorque torqueDir * force.magnitude; } // 应用力和扭矩 boatRigidbody.AddForce(totalForce, ForceMode.Acceleration); boatRigidbody.AddTorque( totalTorque * rotationSpeed * Time.fixedDeltaTime, ForceMode.Acceleration); // 清理临时分配的内存 searchJob.targetPositionBuffer.Dispose(); searchJob.startPositionBuffer.Dispose(); searchJob.errorBuffer.Dispose(); searchJob.candidateLocationBuffer.Dispose(); searchJob.stepCountBuffer.Dispose(); } void OnDestroy() { heightResults.Dispose(); } }这个船只系统通过多个浮力点计算整体浮力和旋转扭矩实现了更真实的物理行为。在实际项目中可以根据需要调整浮力点的数量和位置或者添加额外的阻力系数来模拟不同船型的水动力学特性。