@gnsx/genesys.sdk 4.2.9

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.
Files changed (185) hide show
  1. package/README.md +60 -0
  2. package/dist/src/asset-pack/eslint.config.js +58 -0
  3. package/dist/src/asset-pack/scripts/post-install.js +64 -0
  4. package/dist/src/asset-pack/src/index.js +1 -0
  5. package/dist/src/core/cli.js +303 -0
  6. package/dist/src/core/common.js +325 -0
  7. package/dist/src/core/index.js +6 -0
  8. package/dist/src/core/tools/build-project.js +456 -0
  9. package/dist/src/core/tools/index.js +2 -0
  10. package/dist/src/core/tools/new-asset-pack.js +153 -0
  11. package/dist/src/core/tools/new-project.js +293 -0
  12. package/dist/src/core/types.js +1 -0
  13. package/dist/src/dependencies.js +84 -0
  14. package/dist/src/electron/IpcSerializableError.js +38 -0
  15. package/dist/src/electron/api.js +7 -0
  16. package/dist/src/electron/backend/actions.js +56 -0
  17. package/dist/src/electron/backend/handler.js +452 -0
  18. package/dist/src/electron/backend/logging.js +41 -0
  19. package/dist/src/electron/backend/main.js +369 -0
  20. package/dist/src/electron/backend/menu.js +196 -0
  21. package/dist/src/electron/backend/state.js +201 -0
  22. package/dist/src/electron/backend/telemetry.js +9 -0
  23. package/dist/src/electron/backend/tools/const.js +9 -0
  24. package/dist/src/electron/backend/tools/file-server.js +383 -0
  25. package/dist/src/electron/backend/tools/open-project.js +249 -0
  26. package/dist/src/electron/backend/window.js +161 -0
  27. package/dist/src/templates/eslint.config.js +58 -0
  28. package/dist/src/templates/scripts/genesys/build-project.js +42 -0
  29. package/dist/src/templates/scripts/genesys/calc-bounding-box.js +205 -0
  30. package/dist/src/templates/scripts/genesys/common.js +36 -0
  31. package/dist/src/templates/scripts/genesys/const.js +9 -0
  32. package/dist/src/templates/scripts/genesys/dev/dump-default-scene.js +8 -0
  33. package/dist/src/templates/scripts/genesys/dev/generate-manifest.js +116 -0
  34. package/dist/src/templates/scripts/genesys/dev/launcher.js +39 -0
  35. package/dist/src/templates/scripts/genesys/dev/storage-provider.js +188 -0
  36. package/dist/src/templates/scripts/genesys/dev/update-template-scenes.js +67 -0
  37. package/dist/src/templates/scripts/genesys/doc-server.js +12 -0
  38. package/dist/src/templates/scripts/genesys/genesys-mcp.js +413 -0
  39. package/dist/src/templates/scripts/genesys/mcp/doc-tools.js +70 -0
  40. package/dist/src/templates/scripts/genesys/mcp/editor-functions.js +123 -0
  41. package/dist/src/templates/scripts/genesys/mcp/editor-tools.js +51 -0
  42. package/dist/src/templates/scripts/genesys/mcp/get-scene-state.js +26 -0
  43. package/dist/src/templates/scripts/genesys/mcp/run-subprocess.js +23 -0
  44. package/dist/src/templates/scripts/genesys/mcp/search-actors.js +703 -0
  45. package/dist/src/templates/scripts/genesys/mcp/search-assets.js +296 -0
  46. package/dist/src/templates/scripts/genesys/mcp/utils.js +234 -0
  47. package/dist/src/templates/scripts/genesys/migrate-scenes-and-prefabs.js +252 -0
  48. package/dist/src/templates/scripts/genesys/misc.js +32 -0
  49. package/dist/src/templates/scripts/genesys/mock.js +5 -0
  50. package/dist/src/templates/scripts/genesys/place-actors.js +112 -0
  51. package/dist/src/templates/scripts/genesys/post-install.js +33 -0
  52. package/dist/src/templates/scripts/genesys/remove-engine-comments.js +113 -0
  53. package/dist/src/templates/scripts/genesys/storageProvider.js +146 -0
  54. package/dist/src/templates/scripts/genesys/validate-prefabs.js +115 -0
  55. package/dist/src/templates/src/index.js +20 -0
  56. package/dist/src/templates/src/templates/firstPerson/src/auto-imports.js +1 -0
  57. package/dist/src/templates/src/templates/firstPerson/src/game.js +30 -0
  58. package/dist/src/templates/src/templates/firstPerson/src/player.js +55 -0
  59. package/dist/src/templates/src/templates/fps/src/auto-imports.js +1 -0
  60. package/dist/src/templates/src/templates/fps/src/game.js +30 -0
  61. package/dist/src/templates/src/templates/fps/src/player.js +60 -0
  62. package/dist/src/templates/src/templates/fps/src/weapon.js +54 -0
  63. package/dist/src/templates/src/templates/freeCamera/src/auto-imports.js +1 -0
  64. package/dist/src/templates/src/templates/freeCamera/src/game.js +30 -0
  65. package/dist/src/templates/src/templates/freeCamera/src/player.js +38 -0
  66. package/dist/src/templates/src/templates/sideScroller/src/auto-imports.js +1 -0
  67. package/dist/src/templates/src/templates/sideScroller/src/const.js +43 -0
  68. package/dist/src/templates/src/templates/sideScroller/src/game.js +102 -0
  69. package/dist/src/templates/src/templates/sideScroller/src/level-generator.js +249 -0
  70. package/dist/src/templates/src/templates/sideScroller/src/player.js +100 -0
  71. package/dist/src/templates/src/templates/thirdPerson/src/auto-imports.js +1 -0
  72. package/dist/src/templates/src/templates/thirdPerson/src/game.js +30 -0
  73. package/dist/src/templates/src/templates/thirdPerson/src/player.js +58 -0
  74. package/dist/src/templates/src/templates/vehicle/src/auto-imports.js +1 -0
  75. package/dist/src/templates/src/templates/vehicle/src/base-vehicle.js +122 -0
  76. package/dist/src/templates/src/templates/vehicle/src/game.js +33 -0
  77. package/dist/src/templates/src/templates/vehicle/src/mesh-vehicle.js +188 -0
  78. package/dist/src/templates/src/templates/vehicle/src/player.js +97 -0
  79. package/dist/src/templates/src/templates/vehicle/src/primitive-vehicle.js +258 -0
  80. package/dist/src/templates/src/templates/vehicle/src/ui-hints.js +100 -0
  81. package/dist/src/templates/src/templates/vr-game/src/auto-imports.js +1 -0
  82. package/dist/src/templates/src/templates/vr-game/src/game.js +55 -0
  83. package/dist/src/templates/src/templates/vr-game/src/sample-vr-actor.js +29 -0
  84. package/dist/src/templates/vite.config.js +46 -0
  85. package/package.json +181 -0
  86. package/scripts/post-install.ts +143 -0
  87. package/src/asset-pack/.gitattributes +89 -0
  88. package/src/asset-pack/.github/workflows/publish.yml +90 -0
  89. package/src/asset-pack/eslint.config.js +59 -0
  90. package/src/asset-pack/gitignore +11 -0
  91. package/src/asset-pack/scripts/post-install.ts +81 -0
  92. package/src/asset-pack/src/index.ts +0 -0
  93. package/src/asset-pack/tsconfig.json +34 -0
  94. package/src/templates/.cursor/mcp.json +20 -0
  95. package/src/templates/.cursorignore +2 -0
  96. package/src/templates/.gitattributes +89 -0
  97. package/src/templates/.vscode/settings.json +6 -0
  98. package/src/templates/AGENTS.md +104 -0
  99. package/src/templates/CLAUDE.md +1 -0
  100. package/src/templates/README.md +24 -0
  101. package/src/templates/eslint.config.js +60 -0
  102. package/src/templates/gitignore +11 -0
  103. package/src/templates/index.html +34 -0
  104. package/src/templates/pnpm-lock.yaml +3676 -0
  105. package/src/templates/scripts/genesys/build-project.ts +51 -0
  106. package/src/templates/scripts/genesys/calc-bounding-box.ts +272 -0
  107. package/src/templates/scripts/genesys/common.ts +46 -0
  108. package/src/templates/scripts/genesys/const.ts +9 -0
  109. package/src/templates/scripts/genesys/dev/dump-default-scene.ts +11 -0
  110. package/src/templates/scripts/genesys/dev/generate-manifest.ts +146 -0
  111. package/src/templates/scripts/genesys/dev/launcher.ts +46 -0
  112. package/src/templates/scripts/genesys/dev/storage-provider.ts +229 -0
  113. package/src/templates/scripts/genesys/dev/update-template-scenes.ts +84 -0
  114. package/src/templates/scripts/genesys/doc-server.ts +16 -0
  115. package/src/templates/scripts/genesys/genesys-mcp.ts +526 -0
  116. package/src/templates/scripts/genesys/mcp/doc-tools.ts +86 -0
  117. package/src/templates/scripts/genesys/mcp/editor-functions.ts +151 -0
  118. package/src/templates/scripts/genesys/mcp/editor-tools.ts +73 -0
  119. package/src/templates/scripts/genesys/mcp/get-scene-state.ts +35 -0
  120. package/src/templates/scripts/genesys/mcp/run-subprocess.ts +30 -0
  121. package/src/templates/scripts/genesys/mcp/search-actors.ts +858 -0
  122. package/src/templates/scripts/genesys/mcp/search-assets.ts +380 -0
  123. package/src/templates/scripts/genesys/mcp/utils.ts +281 -0
  124. package/src/templates/scripts/genesys/migrate-scenes-and-prefabs.ts +301 -0
  125. package/src/templates/scripts/genesys/misc.ts +42 -0
  126. package/src/templates/scripts/genesys/mock.ts +6 -0
  127. package/src/templates/scripts/genesys/place-actors.ts +179 -0
  128. package/src/templates/scripts/genesys/post-install.ts +39 -0
  129. package/src/templates/scripts/genesys/prefab.schema.json +85 -0
  130. package/src/templates/scripts/genesys/remove-engine-comments.ts +135 -0
  131. package/src/templates/scripts/genesys/run-mcp-inspector.bat +5 -0
  132. package/src/templates/scripts/genesys/storageProvider.ts +182 -0
  133. package/src/templates/scripts/genesys/validate-prefabs.ts +138 -0
  134. package/src/templates/src/index.ts +22 -0
  135. package/src/templates/src/templates/firstPerson/assets/default.genesys-scene +166 -0
  136. package/src/templates/src/templates/firstPerson/src/auto-imports.ts +0 -0
  137. package/src/templates/src/templates/firstPerson/src/game.ts +39 -0
  138. package/src/templates/src/templates/firstPerson/src/player.ts +59 -0
  139. package/src/templates/src/templates/fps/assets/default.genesys-scene +9460 -0
  140. package/src/templates/src/templates/fps/assets/models/SM_Beam_400.glb +0 -0
  141. package/src/templates/src/templates/fps/assets/models/SM_ChamferCube.glb +0 -0
  142. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400.glb +0 -0
  143. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400_Orange.glb +0 -0
  144. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400.glb +0 -0
  145. package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400_Orange.glb +0 -0
  146. package/src/templates/src/templates/fps/assets/models/SM_Ramp_400x400.glb +0 -0
  147. package/src/templates/src/templates/fps/assets/models/SM_Rifle.glb +0 -0
  148. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200.glb +0 -0
  149. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200_Orange.glb +0 -0
  150. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400.glb +0 -0
  151. package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400_Orange.glb +0 -0
  152. package/src/templates/src/templates/fps/src/auto-imports.ts +0 -0
  153. package/src/templates/src/templates/fps/src/game.ts +39 -0
  154. package/src/templates/src/templates/fps/src/player.ts +66 -0
  155. package/src/templates/src/templates/fps/src/weapon.ts +47 -0
  156. package/src/templates/src/templates/freeCamera/assets/default.genesys-scene +166 -0
  157. package/src/templates/src/templates/freeCamera/src/auto-imports.ts +0 -0
  158. package/src/templates/src/templates/freeCamera/src/game.ts +39 -0
  159. package/src/templates/src/templates/freeCamera/src/player.ts +40 -0
  160. package/src/templates/src/templates/sideScroller/assets/default.genesys-scene +122 -0
  161. package/src/templates/src/templates/sideScroller/src/auto-imports.ts +0 -0
  162. package/src/templates/src/templates/sideScroller/src/const.ts +46 -0
  163. package/src/templates/src/templates/sideScroller/src/game.ts +121 -0
  164. package/src/templates/src/templates/sideScroller/src/level-generator.ts +361 -0
  165. package/src/templates/src/templates/sideScroller/src/player.ts +123 -0
  166. package/src/templates/src/templates/thirdPerson/assets/default.genesys-scene +166 -0
  167. package/src/templates/src/templates/thirdPerson/src/auto-imports.ts +0 -0
  168. package/src/templates/src/templates/thirdPerson/src/game.ts +39 -0
  169. package/src/templates/src/templates/thirdPerson/src/player.ts +58 -0
  170. package/src/templates/src/templates/vehicle/assets/default.genesys-scene +226 -0
  171. package/src/templates/src/templates/vehicle/assets/models/cyberTruck/chassis.glb +0 -0
  172. package/src/templates/src/templates/vehicle/assets/models/cyberTruck/wheel.glb +0 -0
  173. package/src/templates/src/templates/vehicle/src/auto-imports.ts +0 -0
  174. package/src/templates/src/templates/vehicle/src/base-vehicle.ts +145 -0
  175. package/src/templates/src/templates/vehicle/src/game.ts +43 -0
  176. package/src/templates/src/templates/vehicle/src/mesh-vehicle.ts +189 -0
  177. package/src/templates/src/templates/vehicle/src/player.ts +106 -0
  178. package/src/templates/src/templates/vehicle/src/primitive-vehicle.ts +264 -0
  179. package/src/templates/src/templates/vehicle/src/ui-hints.ts +101 -0
  180. package/src/templates/src/templates/vr-game/assets/default.genesys-scene +247 -0
  181. package/src/templates/src/templates/vr-game/src/auto-imports.ts +1 -0
  182. package/src/templates/src/templates/vr-game/src/game.ts +66 -0
  183. package/src/templates/src/templates/vr-game/src/sample-vr-actor.ts +26 -0
  184. package/src/templates/tsconfig.json +35 -0
  185. package/src/templates/vite.config.ts +52 -0
