diff --git a/public/fear/snd/footstep1.mp3 b/public/fear/snd/footstep1.mp3 new file mode 100644 index 0000000..c2e3e8b Binary files /dev/null and b/public/fear/snd/footstep1.mp3 differ diff --git a/public/fear/snd/footstep2.mp3 b/public/fear/snd/footstep2.mp3 new file mode 100644 index 0000000..bb45515 Binary files /dev/null and b/public/fear/snd/footstep2.mp3 differ diff --git a/public/fear/snd/footstep3.mp3 b/public/fear/snd/footstep3.mp3 new file mode 100644 index 0000000..f5865e6 Binary files /dev/null and b/public/fear/snd/footstep3.mp3 differ diff --git a/public/fear/snd/footstep4.mp3 b/public/fear/snd/footstep4.mp3 new file mode 100644 index 0000000..fd446b5 Binary files /dev/null and b/public/fear/snd/footstep4.mp3 differ diff --git a/public/fear/snd/footstep5.mp3 b/public/fear/snd/footstep5.mp3 new file mode 100644 index 0000000..57e3eae Binary files /dev/null and b/public/fear/snd/footstep5.mp3 differ diff --git a/public/fear/snd/footstep6.mp3 b/public/fear/snd/footstep6.mp3 new file mode 100644 index 0000000..135e319 Binary files /dev/null and b/public/fear/snd/footstep6.mp3 differ diff --git a/src/app/fear/scene-components/player.tsx b/src/app/fear/scene-components/player.tsx index 3958def..6443310 100644 --- a/src/app/fear/scene-components/player.tsx +++ b/src/app/fear/scene-components/player.tsx @@ -54,10 +54,32 @@ export default function Player() { const confirmedSegment = useRef(0); const hasTriggeredThisSegment = useRef(false); + const footstepAudio = useRef([]); + const hasStepped = useRef(false); + + + useEffect(() => { playerRoot.set(camera.position.x, FEAR_SETTINGS.PLAYER_HEIGHT, camera.position.z); + footstepAudio.current = Array.from({ length: 6 }, (_, i) => { + const audio = new Audio(`fear/snd/footstep${i + 1}.mp3`); + audio.volume = 0.4; + return audio; + }); }, []); + const playRandomFootstep = () => { + if (footstepAudio.current.length === 0) return; + + const randomIndex = Math.floor(Math.random() * footstepAudio.current.length); + const audio = footstepAudio.current[randomIndex]; + + audio.currentTime = 0; + audio.play().catch((err) => { + console.warn("Footstep playback blocked by browser autocomplete/interaction rules.", err); + }); + }; + useFrame((state, delta) => { const dt = Math.min(delta, 0.1); @@ -73,7 +95,7 @@ export default function Player() { if (moveForward !== 0) targetVelocity.addScaledVector(forward, moveForward); if (moveSide !== 0) targetVelocity.addScaledVector(side, moveSide); - if (targetVelocity.lengthSq() > 0) + if (targetVelocity.lengthSq() > 0) targetVelocity.normalize().multiplyScalar(FEAR_SETTINGS.PLAYER_SPEED); currentVelocity.lerp(targetVelocity, 10 * dt); @@ -86,15 +108,25 @@ export default function Player() { playerRoot.x = THREE.MathUtils.clamp(playerRoot.x, minX, maxX); const isMoving = controls.Forward || controls.Backward || controls.Left || controls.Right; - + bobIntensity.current = THREE.MathUtils.lerp(bobIntensity.current, isMoving ? 1 : 0, 8 * dt); - - if (isMoving) + + if (isMoving) movementCounter.current += dt * 12; - const moveBobY = Math.sin(movementCounter.current) * 0.06 * bobIntensity.current; + const sinWave = Math.sin(movementCounter.current); + const moveBobY = sinWave * 0.06 * bobIntensity.current; const moveBobX = Math.cos(movementCounter.current / 2) * 0.04 * bobIntensity.current; + if (isMoving && sinWave < -0.9) { + if (!hasStepped.current) { + playRandomFootstep(); + hasStepped.current = true; + } + } else if (sinWave > 0) { + hasStepped.current = false; + } + const breatheTime = state.clock.elapsedTime * 1.8; const breatheBobY = Math.sin(breatheTime) * 0.03 * (1 - bobIntensity.current * 0.5); @@ -113,13 +145,13 @@ export default function Player() { flashlightRef.current.target.position.lerp(targetDest, 12 * dt); flashlightRef.current.target.updateMatrixWorld(); - flashlightRef.current.intensity = - FEAR_SETTINGS.FLASHLIGHT_INTENSITY_BASE + + flashlightRef.current.intensity = + FEAR_SETTINGS.FLASHLIGHT_INTENSITY_BASE + Math.sin(state.clock.elapsedTime * 30) * 0.15 * Math.cos(state.clock.elapsedTime * 3); } const length = FEAR_SETTINGS.HALLWAY_LENGTH; - const absoluteZ = -playerRoot.z; + const absoluteZ = -playerRoot.z; const rawSegmentIndex = Math.floor(absoluteZ / length); const progressZ = ((absoluteZ % length) + length) % length / length;