react-three-game 0.0.14 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -11
- package/dist/helpers/index.d.ts +35 -0
- package/dist/helpers/index.js +44 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/shared/GameCanvas.js +13 -13
- package/dist/tools/prefabeditor/EditorTree.js +0 -2
- package/dist/tools/prefabeditor/PrefabEditor.js +0 -2
- package/dist/tools/prefabeditor/PrefabRoot.js +1 -10
- package/dist/tools/prefabeditor/types.d.ts +2 -2
- package/package.json +4 -1
- package/src/helpers/index.ts +95 -0
- package/src/index.ts +3 -0
- package/src/shared/GameCanvas.tsx +5 -2
- package/src/tools/prefabeditor/EditorTree.tsx +0 -2
- package/src/tools/prefabeditor/PrefabEditor.tsx +0 -2
- package/src/tools/prefabeditor/PrefabRoot.tsx +2 -13
- package/src/tools/prefabeditor/types.ts +2 -2
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ npm install react-three-game @react-three/fiber @react-three/rapier three
|
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
```jsx
|
|
67
|
-
import { GameCanvas, PrefabRoot } from 'react-three-game';
|
|
67
|
+
import { GameCanvas, PrefabRoot, ground } from 'react-three-game';
|
|
68
68
|
|
|
69
69
|
export default function App() {
|
|
70
70
|
return (
|
|
@@ -78,15 +78,13 @@ export default function App() {
|
|
|
78
78
|
transform: { type: "Transform", properties: { position: [0, 0, 0] } }
|
|
79
79
|
},
|
|
80
80
|
children: [
|
|
81
|
-
{
|
|
81
|
+
ground({
|
|
82
82
|
id: "floor",
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
},
|
|
83
|
+
position: [0, -1, 0],
|
|
84
|
+
size: 50,
|
|
85
|
+
texture: "/textures/GreyboxTextures/greybox_light_grid.png",
|
|
86
|
+
repeatCount: [25, 25]
|
|
87
|
+
}),
|
|
90
88
|
{
|
|
91
89
|
id: "player",
|
|
92
90
|
components: {
|
|
@@ -109,8 +107,8 @@ export default function App() {
|
|
|
109
107
|
```typescript
|
|
110
108
|
interface GameObject {
|
|
111
109
|
id: string;
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
disabled?: boolean;
|
|
111
|
+
hidden?: boolean;
|
|
114
112
|
components: {
|
|
115
113
|
transform?: TransformComponent;
|
|
116
114
|
geometry?: GeometryComponent;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { GameObject } from "../tools/prefabeditor/types";
|
|
2
|
+
export type Vec3 = [number, number, number];
|
|
3
|
+
export interface GroundOptions {
|
|
4
|
+
/** GameObject id. Defaults to "ground". */
|
|
5
|
+
id?: string;
|
|
6
|
+
/** Plane size. Defaults to 50. */
|
|
7
|
+
size?: number;
|
|
8
|
+
/** Transform overrides. */
|
|
9
|
+
position?: Vec3;
|
|
10
|
+
rotation?: Vec3;
|
|
11
|
+
scale?: Vec3;
|
|
12
|
+
/** Material overrides. */
|
|
13
|
+
color?: string;
|
|
14
|
+
texture?: string;
|
|
15
|
+
/** When true, set repeat wrapping. Defaults to true if texture is provided. */
|
|
16
|
+
repeat?: boolean;
|
|
17
|
+
/** Texture repeat counts when repeat=true. Defaults to [25,25]. */
|
|
18
|
+
repeatCount?: [number, number];
|
|
19
|
+
/** Physics body type. Defaults to "fixed". */
|
|
20
|
+
physicsType?: "fixed" | "dynamic" | "kinematic";
|
|
21
|
+
/** Set true to hide the node. */
|
|
22
|
+
hidden?: boolean;
|
|
23
|
+
/** Set true to disable the node. */
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a ready-to-use plane ground GameObject.
|
|
28
|
+
*
|
|
29
|
+
* Designed to reduce prefab boilerplate:
|
|
30
|
+
* - Transform (rotated to lie flat)
|
|
31
|
+
* - Geometry (plane)
|
|
32
|
+
* - Material (optional texture + repeat)
|
|
33
|
+
* - Physics (fixed by default)
|
|
34
|
+
*/
|
|
35
|
+
export declare function ground(options?: GroundOptions): GameObject;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a ready-to-use plane ground GameObject.
|
|
3
|
+
*
|
|
4
|
+
* Designed to reduce prefab boilerplate:
|
|
5
|
+
* - Transform (rotated to lie flat)
|
|
6
|
+
* - Geometry (plane)
|
|
7
|
+
* - Material (optional texture + repeat)
|
|
8
|
+
* - Physics (fixed by default)
|
|
9
|
+
*/
|
|
10
|
+
export function ground(options = {}) {
|
|
11
|
+
const { id = "ground", size = 50, position = [0, 0, 0], rotation = [-Math.PI / 2, 0, 0], scale = [1, 1, 1], color = "white", texture, repeat = texture ? true : false, repeatCount = [25, 25], physicsType = "fixed", hidden = false, disabled = false, } = options;
|
|
12
|
+
return {
|
|
13
|
+
id,
|
|
14
|
+
disabled,
|
|
15
|
+
hidden,
|
|
16
|
+
components: {
|
|
17
|
+
transform: {
|
|
18
|
+
type: "Transform",
|
|
19
|
+
properties: {
|
|
20
|
+
position,
|
|
21
|
+
rotation,
|
|
22
|
+
scale,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
geometry: {
|
|
26
|
+
type: "Geometry",
|
|
27
|
+
properties: {
|
|
28
|
+
geometryType: "plane",
|
|
29
|
+
args: [size, size],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
material: {
|
|
33
|
+
type: "Material",
|
|
34
|
+
properties: Object.assign(Object.assign({ color }, (texture ? { texture } : {})), (repeat ? { repeat: true, repeatCount } : {})),
|
|
35
|
+
},
|
|
36
|
+
physics: {
|
|
37
|
+
type: "Physics",
|
|
38
|
+
properties: {
|
|
39
|
+
type: physicsType,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { default as PrefabEditor } from './tools/prefabeditor/PrefabEditor';
|
|
|
3
3
|
export { default as PrefabRoot } from './tools/prefabeditor/PrefabRoot';
|
|
4
4
|
export { DragDropLoader } from './tools/dragdrop/DragDropLoader';
|
|
5
5
|
export { TextureListViewer, ModelListViewer, SoundListViewer, SharedCanvas, } from './tools/assetviewer/page';
|
|
6
|
+
export * from './helpers';
|
|
6
7
|
export type { Prefab, GameObject } from './tools/prefabeditor/types';
|
package/dist/index.js
CHANGED
|
@@ -4,3 +4,5 @@ export { default as PrefabEditor } from './tools/prefabeditor/PrefabEditor';
|
|
|
4
4
|
export { default as PrefabRoot } from './tools/prefabeditor/PrefabRoot';
|
|
5
5
|
export { DragDropLoader } from './tools/dragdrop/DragDropLoader';
|
|
6
6
|
export { TextureListViewer, ModelListViewer, SoundListViewer, SharedCanvas, } from './tools/assetviewer/page';
|
|
7
|
+
// Helpers
|
|
8
|
+
export * from './helpers';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -18,7 +19,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
18
19
|
}
|
|
19
20
|
return t;
|
|
20
21
|
};
|
|
21
|
-
import { jsx as _jsx,
|
|
22
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
22
23
|
import { Canvas, extend } from "@react-three/fiber";
|
|
23
24
|
import { WebGPURenderer, MeshBasicNodeMaterial, MeshStandardNodeMaterial, SpriteNodeMaterial, PCFShadowMap } from "three/webgpu";
|
|
24
25
|
import { Suspense, useState } from "react";
|
|
@@ -33,16 +34,15 @@ extend({
|
|
|
33
34
|
export default function GameCanvas(_a) {
|
|
34
35
|
var { loader = false, children } = _a, props = __rest(_a, ["loader", "children"]);
|
|
35
36
|
const [frameloop, setFrameloop] = useState("never");
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}, children: _jsx(Suspense, { children: children }) }), loader ? _jsx(Loader, {}) : null] });
|
|
37
|
+
return _jsx(_Fragment, { children: _jsxs(Canvas, { style: { touchAction: 'none' }, shadows: { type: PCFShadowMap, }, frameloop: frameloop, gl: (_a) => __awaiter(this, [_a], void 0, function* ({ canvas }) {
|
|
38
|
+
const renderer = new WebGPURenderer(Object.assign({ canvas: canvas,
|
|
39
|
+
// @ts-expect-error futuristic
|
|
40
|
+
shadowMap: true, antialias: true }, props));
|
|
41
|
+
yield renderer.init().then(() => {
|
|
42
|
+
setFrameloop("always");
|
|
43
|
+
});
|
|
44
|
+
return renderer;
|
|
45
|
+
}), camera: {
|
|
46
|
+
position: [0, 1, 5],
|
|
47
|
+
}, children: [_jsx(Suspense, { children: children }), loader ? _jsx(Loader, {}) : null] }) });
|
|
48
48
|
}
|
|
@@ -49,13 +49,6 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
|
|
|
49
49
|
setSelectedObject(null);
|
|
50
50
|
}
|
|
51
51
|
}, [selectedId]);
|
|
52
|
-
// const [transformMode, setTransformMode] = useState<"translate" | "rotate" | "scale">("translate"); // Removed local state
|
|
53
|
-
const updateNode = (updater) => {
|
|
54
|
-
if (!selectedId || !onPrefabChange)
|
|
55
|
-
return;
|
|
56
|
-
const newRoot = updatePrefabNode(data.root, selectedId, updater);
|
|
57
|
-
onPrefabChange(Object.assign(Object.assign({}, data), { root: newRoot }));
|
|
58
|
-
};
|
|
59
52
|
const onTransformChange = () => {
|
|
60
53
|
if (!selectedId || !onPrefabChange)
|
|
61
54
|
return;
|
|
@@ -158,7 +151,7 @@ function GameObjectRenderer({ gameObject, selectedId, onSelect, registerRef, loa
|
|
|
158
151
|
}
|
|
159
152
|
clickValid.current = false;
|
|
160
153
|
};
|
|
161
|
-
if (
|
|
154
|
+
if (gameObject.disabled === true || gameObject.hidden === true)
|
|
162
155
|
return null;
|
|
163
156
|
// --- 2. If instanced, short-circuit to a tiny clean branch ---
|
|
164
157
|
const isInstanced = !!((_c = (_b = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.instanced);
|
|
@@ -170,8 +163,6 @@ function GameObjectRenderer({ gameObject, selectedId, onSelect, registerRef, loa
|
|
|
170
163
|
// --- 5. Render children (always relative transforms) ---
|
|
171
164
|
const children = ((_d = gameObject.children) !== null && _d !== void 0 ? _d : []).map((child) => (_jsx(GameObjectRenderer, { gameObject: child, selectedId: selectedId, onSelect: onSelect, registerRef: registerRef, loadedModels: loadedModels, loadedTextures: loadedTextures, editMode: editMode, parentMatrix: worldMatrix }, child.id)));
|
|
172
165
|
// --- 4. Wrap with physics if needed ---
|
|
173
|
-
// Only wrap the core content (geometry/model), not children
|
|
174
|
-
// Children should be siblings, not inside the physics body
|
|
175
166
|
const physicsWrapped = wrapPhysicsIfNeeded(gameObject, core, ctx);
|
|
176
167
|
// --- 6. Final group wrapper ---
|
|
177
168
|
return (_jsxs("group", { ref: (el) => registerRef(gameObject.id, el), position: transformProps.position, rotation: transformProps.rotation, scale: transformProps.scale, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, children: [physicsWrapped, children] }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-three-game",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "Batteries included React Three Fiber game engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -15,8 +15,11 @@
|
|
|
15
15
|
"author": "prnth",
|
|
16
16
|
"license": "VPL",
|
|
17
17
|
"type": "module",
|
|
18
|
+
"workspaces": ["docs"],
|
|
18
19
|
"peerDependencies": {
|
|
19
20
|
"@react-three/fiber": "^9.0.0",
|
|
21
|
+
"@react-three/drei": "^10.0.0",
|
|
22
|
+
"@react-three/rapier": "^2.0.0",
|
|
20
23
|
"react": "^18.0.0 || ^19.0.0",
|
|
21
24
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
22
25
|
"three": "^0.181.0"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { GameObject } from "../tools/prefabeditor/types";
|
|
2
|
+
|
|
3
|
+
export type Vec3 = [number, number, number];
|
|
4
|
+
|
|
5
|
+
export interface GroundOptions {
|
|
6
|
+
/** GameObject id. Defaults to "ground". */
|
|
7
|
+
id?: string;
|
|
8
|
+
|
|
9
|
+
/** Plane size. Defaults to 50. */
|
|
10
|
+
size?: number;
|
|
11
|
+
|
|
12
|
+
/** Transform overrides. */
|
|
13
|
+
position?: Vec3;
|
|
14
|
+
rotation?: Vec3;
|
|
15
|
+
scale?: Vec3;
|
|
16
|
+
|
|
17
|
+
/** Material overrides. */
|
|
18
|
+
color?: string;
|
|
19
|
+
texture?: string;
|
|
20
|
+
/** When true, set repeat wrapping. Defaults to true if texture is provided. */
|
|
21
|
+
repeat?: boolean;
|
|
22
|
+
/** Texture repeat counts when repeat=true. Defaults to [25,25]. */
|
|
23
|
+
repeatCount?: [number, number];
|
|
24
|
+
|
|
25
|
+
/** Physics body type. Defaults to "fixed". */
|
|
26
|
+
physicsType?: "fixed" | "dynamic" | "kinematic";
|
|
27
|
+
|
|
28
|
+
/** Set true to hide the node. */
|
|
29
|
+
hidden?: boolean;
|
|
30
|
+
/** Set true to disable the node. */
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create a ready-to-use plane ground GameObject.
|
|
36
|
+
*
|
|
37
|
+
* Designed to reduce prefab boilerplate:
|
|
38
|
+
* - Transform (rotated to lie flat)
|
|
39
|
+
* - Geometry (plane)
|
|
40
|
+
* - Material (optional texture + repeat)
|
|
41
|
+
* - Physics (fixed by default)
|
|
42
|
+
*/
|
|
43
|
+
export function ground(options: GroundOptions = {}): GameObject {
|
|
44
|
+
const {
|
|
45
|
+
id = "ground",
|
|
46
|
+
size = 50,
|
|
47
|
+
position = [0, 0, 0],
|
|
48
|
+
rotation = [-Math.PI / 2, 0, 0],
|
|
49
|
+
scale = [1, 1, 1],
|
|
50
|
+
color = "white",
|
|
51
|
+
texture,
|
|
52
|
+
repeat = texture ? true : false,
|
|
53
|
+
repeatCount = [25, 25],
|
|
54
|
+
physicsType = "fixed",
|
|
55
|
+
hidden = false,
|
|
56
|
+
disabled = false,
|
|
57
|
+
} = options;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
id,
|
|
61
|
+
disabled,
|
|
62
|
+
hidden,
|
|
63
|
+
components: {
|
|
64
|
+
transform: {
|
|
65
|
+
type: "Transform",
|
|
66
|
+
properties: {
|
|
67
|
+
position,
|
|
68
|
+
rotation,
|
|
69
|
+
scale,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
geometry: {
|
|
73
|
+
type: "Geometry",
|
|
74
|
+
properties: {
|
|
75
|
+
geometryType: "plane",
|
|
76
|
+
args: [size, size],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
material: {
|
|
80
|
+
type: "Material",
|
|
81
|
+
properties: {
|
|
82
|
+
color,
|
|
83
|
+
...(texture ? { texture } : {}),
|
|
84
|
+
...(repeat ? { repeat: true, repeatCount } : {}),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
physics: {
|
|
88
|
+
type: "Physics",
|
|
89
|
+
properties: {
|
|
90
|
+
type: physicsType,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { Canvas, extend } from "@react-three/fiber";
|
|
2
4
|
import { WebGPURenderer, MeshBasicNodeMaterial, MeshStandardNodeMaterial, SpriteNodeMaterial, PCFShadowMap } from "three/webgpu";
|
|
3
5
|
import { Suspense, useState } from "react";
|
|
@@ -16,10 +18,10 @@ extend({
|
|
|
16
18
|
|
|
17
19
|
export default function GameCanvas({ loader = false, children, ...props }: { loader?: boolean, children: React.ReactNode, props?: WebGPURendererParameters }) {
|
|
18
20
|
const [frameloop, setFrameloop] = useState<"never" | "always">("never");
|
|
19
|
-
const [loading, setLoading] = useState(true);
|
|
20
21
|
|
|
21
22
|
return <>
|
|
22
23
|
<Canvas
|
|
24
|
+
style={{ touchAction: 'none' }}
|
|
23
25
|
shadows={{ type: PCFShadowMap, }}
|
|
24
26
|
frameloop={frameloop}
|
|
25
27
|
gl={async ({ canvas }) => {
|
|
@@ -42,7 +44,8 @@ export default function GameCanvas({ loader = false, children, ...props }: { loa
|
|
|
42
44
|
<Suspense>
|
|
43
45
|
{children}
|
|
44
46
|
</Suspense>
|
|
47
|
+
|
|
48
|
+
{loader ? <Loader /> : null}
|
|
45
49
|
</Canvas>
|
|
46
|
-
{loader ? <Loader /> : null}
|
|
47
50
|
</>;
|
|
48
51
|
}
|
|
@@ -39,8 +39,6 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
|
|
|
39
39
|
const handleAddChild = (parentId: string) => {
|
|
40
40
|
const newNode: GameObject = {
|
|
41
41
|
id: crypto.randomUUID(),
|
|
42
|
-
enabled: true,
|
|
43
|
-
visible: true,
|
|
44
42
|
components: {
|
|
45
43
|
transform: {
|
|
46
44
|
type: "Transform",
|
|
@@ -59,15 +59,6 @@ export const PrefabRoot = forwardRef<Group, {
|
|
|
59
59
|
}
|
|
60
60
|
}, [selectedId]);
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
// const [transformMode, setTransformMode] = useState<"translate" | "rotate" | "scale">("translate"); // Removed local state
|
|
64
|
-
|
|
65
|
-
const updateNode = (updater: (node: GameObjectType) => GameObjectType) => {
|
|
66
|
-
if (!selectedId || !onPrefabChange) return;
|
|
67
|
-
const newRoot = updatePrefabNode(data.root, selectedId, updater);
|
|
68
|
-
onPrefabChange({ ...data, root: newRoot });
|
|
69
|
-
};
|
|
70
|
-
|
|
71
62
|
const onTransformChange = () => {
|
|
72
63
|
if (!selectedId || !onPrefabChange) return;
|
|
73
64
|
const obj = objectRefs.current[selectedId];
|
|
@@ -167,7 +158,7 @@ export const PrefabRoot = forwardRef<Group, {
|
|
|
167
158
|
loadedModels={loadedModels}
|
|
168
159
|
loadedTextures={loadedTextures}
|
|
169
160
|
editMode={editMode}
|
|
170
|
-
parentMatrix={new Matrix4()}
|
|
161
|
+
parentMatrix={new Matrix4()}
|
|
171
162
|
/>
|
|
172
163
|
</GameInstanceProvider>
|
|
173
164
|
|
|
@@ -242,7 +233,7 @@ function GameObjectRenderer({
|
|
|
242
233
|
clickValid.current = false;
|
|
243
234
|
};
|
|
244
235
|
|
|
245
|
-
if (
|
|
236
|
+
if (gameObject.disabled === true || gameObject.hidden === true) return null;
|
|
246
237
|
|
|
247
238
|
// --- 2. If instanced, short-circuit to a tiny clean branch ---
|
|
248
239
|
const isInstanced = !!gameObject.components?.model?.properties?.instanced;
|
|
@@ -269,8 +260,6 @@ function GameObjectRenderer({
|
|
|
269
260
|
));
|
|
270
261
|
|
|
271
262
|
// --- 4. Wrap with physics if needed ---
|
|
272
|
-
// Only wrap the core content (geometry/model), not children
|
|
273
|
-
// Children should be siblings, not inside the physics body
|
|
274
263
|
const physicsWrapped = wrapPhysicsIfNeeded(gameObject, core, ctx);
|
|
275
264
|
|
|
276
265
|
// --- 6. Final group wrapper ---
|