Damping is precisely what is needed for "natural" behavior as it stands in for drag force. There is considerable amount of drag force applied to the object when submerged, in direction opposite to movement.
Drag force is always proportional to velocity. If you want precise simulation, calculate buoyancy (proportional to volume/area submerged) and underwater drag (proportional to velocity) and subtract them from the gravity force. Or just inject proper buoyancy and and drag forces into the system each frame.
It can be done with any of the suggested methods; gravity scale, injected forces, etc... But you don't really need any elaborate setup.
# 2D, y axis points downwards
var waterLevel = 100
var fullySubmergedLevel = 200
var maxBuoyancy = 20.0 # if greater than gravity, object will float
var gravity = 9.8
var fluidDrag = .1
func _process(delta):
var buoyancy = clamp(range_lerp(position.y, waterLevel, fullySubmergedLevel, 0.0, maxBuoyancy), 0, maxBuoyancy)
var drag = 0
if position.y > waterLevel:
drag = linear_velocity.y * fluidDrag # nor a proper drag calculation but good enough for this
gravity_scale = (gravity - (buoyancy + drag)) / gravity
Drawback of using a separate Area for "water edge" (or hardcoded "edge" as in above example) is that it doesn't take object's size/volume into consideration. Larger the object, larger its "edge area" will be. If all objects are roughly the same size then it's ok. But if you need more generalized system it's better to calculate buoyancy from object's size.