69 lines
2.7 KiB
GDScript
69 lines
2.7 KiB
GDScript
class_name FloatingBody
|
|
extends RigidBody3D
|
|
|
|
## Buoyant rigid body. Add Marker3D children as float probes (e.g. the four
|
|
## corners of a hull); each probe pushes up in proportion to how deep it is
|
|
## below the ocean surface, which makes the body bob and tilt with the waves.
|
|
|
|
## Upward acceleration as a multiple of gravity when fully submerged.
|
|
## > 1.0 floats, < 1.0 sinks. Higher rides higher in the water.
|
|
@export var buoyancy := 2.0
|
|
## Probe depth (in meters) at which buoyant force reaches full strength.
|
|
@export var full_force_depth := 0.4
|
|
## Linear drag applied while in the water.
|
|
@export var water_drag := 1.5
|
|
## Rotational drag applied while in the water.
|
|
@export var water_angular_drag := 1.0
|
|
|
|
## Probes generated across the hull footprint if no Marker3D children exist.
|
|
@export var probe_extents := Vector3(0.35, 0.0, 0.9) # half-width, height, half-length
|
|
@export var probe_grid := Vector2i(2, 4) # columns (x), rows (z)
|
|
|
|
@export var probe_damping := 4.0
|
|
|
|
var _probe_offsets: Array[Vector3] = [] # local space
|
|
|
|
var _ocean: Ocean
|
|
var _probes: Array[Marker3D] = []
|
|
var _gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
|
var submerged_ratio := 0.0
|
|
|
|
|
|
func _ready() -> void:
|
|
_ocean = get_tree().get_first_node_in_group("ocean") as Ocean
|
|
# manual markers take priority if present...
|
|
for child in get_children():
|
|
if child is Marker3D:
|
|
_probe_offsets.append(child.position)
|
|
# ...otherwise generate a grid over the hull footprint
|
|
if _probe_offsets.is_empty():
|
|
for col in probe_grid.x:
|
|
for row in probe_grid.y:
|
|
_probe_offsets.append(Vector3(
|
|
lerpf(-probe_extents.x, probe_extents.x, col / float(probe_grid.x - 1)),
|
|
probe_extents.y,
|
|
lerpf(-probe_extents.z, probe_extents.z, row / float(probe_grid.y - 1))))
|
|
|
|
|
|
|
|
func _physics_process(_delta: float) -> void:
|
|
if _ocean == null or _probe_offsets.is_empty():
|
|
return
|
|
submerged_ratio = 0.0
|
|
for offset in _probe_offsets:
|
|
var pos := global_transform * offset
|
|
var depth := _ocean.get_wave_height(pos) - pos.y
|
|
if depth > 0.0:
|
|
var factor := clampf(depth / full_force_depth, 0.0, 1.0)
|
|
submerged_ratio += factor / _probe_offsets.size()
|
|
var force := Vector3.UP * _gravity * mass * buoyancy * factor / _probe_offsets.size()
|
|
apply_force(force, pos - global_position)
|
|
var r := pos - global_position
|
|
# velocity of this specific point on the hull (body motion + rotation)
|
|
var point_velocity := linear_velocity + angular_velocity.cross(r)
|
|
var damp := Vector3.UP * -point_velocity.y * probe_damping * factor * mass / _probe_offsets.size()
|
|
apply_force(force + damp, r)
|
|
if submerged_ratio > 0.0:
|
|
apply_central_force(-linear_velocity * water_drag * mass * submerged_ratio)
|
|
apply_torque(-angular_velocity * water_angular_drag * mass * submerged_ratio)
|