A rowing boat without a physics engine in Godot 4: rowing, running aground and landing
A boat at the Caleta pier for rowing along the coast, landing on the sandbar island and fishing in open water. No real physics: it only checks water depth and the pier.

From the shore and the pier at Caleta Salobre, the water is never much deeper than 2 m. The open sea, where the big fish live, starts about 200 m out, and swimming there was a slog. So I moored a rowing boat at the end of the pier.
R (USAR, "use", on mobile) gets you on board. The joystick or WASD rows: the boat turns towards where you push, relative to the camera, and goes faster the more directly ahead it's heading. SALTAR ("jump") gets you off: next to land, onto the pier or where the water is shallow, you're standing; in deep water, you're swimming next to the boat, and you can climb back in from the water. The boat stays where you leave it, shows on the map, and can be brought back from the bollard on the pier. When it's stopped you can fish from it, which is how you reach the open sea and its fish.
How it's built
There's no real physics. While you're aboard, the mod is the player's mover (like the broom when flying): every step it decides where the boat is and seats the player on it. The only question is whether the boat fits in the next spot:
func free_at(p: Vector2, ahead: Vector2) -> bool:
var bow := p + ahead * BOW
if depth_at(p) < DRAFT or depth_at(bow) < DRAFT:
return false
if absf(p.x) > Island.LIMIT - EDGE or absf(p.y) > Island.LIMIT - EDGE:
return false
for q in [p, bow]:
if _pier_distance(q) < PIER_CLEAR:
return false
for d in decor:
if q.distance_to(d) < DECOR_CLEAR:
return false
return true
It checks the water under the centre and under the bow (DRAFT is 0.6 m), the edge of the world, the pier as a line segment, and Caleta's two decorative boats. If it doesn't fit, it stops and tells you there's not enough water there. Since the island isn't stored but computed from a height function, you can ask for the depth at any point.
Top rowing speed is 4.5 m/s (swimming is 2.6, running 6). While you're in the boat the shared interaction is switched off, which is why you get off with SALTAR and not USAR: no extra button needed. The fishing mod doesn't know about the boat either. It asks the mover whether it allows_fishing(), and the boat says yes when it's going under 0.25 m/s and you're not rowing.
If you save while aboard, you're still aboard when you load. If the saved spot isn't water deep enough, the boat goes back to its mooring.
What went wrong
- On boarding, the player stood up until the first physics step. The seated pose was set by the first
move_player, and the fishing mod, which stores the current pose when you cast, kept the standing one. Nowboard()seats you right away, and a test checks the pose without waiting for physics. - From the water, the end of Caleta's pier looked hollow. The village pier had lacked end caps since an earlier round, and nobody noticed until looking at it from the boat. It's now closed on both sides, with a test.
- The boat was built by one Claude session while another built fishing. Two sessions in the same repository share the temp folder and
user://, and one session's tests overwrote the other's test saves: each ended up with its own worktree anduser://.
Measured on PC: 32 checks using the engine's physics (rowing, running aground at the island, getting off onto the pier, the shore and the open sea, climbing back in from the water, touch controls, saving). Draw cost: 56–70 calls in open water; 322 with the boat moored and the village in view.
Still unverified
Not tested on a phone. The rowing sound was generated and measured, but nobody has listened to it. And with no physics, the boat doesn't collide with the pier posts or other villages' boats, and when turning tight against the pier the bow can poke over the planks.