Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | 2x 132x 132x 132x 132x 132x | /**
* AccelerationUpdater - Component that updates movement acceleration at 60fps
*
* Uses useFrame to track continuous movement time and calculate acceleration-based speed.
* Throttles state updates to only call onSpeedUpdate when speed changes meaningfully.
* This component only updates movement state and renders no visual elements.
*
* @module systems/movement/helpers/AccelerationUpdater
* @category Movement
* @korean 가속업데이터
*/
import { useFrame } from "@react-three/fiber";
import React, { useRef } from "react";
import {
ACCELERATION_CONSTANTS,
calculateAcceleratedSpeed,
isDirectionConsistent,
isSpeedChangeMeaningful,
} from "./accelerationUtils";
/**
* Props for AccelerationUpdater component
*/
export interface AccelerationUpdaterProps {
/** Whether player is currently moving */
readonly isMoving: boolean;
/** Current velocity vector */
readonly velocity: { x: number; y: number } | undefined;
/** Ref to track accumulated movement time */
readonly movementTimeRef: React.MutableRefObject<number>;
/** Ref to track last movement direction */
readonly lastDirectionRef: React.MutableRefObject<{ x: number; y: number }>;
/** Callback to update calculated speed - only called on meaningful changes */
readonly onSpeedUpdate: (speed: number) => void;
/** Walking speed in m/s (from archetype or default) */
readonly walkSpeed?: number;
/** Running speed in m/s (from archetype or default) */
readonly runSpeed?: number;
}
/**
* AccelerationUpdater Component
*
* Updates movement acceleration at 60fps using Three.js useFrame hook.
* Tracks continuous movement time and calculates speed based on direction consistency.
* Only calls onSpeedUpdate when speed changes by more than epsilon AND sufficient time
* has passed (100ms throttle), preventing excessive React re-renders at frame rate.
*
* @example
* ```tsx
* <AccelerationUpdater
* isMoving={isMoving}
* velocity={velocity}
* movementTimeRef={movementTimeRef}
* lastDirectionRef={lastDirectionRef}
* onSpeedUpdate={setAccelerationBasedSpeed}
* walkSpeed={physicalAttributes.walkSpeed}
* runSpeed={physicalAttributes.runSpeed}
* />
* ```
*/
export const AccelerationUpdater: React.FC<AccelerationUpdaterProps> = ({
isMoving,
velocity,
movementTimeRef,
lastDirectionRef,
onSpeedUpdate,
walkSpeed = ACCELERATION_CONSTANTS.DEFAULT_WALK_SPEED,
runSpeed = ACCELERATION_CONSTANTS.DEFAULT_RUN_SPEED,
}) => {
// Track last reported speed and time to throttle updates
// Initialize with walk speed (archetype-specific or default)
const lastReportedSpeedRef = useRef<number>(walkSpeed);
const lastUpdateTimeRef = useRef<number>(0);
// Throttle interval: update at most every ~100ms (10Hz) instead of 60fps
const UPDATE_THROTTLE_MS = 100;
useFrame((_state, delta) => {
// If not moving, reset timers and direction
if (!isMoving || !velocity || (velocity.x === 0 && velocity.y === 0)) {
movementTimeRef.current = 0;
lastDirectionRef.current = { x: 0, y: 0 };
// Only update if changed meaningfully
if (isSpeedChangeMeaningful(lastReportedSpeedRef.current, walkSpeed)) {
lastReportedSpeedRef.current = walkSpeed;
onSpeedUpdate(walkSpeed);
lastUpdateTimeRef.current = performance.now();
}
return;
}
// Check direction consistency (within 45 degrees)
const currentDir = { x: velocity.x, y: velocity.y };
const isSameDirection = isDirectionConsistent(currentDir, lastDirectionRef.current);
// Reset accumulated movement time if direction changed too much
if (!isSameDirection) {
movementTimeRef.current = 0;
} else {
// Accumulate movement time while moving in a consistent direction
movementTimeRef.current += delta;
}
// Update last direction for the next frame
lastDirectionRef.current = currentDir;
// Calculate new speed with archetype-specific walk/run speeds
const newSpeed = calculateAcceleratedSpeed(movementTimeRef.current, walkSpeed, runSpeed);
// Throttle updates by both time and epsilon
// Only call onSpeedUpdate if enough time has passed AND speed changed meaningfully
const now = performance.now();
const timeSinceLastUpdate = now - lastUpdateTimeRef.current;
if (
timeSinceLastUpdate >= UPDATE_THROTTLE_MS &&
isSpeedChangeMeaningful(lastReportedSpeedRef.current, newSpeed)
) {
lastReportedSpeedRef.current = newSpeed;
onSpeedUpdate(newSpeed);
lastUpdateTimeRef.current = now;
}
});
return null; // Component only updates movement state, renders no visual elements
};
export default AccelerationUpdater;
|