Making Boat Physics in Godot

ship physics, godot pirate game

My objective was to make a ship that is floating on water; walkable on deck; and its motion is based on dynamic water waves. Yet it should be cheap to compute, not taking too much frame-rate.

Table of Contents

Breakdown

  1. I wrote the same wave function used for vertex displacement in ocean shader in GDScript in Ocean.gd file (file dealing with ocean parameters and handling ocean shader as well).
  2. In boat scene, I added 4 Marker3D nodes to assign the position where I am going to call the wave function.
  3. In for loop, all those positions are used to get wave height at that point, and the values are used to apply force on the boat RigidBody3D.
  4. It makes the boat float on water and move with the waves.
  5. To make performance better and make the ship walkable, the rigid body shape used for boat is a simple shape such as box, placed near the water surface; however, a static body, consisting of actual shape of boat/ship is made the child of this rigid body. So player can walk on static body without making any disturbance to rigid-body calculation which is kept simple by the use of simple box shape. And the static body, being the child of rigid-body moves with it.
  6. All other things such as forward thrust, left or right movement is achieved by applying force to rigid body.
boat physics in godot sailing ship game

Full Code

extends RigidBody3D
class_name BoatBody3D

@export var float_force := 1.0
@export var water_drag := 0.05
@export var water_angular_drag := 0.05

@onready var gravity: float = Vector3(0, -9.8, 0)

@onready var probes = $ProbeContainer.get_children()

var submerged := false

var is_docked: bool = false

@onready var ocean : Ocean = get_tree().get_nodes_in_group("ocean")[0]

@export var max_thrust_force: float = 1024
@export var max_steering: float = 128

var steering: float = 0 # steering rudder angle in radians
var thrust_force: float = 0 # forward thrust force in Newtons




# Called when the node enters the scene tree for the first time.
func _ready():
	add_to_group("boats")



func _physics_process(delta):
	submerged = false
	for p in probes:
		var depth = ocean.get_water_level(p.global_position) - p.global_position.y 
		if depth > 0:
			submerged = true
			apply_force(Vector3.UP * float_force * gravity * depth, p.global_position - global_position)
	
	apply_central_force(-self.global_transform.basis.z.normalized() * Vector3(1, 0, 1) * thrust_force)
	apply_torque(Vector3.UP * steering)
	
	##apply_torque(-global_transform.basis.z.normalized() * steering * 0.05) # for sideways motion
	
	reset_forces()

func _integrate_forces(state: PhysicsDirectBodyState3D):
	if submerged:
		state.linear_velocity *=  1 - water_drag
		state.angular_velocity *= 1 - water_angular_drag 



func thrust():
	if not is_docked:
		thrust_force = max_thrust_force

func steer_right():
	if not is_docked:
		steering = -PI * max_steering

func steer_left():
	if not is_docked:
		steering = PI * max_steering


func reset_forces():
	thrust_force = 0
	steering = 0


Thank you for reading <3

Leave a Reply

Your email address will not be published. Required fields are marked *