scss instead of css

floating play-pause buttons
character model simplification
This commit is contained in:
azykov@mail.ru
2026-06-02 17:32:50 +03:00
parent 45dbf7352c
commit e56f92a4ac
23 changed files with 403 additions and 219 deletions
+2 -3
View File
@@ -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]} />
+17
View File
@@ -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} />;
});
+47
View File
@@ -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} />
</>);
});
+1 -1
View File
@@ -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} />
</>);
});
+24 -4
View File
@@ -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>
)
});
-12
View File
@@ -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>
-52
View File
@@ -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} />
</>)
});