blockly3d/src/components/ThreeView.tsx

73 lines
3.4 KiB
TypeScript

import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { KeyboardControls, Stats } from '@react-three/drei';
import { useRef } from 'react';
function RenderInfoUpdater({ domRef }: { domRef: React.RefObject<HTMLDivElement | null> }) {
const { gl } = useThree();
useFrame(() => {
if (!domRef.current)
return;
const { calls, triangles } = gl.info.render;
const shaders = gl.info.programs?.length ?? 0;
const { geometries, textures } = gl.info.memory;
domRef.current.textContent = `draw: ${calls} | tri: ${triangles} | shaders: ${shaders} | geometries: ${geometries} | textures: ${textures}`;
});
return null;
}
import { observer } from 'mobx-react-lite';
import { state } from '../state';
import { GameView } from './GameView';
import { SceneEditorView } from './SceneEditorView';
const IconStop = () => <svg viewBox="0 0 14 14"><rect x="2" y="2" width="10" height="10" fill="currentColor" /></svg>;
const IconPause = () => <svg viewBox="0 0 14 14"><rect x="2" y="2" width="4" height="10" fill="currentColor" /><rect x="8" y="2" width="4" height="10" fill="currentColor" /></svg >;
const IconPlay = () => <svg viewBox="0 0 14 14"><polygon points="3,1 13,7 3,13" fill="currentColor" /></svg>;
export const ThreeView = observer(function () {
const isGame = state.isGamePlaying;
const infoRef = useRef<HTMLDivElement>(null);
return (
<KeyboardControls map={[
{ name: 'forward', keys: ['ArrowUp', 'w', 'W'] },
{ name: 'backward', keys: ['ArrowDown', 's', 'S'] },
{ name: 'left', keys: ['ArrowLeft', 'a', 'A'] },
{ name: 'right', keys: ['ArrowRight', 'd', 'D'] },
{ name: 'jump', keys: ['Space'] },
]}>
<div style={{ position: 'relative', width: '800px', height: '600px', border: '1px solid #333', borderRadius: '8px', overflow: 'hidden' }}>
<Canvas
// camera={state.world.character.camera}
onPointerMissed={() => state.worldEditor.resetSelectedObject()}
>
<Stats />
<RenderInfoUpdater domRef={infoRef} />
{isGame ? <GameView /> : <SceneEditorView />}
</Canvas>
<div ref={infoRef} style={{
position: 'absolute', bottom: 8, left: 8,
color: 'white', fontSize: 11, fontFamily: 'monospace',
background: 'rgba(0,0,0,0.5)', padding: '2px 6px', borderRadius: 3,
pointerEvents: 'none',
}} />
<div style={{ position: 'absolute', top: 8, right: 8, display: 'flex', gap: 4 }}>
{
state.game
? <>
<button onClick={() => state.stopGame()}><IconStop /></button>
{
state.game!.isPaused
? <button onClick={() => state.game!.resume()}><IconPlay /></button>
: <button onClick={() => state.game!.pause()}><IconPause /></button>
}
</>
: <button onClick={() => state.startGame()}><IconPlay /></button>
}
</div>
</div>
</KeyboardControls>
)
});