tweak mouse sensitivity

This commit is contained in:
Brian Beck 2026-03-13 20:16:47 -07:00
parent 9694e0fd82
commit 1373f12ab4
37 changed files with 333 additions and 264 deletions

View file

@ -77,9 +77,17 @@
grid-template-rows: auto auto;
align-items: center;
gap: 0 6px;
margin: 0 0 8px 0;
}
.CheckboxField:has(.Description) {
margin: 0 0 6px 0;
}
/* .Field:not(:has(.Description)) + .CheckboxField {
margin-top: 10px;
} */
.CheckboxField input[type="checkbox"] {
margin-left: 0;
grid-column: 1;
@ -98,7 +106,7 @@
font-size: 12px;
line-height: 1.3333;
opacity: 0.6;
margin: 2px 0 4px 0;
margin: 2px 0 0 0;
padding: 0;
}
@ -134,7 +142,11 @@
grid-template-columns: 1fr auto;
grid-template-rows: auto auto;
align-items: center;
margin: 0 0 6px 0;
margin: 0 0 12px 0;
}
.Field:has(.Description) {
margin: 0 0 10px 0;
}
.Field label {

View file

@ -72,6 +72,8 @@ export function InspectorControls({
const {
speedMultiplier,
setSpeedMultiplier,
mouseSensitivity,
setMouseSensitivity,
touchMode,
setTouchMode,
invertScroll,
@ -257,6 +259,31 @@ export function InspectorControls({
Reverse how dragging the viewport aims the camera.
</p>
</div>
{isTouch === false && (
<div className={styles.Field}>
<label htmlFor="mouseSensitivityInput">
Mouse sensitivity
</label>
<div className={styles.Control}>
<output htmlFor="mouseSensitivityInput">
{Math.round(mouseSensitivity * 8000) / 64}
</output>
<input
id="mouseSensitivityInput"
type="range"
min={1}
max={64}
step={1}
value={Math.round(mouseSensitivity * 8000)}
onChange={(event) => {
const value = parseInt(event.target.value);
const sens = value / 8000;
setMouseSensitivity(sens);
}}
/>
</div>
</div>
)}
</Accordion>
<Accordion value="preferences" label="Preferences">
<div className={styles.Field}>

View file

@ -58,8 +58,8 @@ const MIN_SPEED_ADJUSTMENT = 2;
const MAX_SPEED_ADJUSTMENT = 11;
const DRAG_THRESHOLD = 3; // px of movement before it counts as a drag
/** Shared mouse/look sensitivity used across all modes (.mis, .rec, live). */
export const MOUSE_SENSITIVITY = 0.002;
/** Hardcoded drag sensitivity (not affected by user setting). */
const DRAG_SENSITIVITY = 0.002;
export const ARROW_LOOK_SPEED = 1; // radians/sec
function quantizeSpeed(speedMultiplier: number): number {
@ -92,8 +92,13 @@ export function MouseAndKeyboardHandler() {
};
}, []);
const { speedMultiplier, setSpeedMultiplier, invertScroll, invertDrag } =
useControls();
const {
speedMultiplier,
setSpeedMultiplier,
mouseSensitivity,
invertScroll,
invertDrag,
} = useControls();
const { onInput, mode } = useInputContext();
const [subscribe, getKeys] = useKeyboardControls<Controls>();
const camera = useThree((state) => state.camera);
@ -104,6 +109,7 @@ export function MouseAndKeyboardHandler() {
const getInvertScroll = useEffectEvent(() => invertScroll);
const getInvertDrag = useEffectEvent(() => invertDrag);
const getMode = useEffectEvent(() => mode);
const getMouseSensitivity = useEffectEvent(() => mouseSensitivity);
// Accumulated mouse deltas between frames.
const mouseDeltaYaw = useRef(0);
@ -145,8 +151,9 @@ export function MouseAndKeyboardHandler() {
const handleMouseMove = (e: MouseEvent) => {
if (controlsRef.current?.isLocked) {
// Pointer is locked: accumulate raw deltas.
mouseDeltaYaw.current += e.movementX * MOUSE_SENSITIVITY;
mouseDeltaPitch.current += e.movementY * MOUSE_SENSITIVITY;
const sens = getMouseSensitivity();
mouseDeltaYaw.current += e.movementX * sens;
mouseDeltaPitch.current += e.movementY * sens;
return;
}
@ -165,8 +172,8 @@ export function MouseAndKeyboardHandler() {
// (decreasing yaw), opposite of fly mode.
const orbitFlip = getMode() === "follow" ? -1 : 1;
const dragSign = (getInvertDrag() ? 1 : -1) * orbitFlip;
mouseDeltaYaw.current += dragSign * e.movementX * MOUSE_SENSITIVITY;
mouseDeltaPitch.current += dragSign * e.movementY * MOUSE_SENSITIVITY;
mouseDeltaYaw.current += dragSign * e.movementX * DRAG_SENSITIVITY;
mouseDeltaPitch.current += dragSign * e.movementY * DRAG_SENSITIVITY;
};
const handleMouseUp = () => {

View file

@ -45,6 +45,8 @@ type DebugContext = {
type ControlsContext = {
speedMultiplier: number;
setSpeedMultiplier: StateSetter<number>;
mouseSensitivity: number;
setMouseSensitivity: StateSetter<number>;
touchMode: TouchMode;
setTouchMode: StateSetter<TouchMode>;
invertScroll: boolean;
@ -58,6 +60,10 @@ type ControlsContext = {
export const MIN_SPEED_MULTIPLIER = 0.01;
export const MAX_SPEED_MULTIPLIER = 1;
export const DEFAULT_MOUSE_SENSITIVITY = 16 / 8000; // 0.002
export const MIN_MOUSE_SENSITIVITY = 1 / 8000;
export const MAX_MOUSE_SENSITIVITY = 64 / 8000;
const SettingsContext = createContext<SettingsContext | null>(null);
const DebugContext = createContext<DebugContext | null>(null);
const ControlsContext = createContext<ControlsContext | null>(null);
@ -66,6 +72,7 @@ type PersistedSettings = {
fogEnabled?: boolean;
highQualityFog?: boolean;
speedMultiplier?: number;
mouseSensitivity?: number;
fov?: number;
audioEnabled?: boolean;
animationEnabled?: boolean;
@ -95,6 +102,9 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
const [fogEnabled, setFogEnabled] = useState(true);
const [highQualityFog, setHighQualityFog] = useState(false);
const [speedMultiplier, setSpeedMultiplier] = useState(0.15);
const [mouseSensitivity, setMouseSensitivity] = useState(
DEFAULT_MOUSE_SENSITIVITY,
);
const [fov, setFov] = useState(90);
const [audioEnabled, setAudioEnabled] = useState(false);
const [audioVolume, setAudioVolume] = useState(0.75);
@ -170,6 +180,8 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
() => ({
speedMultiplier,
setSpeedMultiplier,
mouseSensitivity,
setMouseSensitivity,
touchMode,
setTouchMode,
invertScroll,
@ -182,6 +194,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
[
speedMultiplier,
setSpeedMultiplier,
mouseSensitivity,
touchMode,
setTouchMode,
invertScroll,
@ -226,6 +239,14 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
),
);
}
if (savedSettings.mouseSensitivity != null) {
setMouseSensitivity(
Math.max(
MIN_MOUSE_SENSITIVITY,
Math.min(MAX_MOUSE_SENSITIVITY, savedSettings.mouseSensitivity),
),
);
}
if (savedSettings.fov != null) {
setFov(savedSettings.fov);
}
@ -270,6 +291,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
fogEnabled,
highQualityFog,
speedMultiplier,
mouseSensitivity,
fov,
audioEnabled,
animationEnabled,
@ -298,6 +320,7 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
fogEnabled,
highQualityFog,
speedMultiplier,
mouseSensitivity,
fov,
audioEnabled,
animationEnabled,