feat: implement acceleration, prevent bob from offsetting root pos
This commit is contained in:
@@ -2,13 +2,16 @@ import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { FEAR_SETTINGS, fearState } from "../state";
|
||||
import { PointerLockControls } from "@react-three/drei";
|
||||
|
||||
import * as THREE from "three";
|
||||
|
||||
const forward = new THREE.Vector3();
|
||||
const side = new THREE.Vector3();
|
||||
const direction = new THREE.Vector3();
|
||||
const viewDirection = new THREE.Vector3();
|
||||
const targetDest = new THREE.Vector3();
|
||||
|
||||
const playerRoot = new THREE.Vector3(0, FEAR_SETTINGS.PLAYER_HEIGHT, 0);
|
||||
const targetVelocity = new THREE.Vector3();
|
||||
const currentVelocity = new THREE.Vector3();
|
||||
|
||||
function usePlayerControls() {
|
||||
const keys = useRef({ Forward: false, Backward: false, Left: false, Right: false });
|
||||
@@ -46,69 +49,77 @@ export default function Player() {
|
||||
|
||||
const flashlightRef = useRef<THREE.SpotLight>(null);
|
||||
const movementCounter = useRef<number>(0);
|
||||
const bobIntensity = useRef<number>(0);
|
||||
|
||||
const confirmedSegment = useRef<number>(0);
|
||||
const hasTriggeredThisSegment = useRef<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
playerRoot.set(camera.position.x, FEAR_SETTINGS.PLAYER_HEIGHT, camera.position.z);
|
||||
}, []);
|
||||
|
||||
useFrame((state, delta) => {
|
||||
const dt = Math.min(delta, 0.1);
|
||||
|
||||
camera.getWorldDirection(forward);
|
||||
forward.y = 0;
|
||||
forward.normalize();
|
||||
|
||||
side.crossVectors(forward, new THREE.Vector3(0, 1, 0)).normalize();
|
||||
side.crossVectors(forward, THREE.Object3D.DEFAULT_UP).normalize();
|
||||
|
||||
const moveForward = Number(controls.Forward) - Number(controls.Backward);
|
||||
const moveSide = Number(controls.Right) - Number(controls.Left);
|
||||
|
||||
direction.set(0, 0, 0);
|
||||
if (moveForward !== 0) direction.addScaledVector(forward, moveForward);
|
||||
if (moveSide !== 0) direction.addScaledVector(side, moveSide);
|
||||
targetVelocity.set(0, 0, 0);
|
||||
if (moveForward !== 0) targetVelocity.addScaledVector(forward, moveForward);
|
||||
if (moveSide !== 0) targetVelocity.addScaledVector(side, moveSide);
|
||||
|
||||
if (direction.lengthSq() > 0)
|
||||
direction.normalize().multiplyScalar(FEAR_SETTINGS.PLAYER_SPEED * delta);
|
||||
if (targetVelocity.lengthSq() > 0)
|
||||
targetVelocity.normalize().multiplyScalar(FEAR_SETTINGS.PLAYER_SPEED);
|
||||
|
||||
camera.position.x += direction.x;
|
||||
camera.position.z += direction.z;
|
||||
currentVelocity.lerp(targetVelocity, 10 * dt);
|
||||
|
||||
playerRoot.x += currentVelocity.x * dt;
|
||||
playerRoot.z += currentVelocity.z * dt;
|
||||
|
||||
const minX = -fearState.currentWidth / 2 + FEAR_SETTINGS.WALL_BUFFER;
|
||||
const maxX = fearState.currentWidth / 2 - FEAR_SETTINGS.WALL_BUFFER;
|
||||
|
||||
if (camera.position.x < minX) camera.position.x = minX;
|
||||
if (camera.position.x > maxX) camera.position.x = maxX;
|
||||
|
||||
let bobX = 0;
|
||||
let bobY = 0;
|
||||
playerRoot.x = THREE.MathUtils.clamp(playerRoot.x, minX, maxX);
|
||||
|
||||
const isMoving = controls.Forward || controls.Backward || controls.Left || controls.Right;
|
||||
if (isMoving) {
|
||||
movementCounter.current += delta * 10;
|
||||
bobY = Math.sin(movementCounter.current) * 0.08;
|
||||
bobX = Math.cos(movementCounter.current / 2) * 0.006;
|
||||
|
||||
camera.position.y = FEAR_SETTINGS.PLAYER_HEIGHT + bobY;
|
||||
camera.position.addScaledVector(side, bobX);
|
||||
} else {
|
||||
const breatheTime = state.clock.elapsedTime * 1.5;
|
||||
const breatheY = FEAR_SETTINGS.PLAYER_HEIGHT + Math.sin(breatheTime) * 0.1;
|
||||
camera.position.y = THREE.MathUtils.lerp(camera.position.y, breatheY, 4 * delta);
|
||||
}
|
||||
|
||||
bobIntensity.current = THREE.MathUtils.lerp(bobIntensity.current, isMoving ? 1 : 0, 8 * dt);
|
||||
|
||||
if (isMoving)
|
||||
movementCounter.current += dt * 12;
|
||||
|
||||
const moveBobY = Math.sin(movementCounter.current) * 0.06 * bobIntensity.current;
|
||||
const moveBobX = Math.cos(movementCounter.current / 2) * 0.04 * bobIntensity.current;
|
||||
|
||||
const breatheTime = state.clock.elapsedTime * 1.8;
|
||||
const breatheBobY = Math.sin(breatheTime) * 0.03 * (1 - bobIntensity.current * 0.5);
|
||||
|
||||
camera.position.copy(playerRoot);
|
||||
camera.position.y += moveBobY + breatheBobY;
|
||||
camera.position.addScaledVector(side, moveBobX);
|
||||
|
||||
if (flashlightRef.current) {
|
||||
flashlightRef.current.position.copy(camera.position);
|
||||
camera.getWorldDirection(viewDirection);
|
||||
|
||||
const targetDest = new THREE.Vector3()
|
||||
targetDest
|
||||
.copy(camera.position)
|
||||
.addScaledVector(viewDirection, 10);
|
||||
|
||||
flashlightRef.current.target.position.lerp(targetDest, 10 * delta);
|
||||
flashlightRef.current.target.position.lerp(targetDest, 12 * dt);
|
||||
flashlightRef.current.target.updateMatrixWorld();
|
||||
|
||||
flashlightRef.current.intensity = FEAR_SETTINGS.FLASHLIGHT_INTENSITY_BASE + Math.sin(state.clock.elapsedTime * 30) * 0.3;
|
||||
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 = -camera.position.z;
|
||||
const absoluteZ = -playerRoot.z;
|
||||
const rawSegmentIndex = Math.floor(absoluteZ / length);
|
||||
const progressZ = ((absoluteZ % length) + length) % length / length;
|
||||
|
||||
@@ -127,8 +138,9 @@ export default function Player() {
|
||||
confirmedSegment.current = rawSegmentIndex;
|
||||
}
|
||||
|
||||
if (rawSegmentIndex === confirmedSegment.current && progressZ > 0.35 && progressZ < 0.65)
|
||||
if (rawSegmentIndex === confirmedSegment.current && progressZ > 0.35 && progressZ < 0.65) {
|
||||
hasTriggeredThisSegment.current = false;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -136,12 +148,14 @@ export default function Player() {
|
||||
<PointerLockControls />
|
||||
<spotLight
|
||||
ref={flashlightRef}
|
||||
distance={20}
|
||||
angle={0.4}
|
||||
penumbra={0.7}
|
||||
distance={25}
|
||||
angle={0.35}
|
||||
penumbra={0.8}
|
||||
intensity={0}
|
||||
color="#fffaed"
|
||||
decay={1.7}
|
||||
decay={2}
|
||||
castShadow
|
||||
shadow-bias={-0.001}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user