73 lines
3.4 KiB
TypeScript
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>
|
|
)
|
|
});
|