world autosave on any edit/change

This commit is contained in:
azykov@mail.ru
2026-06-02 19:07:30 +03:00
parent 9217220505
commit 9145277681
7 changed files with 86 additions and 44 deletions
+14 -30
View File
@@ -1,6 +1,6 @@
import { observer } from "mobx-react-lite";
import type { ObjectInstance } from "../types";
import { useEffect, useRef, type RefObject } from "react";
import { useRef, type RefObject } from "react";
import type { Group } from "three";
import { TransformControls, useHelper } from "@react-three/drei";
import { BoxHelper } from "three";
@@ -15,7 +15,6 @@ type ObjectViewProps = {
export const ObjectView = observer(function ({ object }: ObjectViewProps) {
const groupRef = useRef<Group>(null);
const controlsRef = useRef<any>(null);
const isSelected = state.worldEditor.isEnabled &&
state.worldEditor.selectedObjectId === object.id;
@@ -29,48 +28,33 @@ export const ObjectView = observer(function ({ object }: ObjectViewProps) {
? state.worldEditor.selectedObjectMode
: undefined;
useEffect(() => {
if (!isSelected)
return;
const controls = controlsRef.current;
if (!controls)
return;
const onDragChanged = (e: { value: boolean }) => {
state.worldEditor.setIsDragging(e.value);
if (!e.value) {
const group = groupRef.current;
if (group)
state.worldEditor.setObjectTransform(
object.id,
group.position.toArray(),
group.rotation.toArray().slice(0, 3) as R3,
group.scale.toArray(),
);
}
};
controls.addEventListener('dragging-changed', onDragChanged);
return () => {
controls.removeEventListener('dragging-changed', onDragChanged);
state.worldEditor.setIsDragging(false);
};
}, [isSelected]);
const handleClick = (e: ThreeEvent<MouseEvent>) => {
function handleClick(e: ThreeEvent<MouseEvent>) {
if (!state.worldEditor.isEnabled)
return;
if (e.delta > 5)
return;
e.stopPropagation();
state.worldEditor.setSelectedObject(object.id, nextSelectionEditMode(selectionMode));
};
function handleTransformEnd() {
const group = groupRef.current;
if (group)
state.worldEditor.setObjectTransform(
object.id,
group.position.toArray(),
group.rotation.toArray().slice(0, 3) as R3,
group.scale.toArray(),
);
};
return (<>
{
(isSelected && (selectionMode !== undefined) && groupRef.current) &&
<TransformControls
object={groupRef as RefObject<Group>}
mode={selectionMode}
onMouseUp={handleTransformEnd}
/>
}
<group
+21 -1
View File
@@ -6,6 +6,7 @@ import { observer } from 'mobx-react-lite';
import { state } from '../state';
import { SceneView } from './SceneView';
import type { Pos3 } from '../types/3d';
import type { OrthographicCamera, PerspectiveCamera } from 'three';
const CameraSync = observer(function ({ camera }: { camera: Pos3 }) {
const { camera: threeCamera } = useThree();
@@ -22,10 +23,29 @@ const CameraSync = observer(function ({ camera }: { camera: Pos3 }) {
export const SceneEditorView = observer(function () {
const controlsRef = useRef<React.ComponentRef<typeof OrbitControls>>(null);
const cameraAtStart = useRef<string>('');
function cameraSnapshot(camera: OrthographicCamera | PerspectiveCamera): string {
return JSON.stringify({
position: camera.position,
rotation: camera.rotation,
});
}
const handleStart = () => {
const controls = controlsRef.current;
if (!controls)
return;
cameraAtStart.current = cameraSnapshot(controls.object);
};
const handleEnd = () => {
const controls = controlsRef.current;
if (!controls)
return;
const snapshot = cameraSnapshot(controls.object);
if (snapshot === cameraAtStart.current)
return;
const [x, y, z] = controls.object.rotation.toArray();
state.worldEditor.setCamera({
position: controls.object.position.toArray(),
@@ -36,7 +56,7 @@ export const SceneEditorView = observer(function () {
return (<>
<OrbitControls
ref={controlsRef}
enabled={!state.worldEditor.isDragging}
onStart={handleStart}
onEnd={handleEnd}
makeDefault
enableDamping={false}
+5
View File
@@ -8,11 +8,16 @@ export const Toolbar = observer(function () {
state.worldEditor.addObjectCloneAtRandomPosition('test1');
}
function handleLoadWorld(): void {
state.world.load();
}
function handleLoadMockWorld(): void {
state.world.loadMock();
}
return <div className="toolbar">
<button onClick={handleLoadWorld}>Load</button>
<button onClick={handleLoadMockWorld}>Load mock world</button>
<button onClick={handleCloneTest1Object}>Clone test1</button>
</div>