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 132 133 134 | 159x 159x 159x 159x 80x 80x 80x 159x 90x 50x 40x 10x 30x 10x 20x 90x 8x 8x 159x | /**
* useMuscleActivation - Shared hook for muscle activation management
*
* Manages muscle activation state based on current actions and stamina.
* Reduces code duplication in skeletal animation components.
*
* @module hooks/useMuscleActivation
* @category Hooks
* @korean 근육활성화훅
*/
import { useEffect, useRef, useState } from "react";
import { MuscleActivationManager } from "../systems/animation/MuscleActivation";
import type { PlayerAnimation } from "../types/player-visual";
/**
* Options for useMuscleActivation hook
* @korean 근육활성화훅옵션
*/
export interface UseMuscleActivationOptions {
/** Current animation name */
readonly currentAnimation: PlayerAnimation;
/** Specific attack animation name (for attack state) */
readonly attackAnimation?: string;
/** Whether player is blocking */
readonly isBlocking?: boolean;
/** Current stamina level (0-100) */
readonly stamina: number;
}
/**
* Return type for useMuscleActivation hook
* @korean 근육활성화훅반환타입
*/
export interface UseMuscleActivationReturn {
/** Current muscle activation states (bone name -> activation 0-1) */
readonly muscleStates: Map<string, number>;
/** Update muscle activations (call in useFrame) */
readonly updateMuscleActivations: (delta: number, frameCounter: number) => void;
}
/**
* useMuscleActivation hook
*
* Manages muscle activation based on current actions (attack, defend, movement).
* Updates at 60fps with periodic state syncs to reduce re-renders.
*
* @param options - Muscle activation options
* @returns Muscle states and update function
*
* @example
* ```tsx
* const { muscleStates, updateMuscleActivations } = useMuscleActivation({
* currentAnimation: "attack",
* attackAnimation: "jab",
* isBlocking: false,
* stamina: 85,
* });
*
* // In useFrame callback
* let frameCounter = 0;
* useFrame((_, delta) => {
* frameCounter = (frameCounter + 1) % 10;
* updateMuscleActivations(delta, frameCounter);
* });
*
* // Use muscle states in rendering
* <BoneRenderer
* rig={rig}
* muscleStates={muscleStates}
* isExhausted={stamina < 20}
* />
* ```
*
* @korean 근육활성화훅
*/
export function useMuscleActivation(
options: UseMuscleActivationOptions
): UseMuscleActivationReturn {
const { currentAnimation, attackAnimation, isBlocking = false, stamina } = options;
// Muscle activation manager
const muscleManager = useRef(new MuscleActivationManager());
const [muscleStates, setMuscleStates] = useState<Map<string, number>>(
new Map()
);
// Cleanup muscle manager on unmount
useEffect(() => {
return () => {
try {
muscleManager.current.reset();
} catch (error) {
console.warn("MuscleActivationManager reset failed:", error);
}
};
}, []);
// Update muscle activations (called at 60fps in useFrame)
const updateMuscleActivations = (
delta: number,
frameCounter: number
): void => {
// Update muscle system based on current action
if (currentAnimation === "attack" && attackAnimation) {
muscleManager.current.update(attackAnimation, stamina, delta);
} else if (currentAnimation === "defend" || isBlocking) {
muscleManager.current.update("block", stamina, delta);
} else if (
currentAnimation === "walk" ||
currentAnimation === "stance_change"
) {
// Engage stance/leg/core muscles during movement and stance changes
muscleManager.current.update("stance_change", stamina, delta);
} else {
muscleManager.current.relaxAllMuscles(delta);
}
// Sync muscle states to React state deterministically
// (every 10 frames at 60fps = ~6 times/sec)
// Balances animation smoothness with performance and reduces GC pressure
if (frameCounter === 0) {
// Reuse scratch map from manager to avoid repeated allocations
const scratchMap = muscleManager.current.getScratchMapForSync();
setMuscleStates(scratchMap);
}
};
return {
muscleStates,
updateMuscleActivations,
};
}
|