Get your boat or ship floating

Hya,

I’ve started writing a re-make of a once famous strategy game (code will be available at GitHub - neuweiler/CarrierCommander: A java based open source re-make of a once fantastic game). After some hassle to get acquainted with quaternions and rotation of objects, I found a pretty decent way to make a boat floating by using only the water filter (no collision with object). Here:

you can see the result of throwing an aircraft carrier into the sea :wink:

Enemy ahead also does a very nice simulation of ships but with a water object. It measures the immersion in water at six points of the ship and applies forces accordingly. The advantage of this approach is that the ship will move with the waves. My approach is much simpler and doesn’t offer full wave physics. But for my purposes it’s sufficient.

The basic principle:

Quaternion carrierCurrentRotation = new Quaternion();
final static int maxDeltaWaterHeight = 20;

@Override
public void simpleUpdate(float tpf) {
super.simpleUpdate(tpf);
time += tpf;
waterHeight = (float) Math.cos(((time * 0.3f) % FastMath.TWO_PI)) * 1.4f + initialWaterHeight;

if (water != null)
    water.setWaterHeight(waterHeight);
if (carrier != null) {
    RigidBodyControl control = carrier.getControl(RigidBodyControl.class);
    float meterBelowWater = waterHeight - control.getPhysicsLocation().getY() + 4;
    if (meterBelowWater > maxDeltaWaterHeight)
        meterBelowWater = maxDeltaWaterHeight;
    if (meterBelowWater < -maxDeltaWaterHeight)
        meterBelowWater = -maxDeltaWaterHeight;
    float percentageDisplacement = meterBelowWater / maxDeltaWaterHeight;
    float force = (carrier.weigth + percentageDisplacement * carrier.weigth) * 9.81f;
    control.getPhysicsRotation(carrierCurrentRotation);
    Vector3f rotationOffset = carrierCurrentRotation.mult(Vector3f.UNIT_Y);
    control.applyForce(new Vector3f(0f, force, 0f), new Vector3f(50f * rotationOffset.x, rotationOffset.y, 100f * rotationOffset.z));
}

}

Of course this could be optimized to limit the creation of objects inside the update loop and the logic can be moved to a custom controller. I just wanted to explain the principle with a readable example.
When the carrier submerges the water, a linearly increasing force pushes against gravity. When the water is above the water line, the force is stronger than the carrier’s mass*9.81 - hence pushing it up. If the water is below the water line, the counter force is getting smaller. If the carrier is level, the force applies to the centre of the object. When it is rolling, the force’s attack point is moved port/starboard or aft/astern for pitch != 0. This is how the total force of any floating object is working.
To prevent endless oscillations, apply setDamping(0.5f, 0.3f); to the control (smaller objects should get less damping).
All the carrier has is a RigidBodyControl (with a CompoundCollisionShape consisting of two BoxCollisionShape). As the waterHeight changes over time (tidal waves), the carrier also moves up and down.

Hope you find this useful.
Thanks to the jme3 devs for creating such a great framework! Also the water filter rocks !

18 Likes

I missed that post! Thanks to Erlend and his tweet now i’ve seen it.
That’s pretty awesome nice work!!

1 Like

Thanks. The final control class can be found here : https://github.com/neuweiler/CarrierCommander/blob/master/src/net/carriercommander/control/FloatControl.java

There’s also a ship control class which applies forces at the rear end of the ship - as a combination of engine (local z-axis forces) and rudder (local x-axis forces). Depending on the dampening or friction, this approach results in a nice sideways drift typical to ships without keel : https://github.com/neuweiler/CarrierCommander/blob/master/src/net/carriercommander/control/ShipControl.java

Do you have the link of the tweet ?

2 Likes