feat: add bark battle debug feedback
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
type BarkBattleConfig,
|
||||
@@ -12,6 +12,11 @@ type BarkBattleRuntimeShellProps = {
|
||||
title?: string;
|
||||
};
|
||||
|
||||
type DebugEvent = {
|
||||
id: number;
|
||||
text: string;
|
||||
};
|
||||
|
||||
const DEBUG_CONFIG_FIELDS: Array<{
|
||||
key: keyof Pick<
|
||||
BarkBattleConfig,
|
||||
@@ -46,12 +51,39 @@ export function BarkBattleRuntimeShell({ title = '汪汪声浪大作战' }: Bark
|
||||
const controller = controllerRef.current;
|
||||
const [snapshot, setSnapshot] = useState(() => controller.getSnapshot());
|
||||
const [particleText, setParticleText] = useState('');
|
||||
const [playerPulseKey, setPlayerPulseKey] = useState(0);
|
||||
const [opponentPulseKey, setOpponentPulseKey] = useState(0);
|
||||
const [debugEvents, setDebugEvents] = useState<DebugEvent[]>([]);
|
||||
const heldRef = useRef(false);
|
||||
const lastPlayerBarkCountRef = useRef(0);
|
||||
const lastOpponentPowerRef = useRef(0);
|
||||
const debugEventIdRef = useRef(0);
|
||||
|
||||
const appendDebugEvent = useCallback((text: string) => {
|
||||
debugEventIdRef.current += 1;
|
||||
const event = { id: debugEventIdRef.current, text };
|
||||
setDebugEvents((current) => [event, ...current].slice(0, 5));
|
||||
}, []);
|
||||
|
||||
const syncSnapshot = useCallback(() => {
|
||||
const nextSnapshot = controller.getSnapshot();
|
||||
if (nextSnapshot.player.barkCount > lastPlayerBarkCountRef.current) {
|
||||
setPlayerPulseKey((current) => current + 1);
|
||||
appendDebugEvent(`玩家叫声触发 #${nextSnapshot.player.barkCount} · 能量 ${Math.round(nextSnapshot.energy)}`);
|
||||
}
|
||||
if (nextSnapshot.phase === 'playing' && Math.abs(nextSnapshot.opponent.power - lastOpponentPowerRef.current) >= 0.08) {
|
||||
setOpponentPulseKey((current) => current + 1);
|
||||
appendDebugEvent(`对手反击强度 ${(nextSnapshot.opponent.power * 100).toFixed(0)}%`);
|
||||
}
|
||||
lastPlayerBarkCountRef.current = nextSnapshot.player.barkCount;
|
||||
lastOpponentPowerRef.current = nextSnapshot.opponent.power;
|
||||
setSnapshot(nextSnapshot);
|
||||
}, [appendDebugEvent, controller]);
|
||||
|
||||
useEffect(() => {
|
||||
controller.updateConfig(config);
|
||||
setSnapshot(controller.getSnapshot());
|
||||
}, [config, controller]);
|
||||
syncSnapshot();
|
||||
}, [config, controller, syncSnapshot]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = window.setInterval(() => {
|
||||
@@ -61,31 +93,38 @@ export function BarkBattleRuntimeShell({ title = '汪汪声浪大作战' }: Bark
|
||||
} else {
|
||||
controller.submitMockSample(0.12);
|
||||
}
|
||||
setSnapshot(controller.getSnapshot());
|
||||
syncSnapshot();
|
||||
}, 100);
|
||||
return () => window.clearInterval(timer);
|
||||
}, [controller]);
|
||||
}, [controller, syncSnapshot]);
|
||||
|
||||
const restart = () => {
|
||||
heldRef.current = false;
|
||||
controller.restart();
|
||||
setParticleText('');
|
||||
setSnapshot(controller.getSnapshot());
|
||||
setDebugEvents([]);
|
||||
lastPlayerBarkCountRef.current = 0;
|
||||
lastOpponentPowerRef.current = 0;
|
||||
syncSnapshot();
|
||||
};
|
||||
|
||||
const startMock = () => {
|
||||
controller.startWithMockInput();
|
||||
setSnapshot(controller.getSnapshot());
|
||||
appendDebugEvent('开始 mock 对局');
|
||||
syncSnapshot();
|
||||
};
|
||||
|
||||
const finishNow = () => {
|
||||
heldRef.current = false;
|
||||
controller.finishNow();
|
||||
setSnapshot(controller.getSnapshot());
|
||||
appendDebugEvent('人工结束对局');
|
||||
syncSnapshot();
|
||||
};
|
||||
|
||||
const bark = () => {
|
||||
heldRef.current = true;
|
||||
setPlayerPulseKey((current) => current + 1);
|
||||
appendDebugEvent('按下模拟叫声按钮');
|
||||
setParticleText('汪!');
|
||||
window.setTimeout(() => setParticleText(''), 680);
|
||||
};
|
||||
@@ -94,6 +133,8 @@ export function BarkBattleRuntimeShell({ title = '汪汪声浪大作战' }: Bark
|
||||
<main className="bark-battle-runtime" aria-label={title}>
|
||||
<BarkBattleHud
|
||||
snapshot={snapshot}
|
||||
playerPulseKey={playerPulseKey}
|
||||
opponentPulseKey={opponentPulseKey}
|
||||
onStartMicrophone={startMock}
|
||||
onMockBark={bark}
|
||||
onMockQuiet={() => {
|
||||
@@ -111,6 +152,15 @@ export function BarkBattleRuntimeShell({ title = '汪汪声浪大作战' }: Bark
|
||||
<button type="button" onClick={finishNow}>结束</button>
|
||||
<button type="button" onClick={restart}>重置</button>
|
||||
</div>
|
||||
<div className="bark-battle-debug-metrics" aria-label="触发反馈">
|
||||
<span>玩家触发:{snapshot.player.barkCount}</span>
|
||||
<span>玩家强度:{(snapshot.player.power * 100).toFixed(0)}%</span>
|
||||
<span>对手强度:{(snapshot.opponent.power * 100).toFixed(0)}%</span>
|
||||
<span>能量:{Math.round(snapshot.energy)}</span>
|
||||
</div>
|
||||
<ol className="bark-battle-debug-events" aria-label="触发日志">
|
||||
{debugEvents.length ? debugEvents.map((event) => <li key={event.id}>{event.text}</li>) : <li>等待输入触发</li>}
|
||||
</ol>
|
||||
{DEBUG_CONFIG_FIELDS.map((field) => (
|
||||
<label key={field.key}>
|
||||
<span>{field.label}</span>
|
||||
|
||||
Reference in New Issue
Block a user