Files
boatgame/scripts/floating_body.gd
T
2026-07-07 08:17:45 +02:00

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)