world structure: arrays to records
object move with mouse
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import type { ObjectInstance } from "../types";
|
||||
import { useRef } from "react";
|
||||
import type { Mesh } from "three";
|
||||
import { Edges } from "@react-three/drei";
|
||||
import { useEffect, useRef, type RefObject } from "react";
|
||||
import type { Group, Mesh } from "three";
|
||||
import { Edges, TransformControls } from "@react-three/drei";
|
||||
import { state } from "../state";
|
||||
|
||||
type ObjectViewProps = {
|
||||
@@ -10,39 +10,61 @@ type ObjectViewProps = {
|
||||
}
|
||||
|
||||
export const ObjectView = observer(function ({ object }: ObjectViewProps) {
|
||||
const meshRef = useRef<Mesh>(null);
|
||||
const groupRef = useRef<Group>(null);
|
||||
const controlsRef = useRef<any>(null);
|
||||
|
||||
const objectType = state.world.getObjectTypeById(object.typeId);
|
||||
if (!objectType)
|
||||
return null;
|
||||
|
||||
const isSelected = state.worldEditor.isEnabled &&
|
||||
const isSelected = state.worldEditor.isEnabled &&
|
||||
state.worldEditor.selectedObjectId === object.id;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSelected)
|
||||
return;
|
||||
const controls = controlsRef.current;
|
||||
if (!controls)
|
||||
return;
|
||||
const onDragChanged = (e: { value: boolean }) => state.worldEditor.setIsDragging(e.value);
|
||||
controls.addEventListener('dragging-changed', onDragChanged);
|
||||
return () => {
|
||||
controls.removeEventListener('dragging-changed', onDragChanged);
|
||||
state.worldEditor.setIsDragging(false);
|
||||
};
|
||||
}, [isSelected]);
|
||||
|
||||
const handleClick = (e: { stopPropagation: () => void }) => {
|
||||
if (!state.worldEditor.isEnabled)
|
||||
return;
|
||||
e.stopPropagation();
|
||||
|
||||
state.worldEditor.setSelectedObjectId(object.id);
|
||||
};
|
||||
|
||||
const handleObjectChange = () => {
|
||||
const group = groupRef.current;
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
const { x, y, z } = group.position;
|
||||
state.worldEditor.setObjectPosition(object.id, [x, y, z]);
|
||||
};
|
||||
|
||||
return (<>
|
||||
<group
|
||||
position={object.position}
|
||||
rotation={object.rotation}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{(isSelected && groupRef.current) && <TransformControls object={groupRef as RefObject<Group>} onChange={handleObjectChange} mode="translate" />}
|
||||
<group ref={groupRef} position={object.position} rotation={object.rotation} onClick={handleClick}>
|
||||
{
|
||||
objectType.voxels.map((v, idx) => {
|
||||
const vt = state.world.getVoxelTypeById(v.typeId);
|
||||
return <mesh key={idx} ref={meshRef} position={v.position}>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshStandardMaterial color={(v.color ?? vt?.color) ?? 'white'} opacity={(v.opacity ?? vt?.opacity) ?? 1} />
|
||||
{isSelected && <Edges color="white" lineWidth={3} stencilWrite={false} />}
|
||||
</mesh>
|
||||
return (
|
||||
<mesh key={idx} position={v.position}>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshStandardMaterial color={(v.color ?? vt?.color) ?? 'white'} opacity={(v.opacity ?? vt?.opacity) ?? 1} />
|
||||
{isSelected && <Edges color="white" lineWidth={3} stencilWrite={false} />}
|
||||
</mesh>
|
||||
);
|
||||
})
|
||||
}
|
||||
</group>
|
||||
</>);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ import { WorldView } from './WorldView';
|
||||
export const ThreeView = observer(function () {
|
||||
return (
|
||||
<div style={{ width: '800px', height: '600px', border: '1px solid #333', borderRadius: '8px', overflow: 'hidden' }}>
|
||||
<Canvas camera={state.world.character.camera}>
|
||||
<Canvas
|
||||
camera={state.world.character.camera}
|
||||
onPointerMissed={() => state.worldEditor.setSelectedObjectId(undefined)}
|
||||
>
|
||||
<WorldView />
|
||||
</Canvas>
|
||||
</div>
|
||||
|
||||
@@ -5,11 +5,16 @@ import type { RunningGameState } from "../types";
|
||||
|
||||
export const Toolbar = observer(function () {
|
||||
|
||||
function handleCloneTest1ObjectClick(): void {
|
||||
function handleCloneTest1Object(): void {
|
||||
state.worldEditor.addObjectCloneAtRandomPosition('test1');
|
||||
}
|
||||
|
||||
function handleLoadMockWorld(): void {
|
||||
state.world.loadMock();
|
||||
}
|
||||
|
||||
return <div className="toolbar">
|
||||
{state.worldEditor.isEnabled && <div>EDITOR MODE</div>}
|
||||
{state.world.isPlaying
|
||||
? <>
|
||||
<button onClick={() => state.world.stop()}>Stop</button>
|
||||
@@ -21,6 +26,7 @@ export const Toolbar = observer(function () {
|
||||
</>
|
||||
: <button onClick={() => state.world.play()}>Play</button>
|
||||
}
|
||||
<button onClick={handleCloneTest1ObjectClick}>Clone test1</button>
|
||||
<button onClick={handleLoadMockWorld}>Load mock world</button>
|
||||
<button onClick={handleCloneTest1Object}>Clone test1</button>
|
||||
</div>
|
||||
});
|
||||
|
||||
@@ -11,8 +11,6 @@ export const WorldView = observer(function () {
|
||||
const world = state.world;
|
||||
const controlsRef = useRef<React.ComponentRef<typeof OrbitControls>>(null);
|
||||
|
||||
console.log(world.currentScene.objects);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
world.tick(delta);
|
||||
});
|
||||
@@ -31,13 +29,14 @@ export const WorldView = observer(function () {
|
||||
return (<>
|
||||
<OrbitControls
|
||||
ref={controlsRef}
|
||||
enabled={!world.isPlaying}
|
||||
enabled={!world.isPlaying && !state.worldEditor.isDragging}
|
||||
onEnd={handleEnd}
|
||||
makeDefault
|
||||
enableDamping={false}
|
||||
/>
|
||||
<ambientLight intensity={0.5} />
|
||||
<directionalLight position={[5, 5, 5]} intensity={1} />
|
||||
{world.currentScene.objects.map((obj) => <ObjectView key={obj.id} object={obj} />)}
|
||||
{Object.values(world.currentScene.objects).map((obj) => <ObjectView key={obj.id} object={obj} />)}
|
||||
<CharacterView />
|
||||
</>)
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user