@@ -0,0 +1,189 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+
4
+ import { BaseVehicle } from './base-vehicle.js';
5
+
6
+ /**
7
+ * A vehicle that uses a 3D mesh model for visual representation
8
+ *
9
+ * Key points:
10
+ * - Uses invisible collision box for physics simulation (separate from visual mesh)
11
+ * - Loads GLB model for visual representation via GLTFMeshComponent
12
+ * - Movement parameters configured in createVehicleMovementComponent
13
+ * - No component is persisted (root is transient) in order to support recreating all the components in code (which is desired)
14
+ * - Do not create the builtin wheel mesh since the GLB models are used instead (createWheelMeshes = false)
15
+ * - Visual mesh is positioned and scaled to match collision box
16
+ * - Wheel mesh transforms are updated using the onWheelUpdated delegate
17
+ *
18
+ */
19
+ @ENGINE.GameClass()
20
+ export class MeshVehicle extends BaseVehicle {
21
+ constructor() {
22
+ super();
23
+ // the root component is only used for collision and physics simulation
24
+ const rootComponent = ENGINE.MeshComponent.create({
25
+ geometry: new THREE.BoxGeometry(2.77, 1.79, 5.75),
26
+ material: new THREE.MeshStandardMaterial({
27
+ color: 0x4444FF,
28
+ roughness: 0.7,
29
+ metalness: 0.3,
30
+ visible: false,
31
+ transparent: true,
32
+ opacity: 0.5,
33
+ }),
34
+ physicsOptions: {
35
+ enabled: true,
36
+ motionType: ENGINE.PhysicsMotionType.Dynamic,
37
+ generateCollisionEvents: true,
38
+ },
39
+ });
40
+ rootComponent.name = 'root';
41
+ this.setRootComponent(rootComponent, true);
42
+ // mark the component as transient, this prevents all the components from being saved since we're recreating them in code
43
+ rootComponent.setTransient(true);
44
+
45
+ const chassisComponent = ENGINE.GLTFMeshComponent.create({
46
+ modelUrl: '@project/assets/models/cyberTruck/chassis.glb',
47
+ position: new THREE.Vector3(0, 0, -0.75),
48
+ material: new THREE.MeshStandardMaterial({
49
+ color: 0x4444FF,
50
+ roughness: 0.7,
51
+ metalness: 0.3,
52
+ }),
53
+ physicsOptions: {
54
+ enabled: false,
55
+ }
56
+ });
57
+ chassisComponent.castShadow = true;
58
+ chassisComponent.name = 'chassis';
59
+ rootComponent.add(chassisComponent);
60
+
61
+ const wheelMeshes: ENGINE.GLTFMeshComponent[] = [];
62
+ for (let i = 0; i < 4; i++) {
63
+ const wheelComponent = ENGINE.GLTFMeshComponent.create({
64
+ modelUrl: '@project/assets/models/cyberTruck/wheel.glb',
65
+ material: new THREE.MeshStandardMaterial({
66
+ color: 0x44FF44,
67
+ roughness: 0.7,
68
+ metalness: 0.3,
69
+ }),
70
+ physicsOptions: {
71
+ enabled: false,
72
+ }
73
+ });
74
+ wheelComponent.castShadow = true;
75
+ wheelComponent.name = `wheel_${i}`;
76
+ wheelMeshes.push(wheelComponent);
77
+ }
78
+ rootComponent.add(...wheelMeshes);
79
+ const vehicleMovementComponent = MeshVehicle.createVehicleMovementComponent();
80
+
81
+ this.movementComponent = vehicleMovementComponent;
82
+
83
+ const wheelComponents = this.getComponents(ENGINE.GLTFMeshComponent).filter(component => component.name.startsWith('wheel_'));
84
+
85
+ vehicleMovementComponent.onWheelUpdated.add((component, wheelIndex, wheelState) => {
86
+ // The first mesh component is the chassis component, so we need to skip it
87
+ const wheelMesh = wheelComponents[wheelIndex];
88
+
89
+ // Apply the final transformation to the wheel mesh
90
+ wheelMesh.position.copy(wheelState.relativePosition);
91
+ wheelMesh.quaternion.copy(wheelState.relativeQuaternion);
92
+
93
+ // Apply additional 180-degree rotation around Y-axis for wheels 0 and 2
94
+ if (wheelIndex === 0 || wheelIndex === 2) {
95
+ const yRotation = new THREE.Quaternion();
96
+ yRotation.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
97
+ wheelMesh.quaternion.multiplyQuaternions(wheelMesh.quaternion, yRotation);
98
+ }
99
+ });
100
+ }
101
+
102
+ static createVehicleMovementComponent(): ENGINE.VehicleMovementComponent {
103
+ const wheelBaseWidth = 3.2;
104
+ const wheelBaseLength = 3.8;
105
+ const wheelBaseHeight = -0.5;
106
+ const wheelRadius = 0.55;
107
+ const suspensionRestLength = 0.8;
108
+ const wheelWidth = 0.53;
109
+ const suspensionStiffness = 20;
110
+ const suspensionCompression = 1.83;
111
+ const suspensionRelaxation = 1.88;
112
+ const sideFrictionStiffness = 2;
113
+ const frictionSlip = 50;
114
+
115
+ // Create vehicle movement component
116
+ const vehicleMovement = ENGINE.VehicleMovementComponent.create({
117
+ createWheelMeshes: false,
118
+ maxEnginePower: 100,
119
+ maxBrakeForce: 1.5,
120
+ maxSteeringAngle: Math.PI / 6, // 30 degrees
121
+ engineResponseRate: 8.0,
122
+ steeringResponseRate: 12.0,
123
+ brakeResponseRate: 15.0,
124
+ wheelMaterialColor: 0x333333,
125
+ autoBreakWhenNotPossessed: 1,
126
+ autoStopBreak: 1,
127
+ wheels: [
128
+ // Default 4-wheel configuration
129
+ {
130
+ position: new THREE.Vector3(-wheelBaseWidth / 2, wheelBaseHeight, -wheelBaseLength / 2), // Front left
131
+ radius: wheelRadius,
132
+ width: wheelWidth,
133
+ canSteer: true,
134
+ isPowered: false,
135
+ canBrake: true,
136
+ suspensionRestLength,
137
+ suspensionStiffness,
138
+ suspensionCompression,
139
+ suspensionRelaxation,
140
+ sideFrictionStiffness,
141
+ frictionSlip,
142
+ },
143
+ {
144
+ position: new THREE.Vector3(wheelBaseWidth / 2, wheelBaseHeight, -wheelBaseLength / 2), // Front right
145
+ radius: wheelRadius,
146
+ width: wheelWidth,
147
+ canSteer: true,
148
+ isPowered: false,
149
+ canBrake: true,
150
+ suspensionRestLength,
151
+ suspensionStiffness,
152
+ suspensionCompression,
153
+ suspensionRelaxation,
154
+ sideFrictionStiffness,
155
+ frictionSlip,
156
+ },
157
+ {
158
+ position: new THREE.Vector3(-wheelBaseWidth / 2, wheelBaseHeight, wheelBaseLength / 2), // Rear left
159
+ radius: wheelRadius,
160
+ width: wheelWidth,
161
+ canSteer: false,
162
+ isPowered: true,
163
+ canBrake: true,
164
+ suspensionRestLength,
165
+ suspensionStiffness,
166
+ suspensionCompression,
167
+ suspensionRelaxation,
168
+ sideFrictionStiffness,
169
+ frictionSlip,
170
+ },
171
+ {
172
+ position: new THREE.Vector3(wheelBaseWidth / 2, wheelBaseHeight, wheelBaseLength / 2), // Rear right
173
+ radius: wheelRadius,
174
+ width: wheelWidth,
175
+ canSteer: false,
176
+ isPowered: true,
177
+ canBrake: true,
178
+ suspensionRestLength,
179
+ suspensionStiffness,
180
+ suspensionCompression,
181
+ suspensionRelaxation,
182
+ sideFrictionStiffness,
183
+ frictionSlip,
184
+ },
185
+ ],
186
+ });
187
+ return vehicleMovement;
188
+ }
189
+ }
@@ -0,0 +1,106 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+
4
+ import { BaseVehicle } from './base-vehicle.js';
5
+
6
+
7
+ /**
8
+ * A vehicle player class.
9
+ *
10
+ * Key points:
11
+ * - No need to provide movementComponent and camera, they are created internally
12
+ * - The pawn is set to be transient so it's never saved in the level
13
+ * - The directional light follows the player for consistent shadows
14
+ *
15
+ */
16
+ @ENGINE.GameClass()
17
+ export class VehiclePlayer extends ENGINE.ThirdPersonCharacterPawn {
18
+ // Omit all the options that are created internally
19
+ constructor() {
20
+ super();
21
+ // simple camera component - contains a perspective camera by default
22
+ const camera = new THREE.PerspectiveCamera(ENGINE.CAMERA_FOV, 1, 0.1, 1000);
23
+ // set camera position for third person view
24
+ camera.position.set(0, ENGINE.CHARACTER_HEIGHT * 1.3, ENGINE.CHARACTER_HEIGHT * 2);
25
+ camera.lookAt(0, 0, 0);
26
+
27
+ // use capsule root component for collision
28
+ const rootComponent = ENGINE.MeshComponent.create({
29
+ geometry: ENGINE.GameBuilder.createDefaultPawnCapsuleGeometry(),
30
+ material: new THREE.MeshStandardMaterial({ color: ENGINE.Color.YELLOW, visible: false, transparent: true, opacity: 0.5 }),
31
+ physicsOptions: {
32
+ enabled: true,
33
+ // KinematicVelocityBased is required to use the physics character controller
34
+ motionType: ENGINE.PhysicsMotionType.KinematicVelocityBased,
35
+ collisionProfile: ENGINE.DefaultCollisionProfile.Character,
36
+ },
37
+ });
38
+
39
+ this.setRootComponent(rootComponent, true);
40
+ this.rootComponent.add(camera);
41
+
42
+ // use third person movement mechanics
43
+ const movementComponent = ENGINE.CharacterMovementComponent.create({
44
+ movementType: ENGINE.CharacterMovementType.ThirdPerson,
45
+ });
46
+
47
+ this.movementComponent = movementComponent;
48
+
49
+ // Character model settings
50
+ this.enableDirectionalLightFollowing = true;
51
+ this.configUrl = '@engine/assets/character/config/mannequin-anim.json';
52
+
53
+ this.meshComponent.position.copy(new THREE.Vector3(0, -ENGINE.CHARACTER_HEIGHT / 2, 0));
54
+ this.meshComponent.rotation.copy(new THREE.Euler(0, Math.PI, 0));
55
+ rootComponent.add(this.meshComponent);
56
+
57
+ // set the pawn to be transient so it's never saved in the level
58
+ this.setTransient(true);
59
+ }
60
+
61
+ protected override doBeginPlay(): void {
62
+ super.doBeginPlay();
63
+ this.setupVehicleInteraction();
64
+ }
65
+
66
+ protected setupVehicleInteraction(): void {
67
+ // Add E key input handler for vehicle possession
68
+ this.onKeyDown.add((e: KeyboardEvent) => {
69
+ if (e.key.toLowerCase() === 'e') {
70
+ this.tryEnterVehicle();
71
+ return true;
72
+ }
73
+ return false;
74
+ });
75
+ }
76
+
77
+ protected tryEnterVehicle(): void {
78
+ // Find nearby vehicles that can be entered
79
+ const vehicles = this.world!.getActors(BaseVehicle);
80
+
81
+ for (const vehicle of vehicles) {
82
+ if (vehicle.canBeEntered() && vehicle.getNearbyPlayer() === this) {
83
+ this.enterVehicle(vehicle);
84
+ break;
85
+ }
86
+ }
87
+ }
88
+
89
+ protected enterVehicle(vehicle: BaseVehicle): void {
90
+ // Get the current player controller
91
+ const controller = this.getPlayerController();
92
+
93
+ if (controller) {
94
+ // Set this player as the entered player in the vehicle
95
+ vehicle.setEnteredPlayer(this);
96
+
97
+ // Unpossess current pawn and possess the vehicle
98
+ controller.unpossess();
99
+ controller.possess(vehicle);
100
+
101
+ // Hide the player character
102
+ this.rootComponent.visible = false;
103
+ this.rootComponent.setPhysicsEnabled(false);
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,264 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+
4
+ import { BaseVehicle } from './base-vehicle.js';
5
+
6
+
7
+ /**
8
+ * A simple vehicle with a box geometry
9
+ *
10
+ * Key points:
11
+ * - Movement parameters configured in createVehicleMovementComponent
12
+ * - No component is persisted (root is transient) in order to support recreating all the components in code (which is desired)
13
+ * - Rely on the builtin feature to create the wheel mesh (createWheelMeshes = true), no need to create them manually
14
+ * - All decorative meshes have physics disabled won't mess with the handling
15
+ * - Find the tail light meshes after the constructor so it works with deserialized actor
16
+ * - Rely on the base vehicle class to handle the camera and player interaction
17
+ */
18
+ @ENGINE.GameClass()
19
+ export class PrimitiveVehicle extends BaseVehicle {
20
+ private tailLightComponents: ENGINE.MeshComponent[] = [];
21
+
22
+ constructor() {
23
+ super();
24
+
25
+ // Create chassis mesh
26
+ const rootComponent = ENGINE.MeshComponent.create({
27
+ geometry: new THREE.BoxGeometry(2, 1, 4),
28
+ material: new THREE.MeshStandardMaterial({
29
+ color: 0x4444FF,
30
+ roughness: 0.7,
31
+ metalness: 0.3,
32
+ }),
33
+ physicsOptions: {
34
+ enabled: true,
35
+ motionType: ENGINE.PhysicsMotionType.Dynamic,
36
+ generateCollisionEvents: true,
37
+ },
38
+ });
39
+ rootComponent.castShadow = true;
40
+ rootComponent.name = 'chassis';
41
+ // mark the component as transient, this prevents all the components from being saved since we're recreating them in code
42
+ rootComponent.setTransient(true);
43
+ this.setRootComponent(rootComponent, true);
44
+ // create decorative meshes
45
+ PrimitiveVehicle.createDecorativeMeshes(rootComponent);
46
+ this.movementComponent = PrimitiveVehicle.createVehicleMovementComponent();
47
+
48
+ this.tailLightComponents = this.getComponents(ENGINE.MeshComponent)
49
+ .filter((component) => component.name.includes('_taillight'));
50
+ }
51
+
52
+ public override tickPrePhysics(deltaTime: number): void {
53
+ super.tickPrePhysics(deltaTime);
54
+ this.updateBrakeLights();
55
+ }
56
+
57
+ private updateBrakeLights(): void {
58
+ if (!this.movementComponent || this.tailLightComponents.length === 0) return;
59
+
60
+ const inputState = this.movementComponent.getInputState() as ENGINE.VehicleMovementInputState;
61
+ const isBraking = inputState.brakeInput > 0;
62
+
63
+ for (const tailLightComponent of this.tailLightComponents) {
64
+ const mesh = tailLightComponent.getMesh();
65
+ if (mesh && mesh.material) {
66
+ const material = mesh.material as THREE.MeshStandardMaterial;
67
+ if (isBraking) {
68
+ material.color.setHex(0xFF0000);
69
+ } else {
70
+ material.color.setHex(0xFFFFFF);
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ static createDecorativeMeshes(rootComponent: ENGINE.MeshComponent): void {
77
+ // Add windshield
78
+ const windshieldComponent = ENGINE.MeshComponent.create({
79
+ geometry: new THREE.BoxGeometry(1.8, 1.8, 0.1),
80
+ material: new THREE.MeshStandardMaterial({
81
+ color: 0x87CEEB,
82
+ transparent: true,
83
+ opacity: 0.5,
84
+ roughness: 0.1,
85
+ metalness: 0.1,
86
+ }),
87
+ physicsOptions: {
88
+ enabled: false,
89
+ }
90
+ });
91
+ windshieldComponent.position.set(0, 0.4, -1.5);
92
+ windshieldComponent.rotation.x = Math.PI / 8; // Slight tilt
93
+ windshieldComponent.name = 'windshield';
94
+ rootComponent.add(windshieldComponent);
95
+
96
+ // Add left headlight
97
+ const leftHeadlightComponent = ENGINE.MeshComponent.create({
98
+ geometry: new THREE.CylinderGeometry(0.15, 0.15, 0.1, 8),
99
+ material: new THREE.MeshStandardMaterial({
100
+ color: 0xFFFFFF,
101
+ emissive: 0xFFFFAA,
102
+ emissiveIntensity: 0.3,
103
+ }),
104
+ physicsOptions: {
105
+ enabled: false,
106
+ }
107
+ });
108
+ leftHeadlightComponent.position.set(-0.7, 0.1, -2.05);
109
+ leftHeadlightComponent.rotation.z = Math.PI / 2;
110
+ leftHeadlightComponent.name = 'left_headlight';
111
+ rootComponent.add(leftHeadlightComponent);
112
+
113
+ // Add right headlight
114
+ const rightHeadlightComponent = ENGINE.MeshComponent.create({
115
+ geometry: new THREE.CylinderGeometry(0.15, 0.15, 0.1, 8),
116
+ material: new THREE.MeshStandardMaterial({
117
+ color: 0xFFFFFF,
118
+ emissive: 0xFFFFAA,
119
+ emissiveIntensity: 0.3,
120
+ }),
121
+ physicsOptions: {
122
+ enabled: false,
123
+ }
124
+ });
125
+ rightHeadlightComponent.position.set(0.7, 0.1, -2.05);
126
+ rightHeadlightComponent.rotation.z = Math.PI / 2;
127
+ rightHeadlightComponent.name = 'right_headlight';
128
+ rootComponent.add(rightHeadlightComponent);
129
+
130
+ // Add left tail light
131
+ const leftTaillightMaterial = new THREE.MeshStandardMaterial({
132
+ color: 0xFF0000,
133
+ emissive: 0x440000,
134
+ emissiveIntensity: 0.2,
135
+ });
136
+ const leftTaillightComponent = ENGINE.MeshComponent.create({
137
+ geometry: new THREE.CylinderGeometry(0.1, 0.1, 0.1, 8),
138
+ material: leftTaillightMaterial,
139
+ physicsOptions: {
140
+ enabled: false,
141
+ }
142
+ });
143
+ leftTaillightComponent.position.set(-0.7, 0.1, 2.05);
144
+ leftTaillightComponent.rotation.z = Math.PI / 2;
145
+ leftTaillightComponent.name = 'left_taillight';
146
+ rootComponent.add(leftTaillightComponent);
147
+
148
+ // Add right tail light
149
+ const rightTaillightMaterial = new THREE.MeshStandardMaterial({
150
+ color: 0xFF0000,
151
+ emissive: 0x440000,
152
+ emissiveIntensity: 0.2,
153
+ });
154
+ const rightTaillightComponent = ENGINE.MeshComponent.create({
155
+ geometry: new THREE.CylinderGeometry(0.1, 0.1, 0.1, 8),
156
+ material: rightTaillightMaterial,
157
+ physicsOptions: {
158
+ enabled: false,
159
+ }
160
+ });
161
+ rightTaillightComponent.position.set(0.7, 0.1, 2.05);
162
+ rightTaillightComponent.rotation.z = Math.PI / 2;
163
+ rightTaillightComponent.name = 'right_taillight';
164
+ rootComponent.add(rightTaillightComponent);
165
+ }
166
+
167
+ static createVehicleMovementComponent(): ENGINE.VehicleMovementComponent {
168
+ const wheelBaseWidth = 2.6;
169
+ const wheelBaseLength = 3.0;
170
+ const wheelBaseHeight = 0;
171
+ const suspensionRestLength = 0.6;
172
+ const wheelRadius = 0.5;
173
+ const wheelWidth = 0.4;
174
+ const suspensionStiffness = 20;
175
+ const suspensionCompression = 0.83;
176
+ const suspensionRelaxation = 0.88;
177
+ const maxSuspensionForce = 6000.0;
178
+ const maxSuspensionTravel = 5.0;
179
+ const sideFrictionStiffness = 2;
180
+ const frictionSlip = 8;
181
+
182
+ const options = {
183
+ maxEnginePower: 35,
184
+ maxSteeringAngle: Math.PI / 6, // 30 degrees
185
+ maxBrakeForce: 1,
186
+ engineResponseRate: 8.0,
187
+ steeringResponseRate: 12.0,
188
+ brakeResponseRate: 15.0,
189
+ createWheelMeshes: true,
190
+ wheelMaterialColor: 0x333333,
191
+ autoBreakWhenNotPossessed: 1,
192
+ autoStopBreak: 0.5,
193
+ wheels: [
194
+ // Default 4-wheel configuration
195
+ {
196
+ position: new THREE.Vector3(-wheelBaseWidth / 2, wheelBaseHeight, -wheelBaseLength / 2), // Front left
197
+ radius: wheelRadius,
198
+ width: wheelWidth,
199
+ canSteer: true,
200
+ isPowered: false,
201
+ canBrake: true,
202
+ suspensionRestLength,
203
+ suspensionStiffness,
204
+ suspensionCompression,
205
+ suspensionRelaxation,
206
+ maxSuspensionForce,
207
+ maxSuspensionTravel,
208
+ sideFrictionStiffness,
209
+ frictionSlip,
210
+ },
211
+ {
212
+ position: new THREE.Vector3(wheelBaseWidth / 2, wheelBaseHeight, -wheelBaseLength / 2), // Front right
213
+ radius: wheelRadius,
214
+ width: wheelWidth,
215
+ canSteer: true,
216
+ isPowered: false,
217
+ canBrake: true,
218
+ suspensionRestLength,
219
+ suspensionStiffness,
220
+ suspensionCompression,
221
+ suspensionRelaxation,
222
+ maxSuspensionForce,
223
+ maxSuspensionTravel,
224
+ sideFrictionStiffness,
225
+ frictionSlip,
226
+ },
227
+ {
228
+ position: new THREE.Vector3(-wheelBaseWidth / 2, wheelBaseHeight, wheelBaseLength / 2), // Rear left
229
+ radius: wheelRadius,
230
+ width: wheelWidth,
231
+ canSteer: false,
232
+ isPowered: true,
233
+ canBrake: true,
234
+ suspensionRestLength,
235
+ suspensionStiffness,
236
+ suspensionCompression,
237
+ suspensionRelaxation,
238
+ maxSuspensionForce,
239
+ maxSuspensionTravel,
240
+ sideFrictionStiffness,
241
+ frictionSlip,
242
+ },
243
+ {
244
+ position: new THREE.Vector3(wheelBaseWidth / 2, wheelBaseHeight, wheelBaseLength / 2), // Rear right
245
+ radius: wheelRadius,
246
+ width: wheelWidth,
247
+ canSteer: false,
248
+ isPowered: true,
249
+ canBrake: true,
250
+ suspensionRestLength,
251
+ suspensionStiffness,
252
+ suspensionCompression,
253
+ suspensionRelaxation,
254
+ maxSuspensionForce,
255
+ maxSuspensionTravel,
256
+ sideFrictionStiffness,
257
+ frictionSlip,
258
+ },
259
+ ],
260
+ };
261
+
262
+ return ENGINE.VehicleMovementComponent.create(options);
263
+ }
264
+ }
@@ -0,0 +1,101 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+
3
+ /**
4
+ * Dismissable UI component that displays control hints when the game starts
5
+ */
6
+ @ENGINE.GameClass()
7
+ export class UIHints extends ENGINE.Actor {
8
+ private uiElement: ENGINE.UIElement | null = null;
9
+
10
+ constructor() {
11
+ super();
12
+ this.setTransient(true); // Don't save this UI element to the level
13
+ }
14
+
15
+ protected override doBeginPlay(): void {
16
+ super.doBeginPlay();
17
+ this.createHUD();
18
+ }
19
+
20
+ protected override doEndPlay(): void {
21
+ this.removeHUD();
22
+ super.doEndPlay();
23
+ }
24
+
25
+ private createHUD(): void {
26
+ const uiManager = this.world!.uiManager;
27
+
28
+ // Create the UI element using the engine's UI system
29
+ this.uiElement = uiManager.createElement('game-hints', {
30
+ style: {
31
+ position: 'absolute',
32
+ top: '50px',
33
+ right: '50px',
34
+ background: 'rgba(0, 0, 0, 0.9)',
35
+ color: 'white',
36
+ padding: '25px',
37
+ borderRadius: '12px',
38
+ fontFamily: 'Arial, sans-serif',
39
+ fontSize: '16px',
40
+ lineHeight: '1.5',
41
+ maxWidth: '400px',
42
+ border: '2px solid rgba(76, 175, 80, 0.6)',
43
+ boxShadow: '0 8px 32px rgba(0, 0, 0, 0.8)',
44
+ zIndex: '1001'
45
+ }
46
+ });
47
+
48
+ // Set the HTML content
49
+ this.uiElement.setHTML(`
50
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
51
+ <div style="font-weight: bold; color: #4CAF50; font-size: 18px;">
52
+ 🎮 Vehicle Game Controls
53
+ </div>
54
+ <button id="closeHints" style="
55
+ background: #f44336;
56
+ color: white;
57
+ border: none;
58
+ border-radius: 50%;
59
+ width: 30px;
60
+ height: 30px;
61
+ cursor: pointer;
62
+ font-size: 16px;
63
+ font-weight: bold;
64
+ ">×</button>
65
+ </div>
66
+ <div style="margin-bottom: 12px;">
67
+ <span style="color: #FFA726; font-weight: bold;">Movement:</span>
68
+ <br>• WASD or Arrow Keys - Move/Drive
69
+ <br>• Mouse - Camera control
70
+ </div>
71
+ <div style="margin-bottom: 12px;">
72
+ <span style="color: #FFA726; font-weight: bold;">Vehicle Interaction:</span>
73
+ <br>• E - Enter/Exit vehicle (when nearby)
74
+ <br>• F - Flip vehicle (when inside)
75
+ </div>
76
+ <div style="font-size: 14px; color: #CCCCCC; margin-top: 15px; text-align: center; font-style: italic;">
77
+ 💡 Walk near a vehicle and press E to enter
78
+ </div>
79
+ `);
80
+
81
+ // Add close button functionality
82
+ const closeButton = this.uiElement.querySelector('#closeHints') as HTMLButtonElement;
83
+ if (closeButton) {
84
+ closeButton.addEventListener('click', () => {
85
+ this.removeHUD();
86
+ });
87
+ }
88
+
89
+ // Auto-close after 10 seconds
90
+ setTimeout(() => {
91
+ this.removeHUD();
92
+ }, 10000);
93
+ }
94
+
95
+ private removeHUD(): void {
96
+ if (this.uiElement) {
97
+ this.uiElement.remove();
98
+ this.uiElement = null;
99
+ }
100
+ }
101
+ }