Feat/polish-mission1 #12

Merged
math-pixel merged 42 commits from feat/polish-mission1 into develop 2026-06-01 21:51:09 +00:00
2 changed files with 35 additions and 11 deletions
Showing only changes of commit 3b07f40f2d - Show all commits
+31 -10
View File
@@ -14,7 +14,16 @@ const TALKIE_REVEAL_STEPS = new Set([
"completed", "completed",
]); ]);
function TalkieModel(): React.JSX.Element { const TALKIE_REST_Y = -0.55;
const TALKIE_ACTIVE_Y = -0.18;
const TALKIE_FLOAT_Y_AMPLITUDE = 0.025;
const TALKIE_FLOAT_ROTATION_AMPLITUDE = THREE.MathUtils.degToRad(1.6);
interface TalkieModelProps {
active: boolean;
}
function TalkieModel({ active }: TalkieModelProps): React.JSX.Element {
const { scene } = useGLTF(TALKIE_MODEL_PATH); const { scene } = useGLTF(TALKIE_MODEL_PATH);
const model = useMemo(() => scene.clone(true), [scene]); const model = useMemo(() => scene.clone(true), [scene]);
const groupRef = useRef<THREE.Group>(null); const groupRef = useRef<THREE.Group>(null);
@@ -33,15 +42,26 @@ function TalkieModel(): React.JSX.Element {
if (!groupRef.current) return; if (!groupRef.current) return;
const t = clock.getElapsedTime(); const t = clock.getElapsedTime();
groupRef.current.rotation.z = Math.sin(t * 22) * 0.025; const floatY = Math.sin(t * 1.4) * TALKIE_FLOAT_Y_AMPLITUDE;
groupRef.current.position.y = Math.sin(t * 6) * 0.012; const targetY = (active ? TALKIE_ACTIVE_Y : TALKIE_REST_Y) + floatY;
groupRef.current.position.y = THREE.MathUtils.lerp(
groupRef.current.position.y,
targetY,
0.14,
);
if (active) {
groupRef.current.rotation.z = Math.sin(t * 22) * 0.025;
} else {
groupRef.current.rotation.z =
Math.sin(t * 0.8) * TALKIE_FLOAT_ROTATION_AMPLITUDE;
}
}); });
return ( return (
<group ref={groupRef}> <group ref={groupRef} position={[0, TALKIE_REST_Y, 0]}>
<primitive <primitive
object={model} object={model}
position={[0, -0.18, 0]}
rotation={[0.18, Math.PI, -0.08]} rotation={[0.18, Math.PI, -0.08]}
scale={1.45} scale={1.45}
/> />
@@ -73,11 +93,12 @@ export function TalkieDialogueOverlay(): React.JSX.Element | null {
if (!isAfterReveal) return null; if (!isAfterReveal) return null;
const overlayClassName = isNarratorDialogue
? "talkie-dialogue-overlay talkie-dialogue-overlay--active talkie-dialogue-overlay--raised"
: "talkie-dialogue-overlay";
return ( return (
<aside <aside className={overlayClassName} aria-hidden="true">
className={`talkie-dialogue-overlay${isNarratorDialogue ? " talkie-dialogue-overlay--raised" : ""}`}
aria-hidden="true"
>
{isNarratorDialogue ? <TalkieSignalLines /> : null} {isNarratorDialogue ? <TalkieSignalLines /> : null}
<div className="talkie-dialogue-overlay__model-frame"> <div className="talkie-dialogue-overlay__model-frame">
<Canvas <Canvas
@@ -89,7 +110,7 @@ export function TalkieDialogueOverlay(): React.JSX.Element | null {
<ambientLight intensity={2.5} /> <ambientLight intensity={2.5} />
<directionalLight position={[2, 3, 4]} intensity={2.8} /> <directionalLight position={[2, 3, 4]} intensity={2.8} />
<Suspense fallback={null}> <Suspense fallback={null}>
<TalkieModel /> <TalkieModel active={isNarratorDialogue} />
</Suspense> </Suspense>
</Canvas> </Canvas>
</div> </div>
+4 -1
View File
@@ -1257,10 +1257,13 @@ canvas {
.talkie-dialogue-overlay__model-frame { .talkie-dialogue-overlay__model-frame {
position: absolute; position: absolute;
inset: 0; inset: 0;
animation: talkie-radio-shake 1s ease-in-out infinite;
filter: drop-shadow(0 16px 22px rgba(0, 0, 0, 0.55)); filter: drop-shadow(0 16px 22px rgba(0, 0, 0, 0.55));
} }
.talkie-dialogue-overlay--active .talkie-dialogue-overlay__model-frame {
animation: talkie-radio-shake 1s ease-in-out infinite;
}
.talkie-dialogue-overlay__model-frame canvas { .talkie-dialogue-overlay__model-frame canvas {
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;