diff --git a/webclient/src/components/CharacterView.tsx b/webclient/src/components/CharacterView.tsx
index d40422e..a0abf45 100644
--- a/webclient/src/components/CharacterView.tsx
+++ b/webclient/src/components/CharacterView.tsx
@@ -1,5 +1,5 @@
import { observer } from "mobx-react-lite";
-import type { ObjectType, RuntimeGameObjectInstance } from "../types";
+import type { ObjectType, ObjectInstance } from "../types";
import { ObjectViewInternal, type ObjectViewInternalHandle } from "./ObjectViewInternal";
import { useEffect, useLayoutEffect, useMemo, useRef } from "react";
import { Euler, Quaternion, Vector3 } from "three";
@@ -25,7 +25,7 @@ const _charPos = new Vector3();
const _lookAt = new Vector3();
type PlayerObjectViewProps = {
- object: RuntimeGameObjectInstance;
+ object: ObjectInstance;
objectType: ObjectType;
}
diff --git a/webclient/src/components/DebugLabelsView.tsx b/webclient/src/components/DebugLabelsView.tsx
index 1d84431..5176f25 100644
--- a/webclient/src/components/DebugLabelsView.tsx
+++ b/webclient/src/components/DebugLabelsView.tsx
@@ -2,6 +2,7 @@ import { useEffect } from "react";
import { observer } from "mobx-react-lite";
import { debugLabelsStore } from "../state/debugLabelsState";
import { DebugLabel } from "./DebugLabel";
+import type { Scene } from "../types";
export const DebugLabelsView = observer(function ({ scene, editMode }: { scene: Scene, editMode?: boolean }) {
useEffect(
diff --git a/webclient/src/components/GameObjectView.tsx b/webclient/src/components/GameObjectView.tsx
index 2f7e392..8dafd3f 100644
--- a/webclient/src/components/GameObjectView.tsx
+++ b/webclient/src/components/GameObjectView.tsx
@@ -2,6 +2,7 @@ import { observer } from "mobx-react-lite";
import { state } from "../state";
import { ObjectViewInternal } from "./ObjectViewInternal";
import { PlayerObjectView } from "./CharacterView";
+import type { ObjectInstance } from "../types";
export type GameObjectViewProps = {
object: ObjectInstance;
diff --git a/webclient/src/components/ObjectEditorView.tsx b/webclient/src/components/ObjectEditorView.tsx
index afb299f..635afc6 100644
--- a/webclient/src/components/ObjectEditorView.tsx
+++ b/webclient/src/components/ObjectEditorView.tsx
@@ -94,9 +94,6 @@ export const ObjectEditorView = observer(function ({ object, isPlayer }: ObjectE
if (!objectType)
return null;
- // Track cache so observer re-renders when refreshObjectTypeCaches is called
- void object.cache.updatedAt;
-
function handleClick() {
if (state.worldEditor.isEnabled) {
// Reading selection state inside an event handler: not tracked by observer.
diff --git a/webclient/src/components/ObjectViewInternal.tsx b/webclient/src/components/ObjectViewInternal.tsx
index afd0eac..b2d0b3c 100644
--- a/webclient/src/components/ObjectViewInternal.tsx
+++ b/webclient/src/components/ObjectViewInternal.tsx
@@ -1,27 +1,12 @@
-import type { ObjectInstance, ObjectType, RuntimeGameObjectInstance, RuntimeObjectInstance } from "../types";
-import type { VoxelGroup } from "../types/runtime/voxelGroup";
-import { useEffect, useImperativeHandle, useRef, type Ref, memo, useMemo } from "react";
+import type { ObjectInstance, ObjectType } from "../types";
+import { useEffect, useImperativeHandle, useRef, type Ref } from "react";
import { Euler, Quaternion, Mesh, MeshStandardMaterial, type Group } from "three";
import type { ThreeEvent } from "@react-three/fiber";
-import { ConvexHullCollider, RapierRigidBody } from "@react-three/rapier";
+import { RapierRigidBody } from "@react-three/rapier";
import { SyncRigidBody } from "./SyncRigidBody";
import { reaction, runInAction } from "mobx";
import { v3toRapier } from "../utils";
import { state } from "../state";
-import { buildVoxelGroupMesh } from "../utils/graphics/voxelMesh";
-
-const VoxelGroupMesh = memo(function VoxelGroupMesh({ vg, opacity }: { vg: VoxelGroup, opacity: number }) {
- const geo = useMemo(() => buildVoxelGroupMesh(vg.positions), [vg]);
- useEffect(() => () => { geo.dispose(); }, [geo]);
-
- const op = vg.opacity * opacity;
-
- return (
-
-
-
- );
-});
export type ObjectViewInternalHandle = {
group: Group | null;
@@ -71,8 +56,6 @@ export const ObjectViewInternal = function ({ object, objectType, ref, ...props
[object, object.id, props.isPlayer]
);
- const gameObj = object as RuntimeGameObjectInstance;
-
useEffect(
() => {
return reaction(
@@ -98,8 +81,8 @@ export const ObjectViewInternal = function ({ object, objectType, ref, ...props
useEffect(
() => {
- runInAction(() => { gameObj.rb = () => rbRef.current; });
- return () => { runInAction(() => { gameObj.rb = () => null; }); };
+ runInAction(() => { object.rb = () => rbRef.current; });
+ return () => { runInAction(() => { object.rb = () => null; }); };
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[object.id],
@@ -130,9 +113,11 @@ export const ObjectViewInternal = function ({ object, objectType, ref, ...props
return;
const otherObj = Object.values(game.scene.objects)
.find((o) => o.rb()?.handle === otherHandle);
- game.emitEvent('objectTouchStart', { object: gameObj, other: otherObj });
+
+ const context = { object: { ...object, typeId: objectType.id }, other: otherObj };
+ game.emitEvent('objectTouchStart', context);
if (otherObj?.id === game.scene.playerObjectId)
- game.emitEvent('playerTouchStart', { object: gameObj, other: otherObj });
+ game.emitEvent('playerTouchStart', context);
}}
onSync={(data) => {
runInAction(() => {
diff --git a/webclient/src/components/SceneEditorView.tsx b/webclient/src/components/SceneEditorView.tsx
index 16443a4..f576607 100644
--- a/webclient/src/components/SceneEditorView.tsx
+++ b/webclient/src/components/SceneEditorView.tsx
@@ -4,7 +4,7 @@ import { OrbitControls } from '@react-three/drei';
import { observer } from 'mobx-react-lite';
import { state } from '../state';
import { SceneView } from './SceneView';
-import { MOUSE, type OrthographicCamera, type PerspectiveCamera } from 'three';
+import { MOUSE } from 'three';
import { useSyncCamera } from '../utils/hooks/useSyncCamera';
import { Physics } from '@react-three/rapier';
diff --git a/webclient/src/components/SceneView.tsx b/webclient/src/components/SceneView.tsx
index 4bc85fa..35c9611 100644
--- a/webclient/src/components/SceneView.tsx
+++ b/webclient/src/components/SceneView.tsx
@@ -1,11 +1,11 @@
import { observer } from "mobx-react-lite";
-import type { RuntimeScene } from "../types";
import { GameObjectView } from "./GameObjectView";
import { ObjectEditorView } from "./ObjectEditorView";
import { DebugLabelsView } from "./DebugLabelsView";
+import type { Scene } from "../types";
type SceneViewProps = {
- scene: RuntimeScene,
+ scene: Scene,
isEditor?: boolean;
debugOverlay?: boolean;
}
@@ -18,7 +18,7 @@ export const SceneView = observer(function (props: SceneViewProps) {
Object.values(props.scene.objects)
.map((obj) => (
!props.isEditor
- ?
+ ?
:
))
}
diff --git a/webclient/src/components/ThreeView.tsx b/webclient/src/components/ThreeView.tsx
index 0f0e576..193c3aa 100644
--- a/webclient/src/components/ThreeView.tsx
+++ b/webclient/src/components/ThreeView.tsx
@@ -8,7 +8,6 @@ import { SceneEditorView } from './SceneEditorView';
import { JoystickView } from './JoystickView';
import { useEffect } from 'react';
import { runInAction } from 'mobx';
-import { IconPlayerPlayFilled, IconPlayerPauseFilled, IconPlayerStopFilled, IconPlayerSkipForwardFilled } from '@tabler/icons-react';
export const ThreeView = observer(function () {
const isGame = !!state.game;
@@ -39,23 +38,7 @@ export const ThreeView = observer(function () {
{isGame ? : }
{isGame && }
-
- {
- state.game
- ? <>
-
- {
- state.game!.isPaused
- ? <>
-
-
- >
- :
- }
- >
- :
- }
-
+
);
diff --git a/webclient/src/components/layers/LayeredThreeView.tsx b/webclient/src/components/layers/LayeredThreeView.tsx
index c87b755..96e2f7b 100644
--- a/webclient/src/components/layers/LayeredThreeView.tsx
+++ b/webclient/src/components/layers/LayeredThreeView.tsx
@@ -6,6 +6,7 @@ import { SceneEditorObjectSelectionLayer } from "./SceneEditor/SceneEditorObject
import { LightsLayer } from "./LighsLayer";
import { StatsLayer } from './StatsLayer';
import { SceneEditorObjectGizmosLayer } from "./SceneEditor/SceneEditorObjectGizmosLayer";
+import { PlayButtonsLayer } from "./PlayButtonsLayer";
export const LayeredThreeView = observer(function () {
@@ -20,5 +21,6 @@ export const LayeredThreeView = observer(function () {
+
>;
});
diff --git a/webclient/src/components/layers/PlayButtonsLayer.tsx b/webclient/src/components/layers/PlayButtonsLayer.tsx
new file mode 100644
index 0000000..30339b5
--- /dev/null
+++ b/webclient/src/components/layers/PlayButtonsLayer.tsx
@@ -0,0 +1,23 @@
+import { observer } from "mobx-react-lite";
+import { state } from "../../state";
+import { IconPlayerPlayFilled, IconPlayerPauseFilled, IconPlayerStopFilled, IconPlayerSkipForwardFilled } from '@tabler/icons-react';
+
+export const PlayButtonsLayer = observer(function () {
+ return
+ {
+ state.game
+ ? <>
+
+ {
+ state.game!.isPaused
+ ? <>
+
+
+ >
+ :
+ }
+ >
+ :
+ }
+
;
+});
diff --git a/webclient/src/model/gameFactory.ts b/webclient/src/model/gameFactory.ts
index 2c77087..e326fae 100644
--- a/webclient/src/model/gameFactory.ts
+++ b/webclient/src/model/gameFactory.ts
@@ -1,7 +1,6 @@
import { toJS } from "mobx";
import type { Game, Scene, ScriptAction, ScriptCoroutineContext, World } from "../types";
import { clone } from "../utils";
-import { populateRuntimeScene } from "../utils/runtime";
import { ScriptGameEventBus } from "../scripts/eventBus";
import * as ScriptActions from "../scripts/actions";
import { positionOfObject } from "../scripts/positionOfObject";
@@ -19,7 +18,7 @@ import type { ScriptCoroutines } from "../scripts/coroutines";
export class GameFactory {
public static create(world: World): Game {
- const scene = populateRuntimeScene(clone(world.initialScene), world);
+ const scene = clone(world.initialScene);
const game = {
paused: false,
diff --git a/webclient/src/model/worldFactory.ts b/webclient/src/model/worldFactory.ts
index 257c301..b253894 100644
--- a/webclient/src/model/worldFactory.ts
+++ b/webclient/src/model/worldFactory.ts
@@ -1,11 +1,10 @@
-import { type RuntimeWorld, type World } from "../types";
-import { depopulateRuntimeWorld, populateRuntimeWorld } from "../utils/runtime";
+import { type World } from "../types";
import { DEFAULT_VOXEL_TYPES } from "./defaultVoxelTypes";
export class WorldFactory {
- public static create(): RuntimeWorld {
- return populateRuntimeWorld({
+ public static create(): World {
+ return {
objectTypes: {},
voxelTypes: DEFAULT_VOXEL_TYPES,
initialScene: {
@@ -19,17 +18,17 @@ export class WorldFactory {
position: [0, 5, 10],
look: [0, 0, 0],
},
- });
+ };
}
- public static load(): RuntimeWorld | undefined {
+ public static load(): World | undefined {
const json = localStorage.getItem("world");
if (json) {
- return populateRuntimeWorld(JSON.parse(json) as World);
+ return JSON.parse(json) as World;
}
}
- public static save(world: RuntimeWorld): void {
- localStorage.setItem("world", JSON.stringify(depopulateRuntimeWorld(world)));
+ public static save(world: World): void {
+ localStorage.setItem("world", JSON.stringify(world));
}
}
diff --git a/webclient/src/scripts/positionOfObject.ts b/webclient/src/scripts/positionOfObject.ts
index 8f28510..e65acbc 100644
--- a/webclient/src/scripts/positionOfObject.ts
+++ b/webclient/src/scripts/positionOfObject.ts
@@ -1,6 +1,6 @@
-import type { RuntimeGameObjectInstance, V3 } from "../types";
+import type { ObjectInstance, V3 } from "../types";
-export function positionOfObject(obj: RuntimeGameObjectInstance): V3 {
+export function positionOfObject(obj: ObjectInstance): V3 {
const t = obj.rb()?.translation();
const result: V3 = t
? [t.x, t.y, t.z]
diff --git a/webclient/src/state/worldEditorState.ts b/webclient/src/state/worldEditorState.ts
index 7af0be5..21ed093 100644
--- a/webclient/src/state/worldEditorState.ts
+++ b/webclient/src/state/worldEditorState.ts
@@ -4,7 +4,6 @@ import { type ObjectInstance, type Pos3, type R3, type V3, type ObjectType, type
import { createObjectInstance } from "../utils/object";
import { randomId } from "../utils";
import { state } from "./rootState";
-import { populateRuntimeObject } from "../utils/runtime";
import { DEFAULT_VOXEL_TYPE } from "../model/defaultVoxelTypes";
export const ObjectEditModeEnum = [
@@ -130,7 +129,7 @@ export class WorldEditorState {
typeId: string,
pos: { x: number; y: number; z: number; },
): ObjectInstance {
- const obj = populateRuntimeObject(createObjectInstance(randomId(), typeId), this.world.data);
+ const obj = createObjectInstance(randomId(), typeId);
obj.position = [pos.x, pos.y, pos.z];
obj.rotation = [0, 0, 0];
diff --git a/webclient/src/state/worldState.ts b/webclient/src/state/worldState.ts
index 4a87294..d2c53f2 100644
--- a/webclient/src/state/worldState.ts
+++ b/webclient/src/state/worldState.ts
@@ -5,7 +5,6 @@ import { state } from "./rootState";
import { DEFAULT_VOXEL_TYPES } from "../model/defaultVoxelTypes";
import { wolf } from "../model/objectPrefabs/wolf";
import { terrainXZ } from "../model/objectPrefabs/terrain";
-import { populateRuntimeWorld } from "../utils/runtime";
export class WorldState {
public data: World = WorldFactory.create();
@@ -58,7 +57,7 @@ export class WorldState {
objects.map((obj) => [obj.id, obj]),
) as Record;
- this.data = populateRuntimeWorld({
+ this.data = {
objectTypes: {
wolf: {
id: 'wolf',
@@ -97,7 +96,7 @@ export class WorldState {
gameRules: {
gravity: true,
},
- });
+ };
console.log(objects);
state.worldEditor.resetSelection();
diff --git a/webclient/src/types/scripts/eventBus.ts b/webclient/src/types/scripts/eventBus.ts
index d352adc..d19bb47 100644
--- a/webclient/src/types/scripts/eventBus.ts
+++ b/webclient/src/types/scripts/eventBus.ts
@@ -1,4 +1,4 @@
-import type { ObjectInstance, ObjectType, RuntimeGameObjectInstance, Scene, World } from "../model";
+import type { ObjectInstance, ObjectType, Scene, World } from "../model";
import type { ScriptAction } from "./actions";
export type ScriptCoroutine = Generator;
@@ -9,15 +9,15 @@ export type NamedScriptCoroutine = {
}
export type ScriptCoroutineContext = {
- thisObject: RuntimeGameObjectInstance,
+ thisObject: ObjectInstance,
thisObjectType: ObjectType;
};
export type ScriptContext = ScriptCoroutineContext & {
world: World;
scene: Scene;
- object?: RuntimeGameObjectInstance;
- other?: RuntimeGameObjectInstance;
+ object?: ObjectInstance;
+ other?: ObjectInstance;
}
export type ScriptGameEventHandler = (context: ScriptContext) => ScriptCoroutine;
diff --git a/webclient/src/utils/object.ts b/webclient/src/utils/object.ts
index 661e7e9..ecda024 100644
--- a/webclient/src/utils/object.ts
+++ b/webclient/src/utils/object.ts
@@ -10,5 +10,8 @@ export function createObjectInstance(id: string, typeId: string): ObjectInstance
position: [0, 0, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1],
+
+ linearVelocity: [0, 0, 0],
+ angularVelocity: [0, 0, 0],
};
}
diff --git a/webclient/src/utils/runtime/index.ts b/webclient/src/utils/runtime/index.ts
index 53f97dd..1c19a11 100644
--- a/webclient/src/utils/runtime/index.ts
+++ b/webclient/src/utils/runtime/index.ts
@@ -1,3 +1 @@
-export * from './world';
-export * from './scene';
export * from './object';
diff --git a/webclient/src/utils/runtime/object.ts b/webclient/src/utils/runtime/object.ts
index 44491a3..c6861ac 100644
--- a/webclient/src/utils/runtime/object.ts
+++ b/webclient/src/utils/runtime/object.ts
@@ -1,6 +1,4 @@
-import { getObjectVoxelGroups } from "../graphics/voxelGroup";
-import type { ObjectInstance, ObjectType, RuntimeObjectInstance, RuntimeObjectType, V3, World } from "../../types";
-import { buildObjectTrimesh } from "../graphics/mesh";
+import type { ObjectType, V3 } from "../../types";
export function computeBoundingBox(objectType: ObjectType): { min: V3; max: V3 } {
if (!objectType.voxels.length)
@@ -17,35 +15,3 @@ export function computeBoundingBox(objectType: ObjectType): { min: V3; max: V3 }
}
return { min: [minX, minY, minZ], max: [maxX, maxY, maxZ] };
}
-
-export function populateRuntimeObjectType(value: ObjectType, world: World): RuntimeObjectType {
- return {
- ...value,
- runtime: {
- voxelGroups: getObjectVoxelGroups(value, world.voxelTypes),
- colliderMesh: buildObjectTrimesh(value, world.voxelTypes),
- boundingBox: computeBoundingBox(value),
- updatedAt: 0,
- },
- };
-}
-
-export function populateRuntimeObject(value: ObjectInstance, _world: World): RuntimeObjectInstance {
- return {
- ...value,
- runtime: {
- rb: () => null,
- },
- };
-}
-
-export function depopulateRuntimeObjectType(value: RuntimeObjectType, _world: World): ObjectType {
- const { runtime: _, ...result } = value;
- return result;
-}
-
-
-export function depopulateRuntimeObject(value: RuntimeObjectInstance, _world: World): ObjectInstance {
- const { runtime: _, ...result } = value;
- return result;
-}
diff --git a/webclient/src/utils/runtime/scene.ts b/webclient/src/utils/runtime/scene.ts
deleted file mode 100644
index 802c587..0000000
--- a/webclient/src/utils/runtime/scene.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { RuntimeScene, Scene, World } from "../../types";
-import { depopulateRuntimeObject, populateRuntimeObject } from "./object";
-
-export function populateRuntimeScene(scene: Scene, world: World): RuntimeScene {
- return {
- ...scene,
- objects: Object.fromEntries(Object.entries(scene.objects).map(([id, obj]) => [id, populateRuntimeObject(obj, world)])),
- };
-}
-
-export function depopulateRuntimeScene(scene: RuntimeScene, world: World): Scene {
- return {
- ...scene,
- objects: Object.fromEntries(Object.entries(scene.objects).map(([id, obj]) => [id, depopulateRuntimeObject(obj, world)])),
- };
-}
diff --git a/webclient/src/utils/runtime/world.ts b/webclient/src/utils/runtime/world.ts
deleted file mode 100644
index f509a99..0000000
--- a/webclient/src/utils/runtime/world.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { RuntimeWorld, World } from "../../types";
-import { depopulateRuntimeScene, populateRuntimeScene } from "./scene";
-
-export function populateRuntimeWorld(world: World): RuntimeWorld {
- return {
- ...world,
- initialScene: populateRuntimeScene(world.initialScene, world),
- };
-}
-
-export function depopulateRuntimeWorld(world: RuntimeWorld): World {
- return {
- ...world,
- initialScene: depopulateRuntimeScene(world.initialScene, world),
- };
-}