Overview
Gitlantis provides multiple navigation methods with realistic physics simulation. The boat responds to keyboard input or touch-based joystick controls with smooth acceleration, deceleration, and turning mechanics.
Keyboard Controls
The boat responds to both arrow keys and WASD controls:
Movement Keys
Input Handling
Key Alternative Action W↑Move forward S↓Move backward (50% speed) A←Turn left D→Turn right H- Sound horn F- Toggle minimap fullscreen
Source : src/browser/hooks/useBoat/keyboard/index.ts:10-35Keyboard inputs are tracked using a ref-based system that updates on keydown/keyup events: const handleKeyDown = ( e : KeyboardEvent ) => {
switch ( e . key . toLowerCase ()) {
case "arrowup" :
case "w" :
directionInputRef . current . forward = true ;
break ;
case "arrowdown" :
case "s" :
directionInputRef . current . backward = true ;
break ;
// ... etc
}
};
This approach provides zero-latency input response and allows for simultaneous key presses. Source : src/browser/hooks/useBoat/keyboard/index.ts:9-36
Joystick Controls (Mobile)
On mobile devices, a dynamic joystick appears using the nipplejs library:
const options : JoystickManagerOptions = {
zone: joystickRef . current ,
mode: "dynamic" ,
color: "red" ,
size: 100 ,
restOpacity: 0.4 ,
};
The joystick translates touch input into directional commands:
manager . on ( "move" , ( _ , data : JoystickOutputData ) => {
const angle = data . angle ?. radian ?? 0 ;
const force = data . force ?? 0 ;
const dx = Math . cos ( angle ) * force ;
const dy = Math . sin ( angle ) * force ;
onMove ( dx , dy );
});
The joystick is only visible on mobile devices and automatically hides on desktop (using md:invisible class).
Source : src/browser/components/world/joystick/index.tsx:25-42
Movement Physics
The navigation system uses frame-based physics with configurable parameters:
Speed and Acceleration
const config = {
maxSpeed: settings . boatSpeed ,
acceleration: settings . acceleration ,
deceleration: settings . deceleration ,
turnSpeed: settings . turnSpeed ,
turnDeceleration: settings . turnDeceleration ,
rockingAmplitude: settings . rockingAmplitude ,
rockingSpeed: settings . rockingSpeed ,
bobbingAmplitude: settings . bobbingAmplitude ,
bobbingSpeed: settings . bobbingSpeed ,
};
All parameters are user-configurable through the extension settings. Source : src/browser/hooks/useBoat/navigation/index.tsx:24-34
Forward/Backward Movement
The boat accelerates smoothly toward the target speed:
if ( keys . forward ) {
targetSpeed = config . maxSpeed ;
activeInput = INTENDED_DIRECTION . FORWARD ;
} else if ( keys . backward ) {
targetSpeed = - config . maxSpeed * 0.5 ;
activeInput = INTENDED_DIRECTION . BACKWARD ;
}
if ( targetSpeed !== 0 ) {
currentState . speed += ( targetSpeed - currentState . speed ) * config . acceleration ;
} else {
currentState . speed *= 1 - config . deceleration ;
}
Backward movement is limited to 50% of max speed for realistic boat physics.
Source : src/browser/hooks/useBoat/navigation/index.tsx:75-106
Turning Mechanics
Turning behavior adapts based on movement direction:
Moving forward : Left/right keys turn in the expected direction
Moving backward : Turning is reversed (like a car in reverse)
Stationary : Boat can pivot in place
if ( keys . left ) {
if ( isStationary ) {
targetTurn = config . turnSpeed ;
} else {
targetTurn =
currentState . intendedDirection === INTENDED_DIRECTION . BACKWARD
? - config . turnSpeed
: config . turnSpeed ;
}
}
Source : src/browser/hooks/useBoat/navigation/index.tsx:112-121
Visual Effects
Rocking and Bobbing
When moving, the boat exhibits realistic rocking and bobbing motion that decreases with speed:
const movementFactor = 1 - Math . min ( Math . abs ( state . current . speed ) / config . maxSpeed , 1 );
const time = performance . now () / 1000 ;
// Side-to-side rocking
( floating as unknown as Group ). rotation . y =
Math . sin ( time * config . rockingSpeed * Math . PI * 2 ) *
config . rockingAmplitude *
movementFactor ;
// Up-down bobbing
( floating as unknown as Group ). position . y =
Math . sin ( time * config . bobbingSpeed * Math . PI * 2 ) *
config . bobbingAmplitude *
movementFactor ;
The movementFactor reduces rocking when the boat is moving fast, creating more stability at high speeds.
Source : src/browser/hooks/useBoat/navigation/index.tsx:51-64
Stationary Floating
When completely stationary, the boat uses a more complex floating simulation:
const primaryWave = Math . sin ( time * 0.6 ) * state . waveHeight ;
const secondaryWave = Math . sin ( time * 1.3 + Math . PI / 3 ) * ( state . waveHeight * 0.4 );
const chop = Math . sin ( time * 2.8 ) * ( state . waveHeight * state . roughness );
const positionWave = Math . sin ( boatPos . x * 0.1 + time ) * Math . cos ( boatPos . z * 0.1 + time );
const totalFloat = primaryWave + secondaryWave + chop + positionWave * 0.2 ;
This creates realistic multi-layered wave motion when the boat is idle.
Source : src/browser/hooks/useBoat/floating/index.tsx:46-53
Frame-Rate Independence
All physics calculations use delta multipliers to ensure consistent behavior across different frame rates: const deltaMultiplier = Math . min ( delta * 60 , 2 );
boat . rotateY ( currentState . angularVelocity * deltaMultiplier );
boat . translateX ( - currentState . speed * deltaMultiplier );
This prevents the boat from moving faster on high-refresh-rate displays.
Source : src/browser/hooks/useBoat/navigation/index.tsx:48,142-146