Feat/polish-mission1 #12

Merged
math-pixel merged 42 commits from feat/polish-mission1 into develop 2026-06-01 21:51:09 +00:00
4 changed files with 41 additions and 21 deletions
Showing only changes of commit bce7d11b66 - Show all commits
+35 -15
View File
@@ -8,6 +8,10 @@ import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF";
import { useClonedObject } from "@/hooks/three/useClonedObject"; import { useClonedObject } from "@/hooks/three/useClonedObject";
import { useDebugFolder } from "@/hooks/debug/useDebugFolder"; import { useDebugFolder } from "@/hooks/debug/useDebugFolder";
import { useEbikeSounds } from "@/hooks/ebike/useEbikeSounds"; import { useEbikeSounds } from "@/hooks/ebike/useEbikeSounds";
import {
getObjectBottomOffset,
useTerrainHeightSampler,
} from "@/hooks/three/useTerrainHeight";
import { animateCameraTransformTransition } from "@/world/GameCinematics"; import { animateCameraTransformTransition } from "@/world/GameCinematics";
import { useGameStore } from "@/managers/stores/useGameStore"; import { useGameStore } from "@/managers/stores/useGameStore";
import { import {
@@ -32,6 +36,18 @@ export function Ebike({ position }: EbikeProps): React.JSX.Element {
position: position, position: position,
}); });
const model = useClonedObject(scene); const model = useClonedObject(scene);
const terrainHeight = useTerrainHeightSampler();
const parkedPosition = useMemo<Vector3Tuple>(() => {
const [x, y, z] = position;
const height = terrainHeight.getHeight(x, z) ?? y;
const bottomOffset = getObjectBottomOffset(model, [
EBIKE_WORLD_SCALE,
EBIKE_WORLD_SCALE,
EBIKE_WORLD_SCALE,
]);
return [x, height + bottomOffset, z];
}, [model, position, terrainHeight]);
const movementMode = useGameStore((state) => state.player.movementMode); const movementMode = useGameStore((state) => state.player.movementMode);
const mainState = useGameStore((state) => state.mainState); const mainState = useGameStore((state) => state.mainState);
const ebikeStep = useGameStore((state) => state.ebike.currentStep); const ebikeStep = useGameStore((state) => state.ebike.currentStep);
@@ -64,19 +80,19 @@ export function Ebike({ position }: EbikeProps): React.JSX.Element {
y: number; y: number;
z: number; z: number;
}>({ }>({
x: position[0], x: parkedPosition[0],
y: position[1], y: parkedPosition[1],
z: position[2], z: parkedPosition[2],
}); });
const lastGpsUpdatePos = useRef<THREE.Vector3>( const lastGpsUpdatePos = useRef<THREE.Vector3>(
new THREE.Vector3(...position), new THREE.Vector3(...parkedPosition),
); );
// Use ref for internal state, and state for debug visualization (to avoid ref access during render) // Use ref for internal state, and state for debug visualization (to avoid ref access during render)
const restingPositionRef = useRef<Vector3Tuple>([ const restingPositionRef = useRef<Vector3Tuple>([
position[0], parkedPosition[0],
position[1], parkedPosition[1],
position[2], parkedPosition[2],
]); ]);
const restingRotationRef = useRef<number>(EBIKE_WORLD_ROTATION_Y); const restingRotationRef = useRef<number>(EBIKE_WORLD_ROTATION_Y);
const forkRef = useRef<THREE.Object3D | null>(null); const forkRef = useRef<THREE.Object3D | null>(null);
@@ -84,23 +100,27 @@ export function Ebike({ position }: EbikeProps): React.JSX.Element {
// State for debug visualization (synced from refs during useFrame) // State for debug visualization (synced from refs during useFrame)
const [showCameraPoints, setShowCameraPoints] = useState(true); const [showCameraPoints, setShowCameraPoints] = useState(true);
const [debugRestingPosition, setDebugRestingPosition] = const [debugRestingPosition, setDebugRestingPosition] =
useState<Vector3Tuple>([position[0], position[1], position[2]]); useState<Vector3Tuple>([
parkedPosition[0],
parkedPosition[1],
parkedPosition[2],
]);
useEffect(() => { useEffect(() => {
if (movementMode === "ebike") return; if (movementMode === "ebike") return;
restingPositionRef.current = position; restingPositionRef.current = parkedPosition;
restingRotationRef.current = EBIKE_WORLD_ROTATION_Y; restingRotationRef.current = EBIKE_WORLD_ROTATION_Y;
lastGpsUpdatePos.current.set(...position); lastGpsUpdatePos.current.set(...parkedPosition);
if (groupRef.current) { if (groupRef.current) {
groupRef.current.position.set(...position); groupRef.current.position.set(...parkedPosition);
groupRef.current.rotation.set(0, EBIKE_WORLD_ROTATION_Y, 0); groupRef.current.rotation.set(0, EBIKE_WORLD_ROTATION_Y, 0);
} }
window.ebikeParkedPosition = position; window.ebikeParkedPosition = parkedPosition;
window.ebikeParkedRotation = EBIKE_WORLD_ROTATION_Y; window.ebikeParkedRotation = EBIKE_WORLD_ROTATION_Y;
}, [movementMode, position]); }, [movementMode, parkedPosition]);
useEffect(() => { useEffect(() => {
if (model) { if (model) {
@@ -293,7 +313,7 @@ export function Ebike({ position }: EbikeProps): React.JSX.Element {
{!repairGameOwnsEbikeModel ? ( {!repairGameOwnsEbikeModel ? (
<group <group
ref={groupRef} ref={groupRef}
position={position} position={parkedPosition}
rotation={[0, EBIKE_WORLD_ROTATION_Y, 0]} rotation={[0, EBIKE_WORLD_ROTATION_Y, 0]}
scale={EBIKE_WORLD_SCALE} scale={EBIKE_WORLD_SCALE}
> >
@@ -301,7 +321,7 @@ export function Ebike({ position }: EbikeProps): React.JSX.Element {
<InteractableObject <InteractableObject
kind="trigger" kind="trigger"
label={interactionLabel} label={interactionLabel}
position={position} position={parkedPosition}
radius={5} radius={5}
onPress={handleInteract} onPress={handleInteract}
> >
+1 -1
View File
@@ -15,7 +15,7 @@ export const EBIKE_DROP_PLAYER_TRANSFORM: CameraTransform = {
rotation: [0, 0, 0], rotation: [0, 0, 0],
}; };
export const EBIKE_WORLD_POSITION: Vector3Tuple = [57.9, 6.3, 58.35]; export const EBIKE_WORLD_POSITION: Vector3Tuple = [65, 0.8, 72];
export const EBIKE_WORLD_ROTATION_Y = -2.5; export const EBIKE_WORLD_ROTATION_Y = -2.5;
export const EBIKE_WORLD_SCALE = 0.35; export const EBIKE_WORLD_SCALE = 0.35;
+3 -3
View File
@@ -5,7 +5,7 @@ export const PLAYER_EYE_HEIGHT = 1.75;
export const PLAYER_CAPSULE_RADIUS = 0.35; export const PLAYER_CAPSULE_RADIUS = 0.35;
export const PLAYER_WALK_SPEED = 5; export const PLAYER_WALK_SPEED = 5;
export const PLAYER_EBIKE_SPEED = 20; export const PLAYER_EBIKE_SPEED = 30;
export const PLAYER_AIR_CONTROL_FACTOR = 0.35; export const PLAYER_AIR_CONTROL_FACTOR = 0.35;
export const PLAYER_JUMP_SPEED = 9; export const PLAYER_JUMP_SPEED = 9;
export const PLAYER_GRAVITY = 30; export const PLAYER_GRAVITY = 30;
@@ -16,8 +16,8 @@ export const PLAYER_FALL_RESPAWN_Y = -20;
export const PLAYER_FALL_RESPAWN_DELAY = 3; export const PLAYER_FALL_RESPAWN_DELAY = 3;
export const PLAYER_SPAWN_POSITION_GAME: Vector3Tuple = [ export const PLAYER_SPAWN_POSITION_GAME: Vector3Tuple = [
LA_FABRIK_PLAYER_SPAWN[0] + 5, LA_FABRIK_PLAYER_SPAWN[0] + 1,
LA_FABRIK_PLAYER_SPAWN[1], LA_FABRIK_PLAYER_SPAWN[1],
LA_FABRIK_PLAYER_SPAWN[2] + 5, LA_FABRIK_PLAYER_SPAWN[2] - 1,
]; ];
export const PLAYER_SPAWN_POSITION_PHYSICS: Vector3Tuple = [0, 3, 0]; export const PLAYER_SPAWN_POSITION_PHYSICS: Vector3Tuple = [0, 3, 0];
+2 -2
View File
@@ -6,8 +6,8 @@ export const LA_FABRIK_HALF_EXTENTS = {
x: 8.5, x: 8.5,
z: 7.5, z: 7.5,
} as const; } as const;
export const LA_FABRIK_PLAYER_SPAWN: Vector3Tuple = [59.5, 7.8, 64.64]; export const LA_FABRIK_PLAYER_SPAWN: Vector3Tuple = [59.5, 6.3, 64.64];
export const LA_FABRIK_INITIAL_LOOK_AT: Vector3Tuple = [58, 7.8, 62.5]; export const LA_FABRIK_INITIAL_LOOK_AT: Vector3Tuple = [58, 7.3, 62.5];
export const LA_FABRIK_INTERIOR_LIGHT_POSITION: Vector3Tuple = [59.5, 9, 64.64]; export const LA_FABRIK_INTERIOR_LIGHT_POSITION: Vector3Tuple = [59.5, 9, 64.64];
export function isInsideLaFabrikFootprint( export function isInsideLaFabrikFootprint(