From b5a9772248f7673bf9c35551bd97b60d77ceb6b3 Mon Sep 17 00:00:00 2001 From: "azykov@mail.ru" Date: Wed, 3 Jun 2026 19:54:07 +0300 Subject: [PATCH] ConvexHullCollider instead of TriMesh --- src/components/GameView.tsx | 2 +- src/components/ObjectEditorView.tsx | 1 + src/components/ObjectViewInternal.tsx | 39 ++++++++++-------------- src/components/SceneView.tsx | 8 ++--- src/state/gameState.ts | 43 +++++++++++++++++++-------- src/types/model/runtime.ts | 1 + src/utils/runtime/object.ts | 2 ++ 7 files changed, 55 insertions(+), 41 deletions(-) diff --git a/src/components/GameView.tsx b/src/components/GameView.tsx index 087add3..b1fff92 100644 --- a/src/components/GameView.tsx +++ b/src/components/GameView.tsx @@ -58,7 +58,7 @@ export const GameView = observer(function () { return (<> - {/* */} + diff --git a/src/components/ObjectEditorView.tsx b/src/components/ObjectEditorView.tsx index 0528c9d..528041d 100644 --- a/src/components/ObjectEditorView.tsx +++ b/src/components/ObjectEditorView.tsx @@ -80,6 +80,7 @@ export const ObjectEditorView = observer(function ({ object }: ObjectEditorViewP diff --git a/src/components/ObjectViewInternal.tsx b/src/components/ObjectViewInternal.tsx index b5336e2..276e08d 100644 --- a/src/components/ObjectViewInternal.tsx +++ b/src/components/ObjectViewInternal.tsx @@ -1,43 +1,36 @@ import { observer } from "mobx-react-lite"; import type { ObjectType, RuntimeObjectInstance } from "../types"; -import { forwardRef, useMemo } from "react"; +import { forwardRef } from "react"; import type { Group } from "three"; import { Instance, Instances } from "@react-three/drei"; import type { ThreeEvent } from "@react-three/fiber"; -import { state } from "../state"; -import { TrimeshCollider } from "@react-three/rapier"; +import { ConvexHullCollider } from "@react-three/rapier"; import { SyncRigidBody } from "./SyncRigidBody"; -import { runInAction } from "mobx"; -import { buildObjectTrimesh } from "../utils/graphics/mesh"; type ObjectViewInternalProps = { object: Omit; objectType: ObjectType; + isEditor?: boolean; onClick?: (e: ThreeEvent) => void; } export const ObjectViewInternal = observer(forwardRef( - function ({ object, objectType, onClick }, ref) { - const trimeshArgs = useMemo( - () => buildObjectTrimesh(objectType, state.world.data.voxelTypes), - // eslint-disable-next-line react-hooks/exhaustive-deps - [objectType.id] - ); + function ({ object, objectType, ...props }, ref) { return ( { - runInAction(() => { - const obj = state.game?.scene.objects[object.id]; - if (obj) { - obj.position = data.position; - obj.rotation = data.rotation; - obj.linearVelocity = data.linearVelocity; - obj.radialVelocity = data.radialVelocity; - } - }); + onSync={(_data) => { + // TODO rewrite so that is does not trigger re-render + // runInAction(() => { + // object.position = data.position; + // object.rotation = data.rotation; + // if (!props.isEditor) { + // (object as RuntimeGameObjectInstance).linearVelocity = data.linearVelocity; + // (object as RuntimeGameObjectInstance).radialVelocity = data.radialVelocity; + // } + // }); }} > { object.cache.voxelGroups.map((vg) => @@ -57,7 +50,7 @@ export const ObjectViewInternal = observer(forwardRef ) } - {trimeshArgs && } + {object.cache.colliderMesh && } ); diff --git a/src/components/SceneView.tsx b/src/components/SceneView.tsx index 8ad814a..cf85138 100644 --- a/src/components/SceneView.tsx +++ b/src/components/SceneView.tsx @@ -2,8 +2,6 @@ import { observer } from "mobx-react-lite"; import type { RuntimeScene } from "../types"; import { CharacterView } from "./CharacterView"; import { GameObjectView } from "./GameObjectView"; -import { useFrame } from "@react-three/fiber"; -import { useRapier } from "@react-three/rapier"; import { ObjectEditorView } from "./ObjectEditorView"; type SceneViewProps = { @@ -12,9 +10,9 @@ type SceneViewProps = { } export const SceneView = observer(function (props: SceneViewProps) { - const rapier = useRapier(); + // const rapier = useRapier(); - useFrame((_, dt) => { + // useFrame((_, dt) => { // if (props.editMode) // return; @@ -23,7 +21,7 @@ export const SceneView = observer(function (props: SceneViewProps) { // return; // rapier.step(dt); - }) + // }) return (<> diff --git a/src/state/gameState.ts b/src/state/gameState.ts index 03b522c..c04e0df 100644 --- a/src/state/gameState.ts +++ b/src/state/gameState.ts @@ -7,10 +7,16 @@ import { GameFactory } from "../model/gameFactory"; export class GameState { private readonly world: WorldState; - public data: Game; + public isPaused: boolean = false; + public time: number = 0; + public scene: RuntimeGameScene; private startAutoSave() { - return reaction(() => toJS(this.data), (data) => this.saveData(data), { delay: 5000 }); + return reaction( + () => (this.asGame), + (data) => this.saveData(data), + { delay: 5000 } + ); } private withoutAutoSave(fn: () => void) { @@ -23,18 +29,31 @@ export class GameState { constructor(world: WorldState) { this.world = world; - this.data = GameFactory.create(toJS(this.world.data)); + const game = GameFactory.create(toJS(this.world.data)); + this.isPaused = game.paused; + this.time = game.time; + this.scene = game.scene; this._stopAutoSave = this.startAutoSave(); makeAutoObservable(this); } - public get isPaused(): boolean { - return this.data.paused; + public get asGame(): Game { + return { + paused: toJS(this.isPaused), + time: toJS(this.time), + scene: toJS(this.scene), + } } - public get scene(): RuntimeGameScene { - return this.data.scene; + public setGame(value: Game) { + this.isPaused = value.paused; + this.time = value.time; + this.scene = value.scene; + } + + public setIsPaused(value: boolean) { + this.isPaused = value; } public get camera(): Pos3 { @@ -63,19 +82,19 @@ export class GameState { const stack = new Error('Saving game...').stack!.split('\n').slice(1); const { ...debug } = toJS(data); console.dir({ stack, debug }); - GameFactory.save(toJS(this.data)); + GameFactory.save(data); } public save(): void { - this.saveData(this.data); + this.saveData(this.asGame); } public resume(): void { - this.data.paused = false; + this.isPaused = false; } public pause(): void { - this.data.paused = true; + this.isPaused = true; } public setCharacterTransform( @@ -99,6 +118,6 @@ export class GameState { if (this.isPaused) return; - this.data.time += deltaTime; + this.time += deltaTime; } } \ No newline at end of file diff --git a/src/types/model/runtime.ts b/src/types/model/runtime.ts index c37461c..f83bcb4 100644 --- a/src/types/model/runtime.ts +++ b/src/types/model/runtime.ts @@ -4,6 +4,7 @@ import type { ObjectInstance } from "./object"; export type ObjectInstanceRuntimeData = { cache: { voxelGroups: VoxelGroup[]; + colliderMesh: [Float32Array, Uint32Array] | null; }; } diff --git a/src/utils/runtime/object.ts b/src/utils/runtime/object.ts index 8f48946..36e09e8 100644 --- a/src/utils/runtime/object.ts +++ b/src/utils/runtime/object.ts @@ -1,5 +1,6 @@ import { getObjectVoxelGroups } from "../graphics/voxelGroup"; import type { ObjectInstance, RuntimeObjectInstance, World } from "../../types"; +import { buildObjectTrimesh } from "../graphics/mesh"; export function populateRuntimeObject(object: ObjectInstance, world: World): RuntimeObjectInstance { const objectType = world.objectTypes[object.typeId]; @@ -8,6 +9,7 @@ export function populateRuntimeObject(object: ObjectInstance, world: World): Run ...object, cache: { voxelGroups: getObjectVoxelGroups(objectType, world.voxelTypes), + colliderMesh: buildObjectTrimesh(objectType, world.voxelTypes), }, };