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 _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: var tx := 0.5 if probe_grid.x <= 1 else col / float(probe_grid.x - 1) var tz := 0.5 if probe_grid.y <= 1 else row / float(probe_grid.y - 1) _probe_offsets.append(Vector3( lerpf(-probe_extents.x, probe_extents.x, tx), probe_extents.y, lerpf(-probe_extents.z, probe_extents.z, tz))) 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() 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)