The boat in North Star is a core element, requiring smooth movement, turning, and ocean interaction while allowing the player to move freely onboard. Several approaches were explored to achieve this.
The boat was initially implemented as a kinematic rigid body moving via scripting, with all objects physically on top of it. However, this caused issues with hand tracking and physics constraints, leading us to abandon this approach.
Another idea was to move the environment around the boat instead of the boat itself. However, this led to complications with persistent objects outside the boat, so it was not pursued.
The final approach that we stuck with used a “fake movement” system, where the boat’s visual position is updated just before rendering and reset afterward. This prevents physics issues, avoids dragging objects along with the boat, and removes the need to move the world around the boat (ocean, islands, sky, reflections, etc.). Several helper functions were also developed for transforming between world space and “boat space.”
To simulate ocean movement, a procedural noise system was implemented to make the boat rock and bob. The effect scales with boat speed, which is influenced by wind direction and sail angle. A more physically realistic wave-height and momentum-based system was tested but was ultimately replaced for better player comfort and direct movement control.
Specific scripted boat movements were implemented using the timeline for special events, such as waves hitting the boat or attacks from creatures like the Kraken.
- BoatMovementAsset
- BoatMovementBehaviour
- BoatMovementMixerBehaviour
- BoatMovementTrack
- WaveControlAsset
- WaveControlBehaviour
- WaveControlTrack
To ensure player comfort, procedural motion is controlled directly, allowing the magnitude to be adjusted. An additional comfort option locks the horizon angle in place, keeping the player upright as if they had perfect “sea legs.” This prevents motion sickness by stabilizing the horizon.
var boatRotation = BoatController.Instance.MovementSource.CurrentRotation;
CameraRig.rotation = Quaternion.Slerp(Quaternion.identity, Quaternion.Inverse(boatRotation), GlobalSettings.PlayerSettings.ReorientStrength) * CameraRig.parent.localRotation;
(from BodyPositions.cs)