scss instead of css
floating play-pause buttons character model simplification
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { state } from "../state";
|
||||
import type { Character, Scene } from "../types";
|
||||
|
||||
export const CharacterView = observer(function () {
|
||||
const character = state.world.currentScene.character;
|
||||
export const CharacterView = observer(function ({ character }: { character: Character }) {
|
||||
|
||||
return <mesh position={character.position} rotation={character.look}>
|
||||
<boxGeometry args={[0.8, 0.8, 0.8]} />
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { SceneView } from "./SceneView";
|
||||
import { state } from "../state";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
|
||||
export const GameView = observer(function () {
|
||||
const game = state.game;
|
||||
|
||||
useFrame((_, delta) => {
|
||||
state.game?.tick(delta);
|
||||
});
|
||||
|
||||
if (!game)
|
||||
return null;
|
||||
|
||||
return <SceneView scene={game.scene} />;
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useLayoutEffect, useRef } from 'react';
|
||||
import type React from 'react';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { OrbitControls } from '@react-three/drei';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { state } from '../state';
|
||||
import { SceneView } from './SceneView';
|
||||
import type { Pos3 } from '../types/3d';
|
||||
|
||||
const CameraSync = observer(function ({ camera }: { camera: Pos3 }) {
|
||||
const { camera: threeCamera } = useThree();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
threeCamera.position.set(camera.position[0], camera.position[1], camera.position[2]);
|
||||
threeCamera.rotation.set(camera.look[0], camera.look[1], camera.look[2]);
|
||||
threeCamera.updateProjectionMatrix();
|
||||
});
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
export const SceneEditorView = observer(function () {
|
||||
const controlsRef = useRef<React.ComponentRef<typeof OrbitControls>>(null);
|
||||
|
||||
const handleEnd = () => {
|
||||
const controls = controlsRef.current;
|
||||
if (!controls)
|
||||
return;
|
||||
const [x, y, z] = controls.object.rotation.toArray();
|
||||
state.worldEditor.setCamera({
|
||||
position: controls.object.position.toArray(),
|
||||
look: [x, y, z],
|
||||
});
|
||||
};
|
||||
|
||||
return (<>
|
||||
<OrbitControls
|
||||
ref={controlsRef}
|
||||
enabled={!state.worldEditor.isDragging}
|
||||
onEnd={handleEnd}
|
||||
makeDefault
|
||||
enableDamping={false}
|
||||
/>
|
||||
<CameraSync camera={state.worldEditor.camera} />
|
||||
<SceneView scene={state.worldEditor.scene} />
|
||||
</>);
|
||||
});
|
||||
@@ -13,6 +13,6 @@ export const SceneView = observer(function ({ scene }: SceneViewProps) {
|
||||
<directionalLight position={[5, 5, 5]} intensity={1} />
|
||||
{Object.values(scene.objects).map((obj) =>
|
||||
<ObjectView key={obj.id} object={obj} />)}
|
||||
<CharacterView />
|
||||
<CharacterView character={scene.character} />
|
||||
</>);
|
||||
});
|
||||
|
||||
@@ -1,17 +1,37 @@
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { state } from '../state';
|
||||
import { WorldView } from './WorldView';
|
||||
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;
|
||||
return (
|
||||
<div style={{ width: '800px', height: '600px', border: '1px solid #333', borderRadius: '8px', overflow: 'hidden' }}>
|
||||
<div style={{ position: 'relative', width: '800px', height: '600px', border: '1px solid #333', borderRadius: '8px', overflow: 'hidden' }}>
|
||||
<Canvas
|
||||
camera={state.world.character.camera}
|
||||
// camera={state.world.character.camera}
|
||||
onPointerMissed={() => state.worldEditor.resetSelectedObject()}
|
||||
>
|
||||
<WorldView />
|
||||
{isGame ? <GameView /> : <SceneEditorView />}
|
||||
</Canvas>
|
||||
<div style={{ position: 'absolute', top: 8, right: 8, display: 'flex', gap: 4 }}>
|
||||
{
|
||||
state.game
|
||||
? <>
|
||||
<button onClick={() => state.game!.stop()}><IconStop /></button>
|
||||
{
|
||||
state.game!.isPaused
|
||||
? <button onClick={() => state.game!.resume()}><IconPlay /></button>
|
||||
: <button onClick={() => state.game!.pause()}><IconPause /></button>
|
||||
}
|
||||
</>
|
||||
: <button onClick={() => state.world.play()}><IconPlay /></button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
|
||||
@@ -14,18 +14,6 @@ export const Toolbar = observer(function () {
|
||||
}
|
||||
|
||||
return <div className="toolbar">
|
||||
{state.worldEditor.isEnabled && <div>EDITOR MODE</div>}
|
||||
{state.world.isPlaying
|
||||
? <>
|
||||
<button onClick={() => state.world.stop()}>Stop</button>
|
||||
{
|
||||
(state.world.data.state as RunningGameState).paused
|
||||
? <button onClick={() => state.world.play()}>Play</button>
|
||||
: <button onClick={() => state.world.pause()}>Pause</button>
|
||||
}
|
||||
</>
|
||||
: <button onClick={() => state.world.play()}>Play</button>
|
||||
}
|
||||
<button onClick={handleLoadMockWorld}>Load mock world</button>
|
||||
<button onClick={handleCloneTest1Object}>Clone test1</button>
|
||||
</div>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import { useLayoutEffect, useRef } from 'react';
|
||||
import type React from 'react';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { OrbitControls } from '@react-three/drei';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { state } from '../state';
|
||||
import { SceneView } from './SceneView';
|
||||
|
||||
const CameraSync = observer(function () {
|
||||
const { camera } = useThree();
|
||||
const cam = state.world.currentCamera; // MobX tracks this; re-renders on change
|
||||
|
||||
useLayoutEffect(() => {
|
||||
camera.position.set(cam.position[0], cam.position[1], cam.position[2]);
|
||||
camera.rotation.set(cam.look[0], cam.look[1], cam.look[2]);
|
||||
camera.updateProjectionMatrix();
|
||||
});
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
export const WorldView = observer(function () {
|
||||
const world = state.world;
|
||||
const controlsRef = useRef<React.ComponentRef<typeof OrbitControls>>(null);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
world.tick(delta);
|
||||
});
|
||||
|
||||
const handleEnd = () => {
|
||||
const controls = controlsRef.current;
|
||||
if (!controls || world.isPlaying)
|
||||
return;
|
||||
const [x, y, z] = controls.object.rotation.toArray();
|
||||
state.worldEditor.setCamera({
|
||||
position: controls.object.position.toArray(),
|
||||
look: [x, y, z],
|
||||
});
|
||||
};
|
||||
|
||||
return (<>
|
||||
<OrbitControls
|
||||
ref={controlsRef}
|
||||
enabled={!world.isPlaying && !state.worldEditor.isDragging}
|
||||
onEnd={handleEnd}
|
||||
makeDefault
|
||||
enableDamping={false}
|
||||
/>
|
||||
<CameraSync />
|
||||
<SceneView scene={state.world.currentScene} />
|
||||
</>)
|
||||
});
|
||||
Reference in New Issue
Block a user