Here is updated and better code. I like the results.
The Physics Body has linear and angular damping set to 1 for objects. Mass is pretty high such as 1240000 for boat. Gravity is 1.
using System.Collections.Generic;
using Unity.Entities;
using Unity.Physics;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Unity.Physics.Extensions;
using Unity.Entities.UniversalDelegates;
using Crest;
using UnityEditor.Build.Pipeline;
namespace DOTSActorWater
{
[UpdateAfter(typeof(Unity.Physics.Systems.StepPhysicsWorld))]
public class ActorWaterBuoyancySimpleSystem : ComponentSystem
{
private SampleHeightHelper sampleHeightHelper;
private SampleFlowHelper sampleFlowHelper;
private Vector3 _displacementToObject;
private float3 position;
private Vector3 normal;
private Vector3 waterSurfaceVel;
private Vector3 undispPos;
private Vector3 dispPos;
private float bottomDepth;
private Dictionary<int, SampleHeightHelper> sampleHeightHelpers = new Dictionary<int, SampleHeightHelper>();
private Dictionary<int, SampleHeightHelper> sampleHeightLengthHelpers = new Dictionary<int, SampleHeightHelper>();
private Dictionary<int, SampleFlowHelper> sampleFlowHelpers = new Dictionary<int, SampleFlowHelper>();
private float deltaTime;
protected override void OnUpdate()
{
deltaTime = Time.DeltaTime;
CrestScanner();
DoBuoyancy();
}
private void CrestScanner()
{
//Check For Ocean Rendrer
if (OceanRenderer.Instance == null)
return;
var collProvider = OceanRenderer.Instance.CollisionProvider;
var deltaTime = Time.DeltaTime;
Entities.ForEach((Entity entity, ref ActorActionWaterBuoyancySimpleComponent actorActionWaterBuoyancyComponent, ref Translation translation, ref LocalToWorld localToWorld) =>
{
position = translation.Value;
normal = Vector3.up;
waterSurfaceVel = Vector3.zero;
_displacementToObject = Vector3.zero;
if (!sampleHeightHelpers.ContainsKey(entity.Index))
{
sampleHeightHelpers.Add(entity.Index, new SampleHeightHelper());
sampleHeightLengthHelpers.Add(entity.Index, new SampleHeightHelper());
sampleFlowHelpers.Add(entity.Index, new SampleFlowHelper());
}
sampleFlowHelper = sampleFlowHelpers[entity.Index];
sampleHeightHelper = sampleHeightHelpers[entity.Index];
sampleHeightHelper.Init(position, actorActionWaterBuoyancyComponent.objectWidth);
sampleHeightHelper.Sample(ref _displacementToObject, ref normal, ref waterSurfaceVel);
if (QueryFlow.Instance)
{
sampleFlowHelper.Init(position, actorActionWaterBuoyancyComponent.objectWidth);
Vector2 surfaceFlow = Vector2.zero;
sampleFlowHelper.Sample(ref surfaceFlow);
waterSurfaceVel += new Vector3(surfaceFlow.x, 0, surfaceFlow.y);
}
actorActionWaterBuoyancyComponent.waterSurfaceVelocity = waterSurfaceVel;
actorActionWaterBuoyancyComponent.waterSurfaceNormal = normal;
undispPos = (Vector3)position - _displacementToObject;
undispPos.y = OceanRenderer.Instance.SeaLevel;
dispPos = undispPos + _displacementToObject;
//Get Bottom Of Object
bottomDepth = dispPos.y - position.y + actorActionWaterBuoyancyComponent.buoyancyBottomHeight;
actorActionWaterBuoyancyComponent.bottomDepth = bottomDepth;
actorActionWaterBuoyancyComponent.disabled = bottomDepth < 0;
sampleHeightLengthHelpers[entity.Index].Init(position, actorActionWaterBuoyancyComponent.objectLength);
var dummy = 0f;
var normalLongitudinal = Vector3.up;
if (sampleHeightLengthHelpers[entity.Index].Sample(ref dummy, ref normalLongitudinal))
{
var F = (Vector3)localToWorld.Forward;
F.y = 0f;
F.Normalize();
normal -= Vector3.Dot(F, normal) * F;
var R = (Vector3)localToWorld.Right;
R.y = 0f;
R.Normalize();
normalLongitudinal -= Vector3.Dot(R, normalLongitudinal) * R;
actorActionWaterBuoyancyComponent.normalLongitudinal = normalLongitudinal;
}
});
}
private void DoBuoyancy()
{
var forces = new List<float3>();
var physicsWorld = World.GetExistingSystem<Unity.Physics.Systems.BuildPhysicsWorld>().PhysicsWorld;
Entities.ForEach((Entity entity, ref ActorActionWaterBuoyancySimpleComponent actorActionWaterBuoyancyComponent, ref Translation translation, ref Rotation rotation, ref LocalToWorld localToWorld, ref PhysicsVelocity physicsVelocity, ref PhysicsMass physicsMass) =>
{
if (actorActionWaterBuoyancyComponent.bottomDepth > 0 && !actorActionWaterBuoyancyComponent.disabled)
{
//Buoyancy
var bottomDepth = actorActionWaterBuoyancyComponent.bottomDepth;
var buoyancy = -Physics.gravity.normalized * actorActionWaterBuoyancyComponent.buoyancyCoeff * bottomDepth * bottomDepth * bottomDepth;
physicsVelocity.Linear += (float3)buoyancy * deltaTime;
//Get Forces
var velocityRelativeToWater = physicsVelocity.Linear - actorActionWaterBuoyancyComponent.waterSurfaceVelocity;
var forcePosition = translation.Value + actorActionWaterBuoyancyComponent.forceHeightOffset * (float3)Vector3.up;
forces.Add(Vector3.up * Vector3.Dot(Vector3.up, -velocityRelativeToWater) * actorActionWaterBuoyancyComponent.dragInWaterUp);
forces.Add(localToWorld.Right * Vector3.Dot(localToWorld.Right, -velocityRelativeToWater) * actorActionWaterBuoyancyComponent.dragInWaterRight);
forces.Add(localToWorld.Forward * Vector3.Dot(localToWorld.Forward, -velocityRelativeToWater) * actorActionWaterBuoyancyComponent.dragInWaterForward);
//Apply Forces
for (int i = 0; i < forces.Count; i++)
physicsVelocity.ApplyImpulse(physicsMass, translation, rotation, forces[i], forcePosition);
//Torque
{
var torqueWidth = (float3)Vector3.Cross(localToWorld.Up, actorActionWaterBuoyancyComponent.waterSurfaceNormal);
var torqueLength = (float3)Vector3.Cross(localToWorld.Up, actorActionWaterBuoyancyComponent.normalLongitudinal);
physicsVelocity.Angular += torqueWidth * actorActionWaterBuoyancyComponent.boyancyTorque * deltaTime;
physicsVelocity.Angular += torqueLength * actorActionWaterBuoyancyComponent.boyancyTorque * deltaTime;
}
}
});
}
}
}
using System;
using System.ComponentModel;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace DOTSActorWater
{
[Serializable]
public struct ActorActionWaterBuoyancySimpleComponent : IComponentData
{
//Main Data
public float buoyancyBottomHeight;
public float buoyancyCoeff;
public float boyancyTorque;
public float objectWidth;
public float objectLength;
public float forceHeightOffset;
public float dragInWaterUp;
public float dragInWaterRight;
public float dragInWaterForward;
public float dragInWaterRotational;
//Write Data
public float3 inverseInertia;
public float3 waterSurfaceVelocity;
public float3 waterSurfaceNormal;
public float3 normalLongitudinal;
public float bottomDepth;
public bool disabled;
}
}
using Unity.Entities;
using Unity.Transforms;
using UnityEngine;
namespace DOTSActorWater
{
public class ActorWaterBuyancySimpleAuthor : MonoBehaviour, IConvertGameObjectToEntity
{
[Header("Buoyancy")]
public float buoyancyBottomHeight = -1.3f;
public float buoyancyCoeff = 1.5f;
public float boyancyTorque = 8f;
[Header("Wave Response")]
public float objectWidth = 4.5f;
public float objectLength = 2.25f;
[Header("Fore | Drag")]
public float forceHeightOffset = -0.3f;
public float dragInWaterUp = 3f;
public float dragInWaterRight = 2f;
public float dragInWaterForward = 1f;
public float dragInWaterRotational = 0.2f;
[Header("Physics Body")]
public Vector3 inverseInertia;
public void Convert(Entity entity, EntityManager entityManager, GameObjectConversionSystem conversionSystem)
{
entityManager.AddComponentData(entity, new ActorActionWaterBuoyancySimpleComponent()
{
buoyancyBottomHeight = buoyancyBottomHeight,
buoyancyCoeff = buoyancyCoeff,
boyancyTorque = boyancyTorque,
objectWidth = objectWidth,
objectLength = objectLength,
forceHeightOffset = forceHeightOffset,
dragInWaterUp = dragInWaterUp,
dragInWaterRight = dragInWaterRight,
dragInWaterForward = dragInWaterForward,
dragInWaterRotational = dragInWaterRotational,
inverseInertia = inverseInertia
});
entityManager.SetComponentData(entity, new Translation { Value = transform.position });
}
public void OnDrawGizmos()
{
}
}
}