@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,102 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+ import './auto-imports.js';
4
+ import { SideScrollerLevelGenerator } from './level-generator.js';
5
+ import { SideScrollerPlayer } from './player.js';
6
+ // ============================================================================
7
+ // SIDE SCROLLER GAME
8
+ // ============================================================================
9
+ // A side-scroller game with animated character and infinite level generation
10
+ class SideScrollerGame extends ENGINE.BaseGameLoop {
11
+ player = null;
12
+ playerController = null;
13
+ levelGenerator = null;
14
+ createLoadingScreen() {
15
+ // enable the default loading screen
16
+ return new ENGINE.DefaultLoadingScreen();
17
+ }
18
+ async preStart() {
19
+ this.createLevel();
20
+ this.createPlayer();
21
+ }
22
+ tick(tickTime) {
23
+ super.tick(tickTime);
24
+ // Update level generation based on player position
25
+ if (this.player && this.levelGenerator) {
26
+ const playerPosition = this.player.getWorldPosition();
27
+ this.levelGenerator.updateLevel(playerPosition.x);
28
+ }
29
+ }
30
+ createPlayer() {
31
+ const spawnX = 1;
32
+ // Create the side-scroller player with animated character
33
+ const position = new THREE.Vector3(spawnX, ENGINE.CHARACTER_HEIGHT / 2, 0);
34
+ this.player = SideScrollerPlayer.create({ position });
35
+ // Create the controller and possess the player
36
+ this.playerController = ENGINE.PlayerController.create();
37
+ this.playerController.possess(this.player);
38
+ // Add both to the world
39
+ this.world.addActors(this.player, this.playerController);
40
+ this.setInitialPlayerPosition(spawnX);
41
+ }
42
+ /**
43
+ * Sets the initial position of the player.
44
+ * @param spawnX - The X coordinate to spawn the player at.
45
+ */
46
+ setInitialPlayerPosition(spawnX) {
47
+ const groundHeight = this.findGroundHeightAtX(spawnX);
48
+ if (groundHeight !== null) {
49
+ this.player?.setWorldPosition(new THREE.Vector3(spawnX, groundHeight + ENGINE.CHARACTER_HEIGHT / 2, 0));
50
+ }
51
+ else {
52
+ this.world.runInNextTick(() => {
53
+ this.setInitialPlayerPosition(spawnX);
54
+ });
55
+ }
56
+ }
57
+ /**
58
+ * Performs a hit test to find the ground height at a given X coordinate
59
+ * @param x - The X coordinate to test
60
+ * @returns The Y coordinate of the ground surface, or 0 if no ground is found
61
+ */
62
+ findGroundHeightAtX(x) {
63
+ // Start from a high position and cast downward
64
+ const rayOrigin = new THREE.Vector3(x, 50, 0);
65
+ const rayDirection = new THREE.Vector3(0, -1, 0); // Downward
66
+ const hitResults = this.world.getPhysicsEngine()?.performHitTest({
67
+ origin: rayOrigin,
68
+ direction: rayDirection,
69
+ maxDistance: 100, // Cast ray down 100 units
70
+ stopOnFirstHit: true,
71
+ });
72
+ if (hitResults && hitResults.length > 0) {
73
+ // Return the Y coordinate of the hit location (top surface of ground)
74
+ return hitResults[0].hitLocation.y;
75
+ }
76
+ return null;
77
+ }
78
+ createLevel() {
79
+ // Create the level generator
80
+ this.levelGenerator = new SideScrollerLevelGenerator(this.world, {
81
+ enableDebugVisualization: false,
82
+ createGroundMesh: true
83
+ });
84
+ // Generate initial level chunks
85
+ this.levelGenerator.updateLevel(0);
86
+ }
87
+ destroy() {
88
+ // Clean up level generator
89
+ if (this.levelGenerator) {
90
+ this.levelGenerator.cleanup();
91
+ this.levelGenerator = null;
92
+ }
93
+ super.destroy();
94
+ }
95
+ }
96
+ export function main(container, gameId) {
97
+ const game = new SideScrollerGame(container, {
98
+ ...ENGINE.BaseGameLoop.DEFAULT_OPTIONS,
99
+ gameId
100
+ });
101
+ return game;
102
+ }
@@ -0,0 +1,249 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+ import { COLORS, LEVEL_CONFIG } from './const.js';
4
+ // Simple level generator for infinite side-scrolling levels
5
+ export class SideScrollerLevelGenerator {
6
+ world;
7
+ // Map of chunk index to chunk
8
+ levelChunks = new Map();
9
+ // Configuration
10
+ chunkSize;
11
+ chunksAhead;
12
+ chunksBehind;
13
+ platformHeightRange;
14
+ platformWidthRange;
15
+ obstacleHeightRange;
16
+ chunkMargin;
17
+ minSpacing;
18
+ debugVisualizationEnabled = false;
19
+ createGroundMesh = false;
20
+ constructor(world, options = {}) {
21
+ this.world = world;
22
+ // Apply defaults
23
+ this.chunkSize = options.chunkSize ?? LEVEL_CONFIG.CHUNK_SIZE;
24
+ this.chunksAhead = options.chunksAhead ?? LEVEL_CONFIG.CHUNKS_AHEAD;
25
+ this.chunksBehind = options.chunksBehind ?? LEVEL_CONFIG.CHUNKS_BEHIND;
26
+ this.platformHeightRange = options.platformHeightRange ?? LEVEL_CONFIG.PLATFORM_HEIGHT_RANGE;
27
+ this.platformWidthRange = options.platformWidthRange ?? LEVEL_CONFIG.PLATFORM_WIDTH_RANGE;
28
+ this.obstacleHeightRange = options.obstacleHeightRange ?? LEVEL_CONFIG.OBSTACLE_HEIGHT_RANGE;
29
+ this.chunkMargin = options.chunkMargin ?? LEVEL_CONFIG.CHUNK_MARGIN;
30
+ this.minSpacing = options.minSpacing ?? LEVEL_CONFIG.MIN_SPACING;
31
+ this.debugVisualizationEnabled = options.enableDebugVisualization ?? false;
32
+ this.createGroundMesh = options.createGroundMesh ?? false;
33
+ }
34
+ updateLevel(playerX) {
35
+ const playerChunkIndex = this.getChunkIndex(playerX);
36
+ const validChunkIndices = [];
37
+ for (let i = playerChunkIndex - this.chunksBehind; i <= playerChunkIndex + this.chunksAhead; i++) {
38
+ this.generateChunk(i);
39
+ validChunkIndices.push(i);
40
+ }
41
+ for (const index of this.levelChunks.keys()) {
42
+ if (!validChunkIndices.includes(index)) {
43
+ this.removeChunk(index);
44
+ }
45
+ }
46
+ }
47
+ getChunkIndex(x) {
48
+ return Math.floor(x / this.chunkSize);
49
+ }
50
+ removeChunk(index) {
51
+ const chunk = this.levelChunks.get(index);
52
+ if (chunk) {
53
+ this.world.removeActors(...chunk.actors);
54
+ this.levelChunks.delete(index);
55
+ console.log(`[LevelGenerator] Removed chunk at index ${index}`);
56
+ }
57
+ }
58
+ generateChunk(index) {
59
+ if (this.levelChunks.has(index)) {
60
+ return;
61
+ }
62
+ const startX = index * this.chunkSize;
63
+ const endX = startX + this.chunkSize;
64
+ const actors = [];
65
+ // Generate positioned elements for this chunk
66
+ const elements = this.generatePositionedElements(startX);
67
+ // Create actors for each element
68
+ for (const element of elements) {
69
+ const actor = this.createElementActor(element);
70
+ actors.push(actor);
71
+ }
72
+ // Add ground mesh if enabled
73
+ if (this.createGroundMesh) {
74
+ const groundActor = this.createGroundMeshActor(startX);
75
+ actors.push(groundActor);
76
+ }
77
+ // Add debug visualization if enabled
78
+ if (this.debugVisualizationEnabled) {
79
+ const debugActor = this.createChunkDebugVisualization(startX);
80
+ actors.push(debugActor);
81
+ }
82
+ // Add all actors to the world
83
+ this.world.addActors(...actors);
84
+ // Track this chunk
85
+ const chunk = {
86
+ startX,
87
+ endX,
88
+ actors,
89
+ };
90
+ console.log(`[LevelGenerator] Generated chunk at index ${index}`);
91
+ this.levelChunks.set(index, chunk);
92
+ }
93
+ generatePositionedElements(startX) {
94
+ const elements = [];
95
+ const usableWidth = this.chunkSize - (this.chunkMargin * 2); // Leave margin on each side
96
+ const usableStartX = startX + this.chunkMargin;
97
+ // Simple alternating generation: platform -> obstacle -> platform -> obstacle...
98
+ const totalElements = Math.floor(usableWidth / this.minSpacing); // How many elements can fit
99
+ let currentX = usableStartX;
100
+ let isPlatform = true; // Start with platform
101
+ for (let i = 0; i < totalElements && currentX < (startX + this.chunkSize - this.chunkMargin); i++) {
102
+ if (isPlatform) {
103
+ // Generate platform with random dimensions
104
+ const width = this.platformWidthRange.min +
105
+ Math.random() * (this.platformWidthRange.max - this.platformWidthRange.min);
106
+ const height = this.platformHeightRange.min +
107
+ Math.random() * (this.platformHeightRange.max - this.platformHeightRange.min);
108
+ elements.push({
109
+ x: currentX + width / 2, // Center position
110
+ width: width,
111
+ height: height,
112
+ type: 'platform'
113
+ });
114
+ currentX += width + this.minSpacing; // Move past this platform plus spacing
115
+ }
116
+ else {
117
+ // Generate obstacle with random dimensions
118
+ const width = LEVEL_CONFIG.OBSTACLE_WIDTH_RANGE.min +
119
+ Math.random() * (LEVEL_CONFIG.OBSTACLE_WIDTH_RANGE.max - LEVEL_CONFIG.OBSTACLE_WIDTH_RANGE.min);
120
+ const height = this.obstacleHeightRange.min +
121
+ Math.random() * (this.obstacleHeightRange.max - this.obstacleHeightRange.min);
122
+ elements.push({
123
+ x: currentX + width / 2, // Center position
124
+ width: width,
125
+ height: height,
126
+ type: 'obstacle'
127
+ });
128
+ currentX += width + this.minSpacing; // Move past this obstacle plus spacing
129
+ }
130
+ // Alternate between platform and obstacle
131
+ isPlatform = !isPlatform;
132
+ }
133
+ return elements;
134
+ }
135
+ createElementActor(element) {
136
+ const geometry = new THREE.BoxGeometry(element.width, element.height, LEVEL_CONFIG.DEPTH);
137
+ const material = new THREE.MeshStandardMaterial({ color: element.type === 'platform' ? COLORS.PLATFORM : COLORS.OBSTACLE });
138
+ const meshComponent = ENGINE.MeshComponent.create({
139
+ geometry: geometry,
140
+ material: material,
141
+ position: new THREE.Vector3(element.x, element.height / 2, 0), // Position at ground level
142
+ physicsOptions: {
143
+ enabled: true,
144
+ motionType: ENGINE.PhysicsMotionType.Static,
145
+ collisionProfile: ENGINE.DefaultCollisionProfile.BlockAll,
146
+ },
147
+ });
148
+ // Enable shadow casting and receiving
149
+ meshComponent.castShadow = true;
150
+ meshComponent.receiveShadow = true;
151
+ const actor = ENGINE.Actor.create();
152
+ actor.setRootComponent(meshComponent, false);
153
+ return actor;
154
+ }
155
+ createChunkDebugVisualization(startX) {
156
+ // Create a wireframe box to visualize chunk boundaries
157
+ const chunkGeometry = new THREE.BoxGeometry(this.chunkSize, LEVEL_CONFIG.DEBUG_VISUALIZATION_HEIGHT, LEVEL_CONFIG.DEPTH);
158
+ const chunkMaterial = new THREE.MeshBasicMaterial({
159
+ color: LEVEL_CONFIG.DEBUG_VISUALIZATION_COLOR,
160
+ opacity: LEVEL_CONFIG.DEBUG_VISUALIZATION_OPACITY,
161
+ wireframe: true,
162
+ transparent: true,
163
+ });
164
+ const meshComponent = ENGINE.MeshComponent.create({
165
+ geometry: chunkGeometry,
166
+ material: chunkMaterial,
167
+ position: new THREE.Vector3(startX + this.chunkSize / 2, LEVEL_CONFIG.DEBUG_VISUALIZATION_HEIGHT / 2, 0),
168
+ physicsOptions: {
169
+ enabled: false,
170
+ },
171
+ });
172
+ const chunkBoundary = ENGINE.Actor.create();
173
+ chunkBoundary.setRootComponent(meshComponent, false);
174
+ return chunkBoundary;
175
+ }
176
+ createGroundMeshActor(startX) {
177
+ // Create a ground mesh that spans the entire chunk width
178
+ const groundGeometry = new THREE.BoxGeometry(this.chunkSize, LEVEL_CONFIG.GROUND_HEIGHT, LEVEL_CONFIG.DEPTH);
179
+ const groundMaterial = new THREE.MeshStandardMaterial({
180
+ color: COLORS.GROUND,
181
+ });
182
+ const groundMeshComponent = ENGINE.MeshComponent.create({
183
+ geometry: groundGeometry,
184
+ material: groundMaterial,
185
+ position: new THREE.Vector3(startX + this.chunkSize / 2, LEVEL_CONFIG.GROUND_Y_POSITION, 0),
186
+ physicsOptions: {
187
+ enabled: true,
188
+ motionType: ENGINE.PhysicsMotionType.Static,
189
+ collisionProfile: ENGINE.DefaultCollisionProfile.BlockAll,
190
+ },
191
+ });
192
+ // Enable shadow receiving
193
+ groundMeshComponent.receiveShadow = true;
194
+ const actor = ENGINE.Actor.create();
195
+ actor.setRootComponent(groundMeshComponent, false);
196
+ return actor;
197
+ }
198
+ /**
199
+ * Gets detailed geometry information at a given x location.
200
+ * @param x - The x coordinate to query
201
+ * @returns An array of geometry info objects at the given x location
202
+ */
203
+ getGeometryInfoAtX(x) {
204
+ const chunkIndex = this.getChunkIndex(x);
205
+ const chunk = this.levelChunks.get(chunkIndex);
206
+ if (!chunk) {
207
+ return [];
208
+ }
209
+ const geometries = [];
210
+ // Add ground mesh if enabled
211
+ if (this.createGroundMesh) {
212
+ const groundCenterY = LEVEL_CONFIG.GROUND_Y_POSITION;
213
+ const groundHalfHeight = LEVEL_CONFIG.GROUND_HEIGHT / 2;
214
+ geometries.push({
215
+ type: 'ground',
216
+ topY: groundCenterY + groundHalfHeight,
217
+ bottomY: groundCenterY - groundHalfHeight,
218
+ width: this.chunkSize,
219
+ centerX: chunk.startX + this.chunkSize / 2
220
+ });
221
+ }
222
+ // Add positioned elements (platforms and obstacles)
223
+ const elements = this.generatePositionedElements(chunk.startX);
224
+ for (const element of elements) {
225
+ const elementLeft = element.x - element.width / 2;
226
+ const elementRight = element.x + element.width / 2;
227
+ // Check if the x coordinate falls within this element's bounds
228
+ if (x >= elementLeft && x <= elementRight) {
229
+ geometries.push({
230
+ type: element.type,
231
+ topY: element.height,
232
+ bottomY: 0, // Elements are positioned at ground level (y=0 bottom)
233
+ width: element.width,
234
+ centerX: element.x
235
+ });
236
+ }
237
+ }
238
+ // Sort by top height (highest first)
239
+ geometries.sort((a, b) => b.topY - a.topY);
240
+ return geometries;
241
+ }
242
+ cleanup() {
243
+ for (const index of this.levelChunks.keys()) {
244
+ this.removeChunk(index);
245
+ }
246
+ this.levelChunks.clear();
247
+ console.log('[LevelGenerator] Cleaned up');
248
+ }
249
+ }
@@ -0,0 +1,100 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import * as ENGINE from '@gnsx/genesys.js';
8
+ import * as THREE from 'three';
9
+ import { CAMERA_SETTINGS, PLAYER_MOVEMENT } from './const.js';
10
+ // ============================================================================
11
+ // PLAYER PAWN
12
+ // ============================================================================
13
+ // The side-scroller player pawn class using animated character.
14
+ let SideScrollerPlayer = class SideScrollerPlayer extends ENGINE.ThirdPersonCharacterPawn {
15
+ camera;
16
+ characterMesh = null;
17
+ smoothedCameraHeight = null;
18
+ smoothedLookAtHeight = null;
19
+ constructor() {
20
+ super();
21
+ // Create directional movement component for side-scrolling
22
+ const movementComponent = ENGINE.DirectionalCharacterMovementComponent.create({
23
+ direction: ENGINE.CharacterMovementDirection.LeftRight, // Move left and right
24
+ autoMove: false, // Player controls movement manually
25
+ jumpSpeed: PLAYER_MOVEMENT.JUMP_SPEED,
26
+ maxSpeed: PLAYER_MOVEMENT.MAX_SPEED,
27
+ maxMidAirJumps: PLAYER_MOVEMENT.MAX_MID_AIR_JUMPS, // Allow double jump
28
+ // Physics and movement settings for platformer feel
29
+ accelerationLambda: PLAYER_MOVEMENT.ACCELERATION_LAMBDA,
30
+ decelerationLambda: PLAYER_MOVEMENT.DECELERATION_LAMBDA,
31
+ midAirAccelerationLambda: PLAYER_MOVEMENT.MID_AIR_ACCELERATION_LAMBDA,
32
+ midAirDecelerationLambda: PLAYER_MOVEMENT.MID_AIR_DECELERATION_LAMBDA,
33
+ });
34
+ // Physics options for the player
35
+ const physicsOptions = {
36
+ enabled: true,
37
+ motionType: ENGINE.PhysicsMotionType.KinematicVelocityBased,
38
+ collisionProfile: ENGINE.DefaultCollisionProfile.Character,
39
+ generateCollisionEvents: true,
40
+ };
41
+ // Create root component with physics (invisible capsule for collision)
42
+ const rootComponent = ENGINE.MeshComponent.create({
43
+ geometry: new THREE.CapsuleGeometry(ENGINE.CHARACTER_WIDTH / 2, ENGINE.CHARACTER_HEIGHT - ENGINE.CHARACTER_WIDTH),
44
+ material: new THREE.MeshStandardMaterial({
45
+ color: 0xffff00,
46
+ visible: false, // Make invisible since we'll have the animated character mesh
47
+ transparent: true,
48
+ opacity: 0.5
49
+ }),
50
+ physicsOptions,
51
+ });
52
+ this.setRootComponent(rootComponent, true);
53
+ this.movementComponent = movementComponent;
54
+ // Use default character model from engine assets
55
+ this.configUrl = '@engine/assets/character/config/mannequin-anim.json';
56
+ this.meshComponent.position.copy(new THREE.Vector3(0, -ENGINE.CHARACTER_HEIGHT / 2, 0));
57
+ this.meshComponent.rotation.copy(new THREE.Euler(0, Math.PI, 0)); // Face right initially
58
+ rootComponent.add(this.meshComponent);
59
+ // Enable shadow casting for the character
60
+ this.rootComponent.castShadow = true;
61
+ this.camera = new THREE.PerspectiveCamera(CAMERA_SETTINGS.FOV, 1, CAMERA_SETTINGS.NEAR, CAMERA_SETTINGS.FAR);
62
+ this.enableDirectionalLightFollowing = true;
63
+ }
64
+ doBeginPlay() {
65
+ super.doBeginPlay();
66
+ this.getWorld()?.setOverrideCamera(this.camera);
67
+ }
68
+ tickPrePhysics(deltaTime) {
69
+ super.tickPrePhysics(deltaTime);
70
+ // manually update the camera position
71
+ const desiredCameraPosition = this.rootComponent.localToWorld(new THREE.Vector3(CAMERA_SETTINGS.POSITION.x, CAMERA_SETTINGS.POSITION.y, CAMERA_SETTINGS.POSITION.z));
72
+ const desiredCameraLookAt = this.rootComponent.localToWorld(new THREE.Vector3(CAMERA_SETTINGS.LOOK_AT.x, CAMERA_SETTINGS.LOOK_AT.y, CAMERA_SETTINGS.LOOK_AT.z));
73
+ this.smoothedCameraHeight ??= desiredCameraPosition.y;
74
+ this.smoothedLookAtHeight ??= desiredCameraLookAt.y;
75
+ this.smoothedCameraHeight = THREE.MathUtils.damp(this.smoothedCameraHeight, desiredCameraPosition.y, CAMERA_SETTINGS.DAMP_FACTOR, deltaTime);
76
+ this.smoothedLookAtHeight = THREE.MathUtils.damp(this.smoothedLookAtHeight, desiredCameraLookAt.y, CAMERA_SETTINGS.DAMP_FACTOR, deltaTime);
77
+ this.camera.position.x = desiredCameraPosition.x;
78
+ this.camera.position.y = this.smoothedCameraHeight;
79
+ this.camera.position.z = desiredCameraPosition.z;
80
+ this.camera.lookAt(new THREE.Vector3(desiredCameraLookAt.x, this.smoothedLookAtHeight, desiredCameraLookAt.z));
81
+ // change the direction of the character mesh based on the movement direction
82
+ this.characterMesh ??= this.getComponent(ENGINE.GLTFMeshComponent);
83
+ if (this.characterMesh && this.movementComponent) {
84
+ const movementComponent = this.movementComponent;
85
+ const velocity = movementComponent.getVelocities();
86
+ if (velocity.right > 0.1) {
87
+ // Moving right - face right (90 degrees)
88
+ this.characterMesh.rotation.set(0, Math.PI / 2, 0);
89
+ }
90
+ else if (velocity.right < -0.1) {
91
+ // Moving left - face left (-90 degrees)
92
+ this.characterMesh.rotation.set(0, -Math.PI / 2, 0);
93
+ }
94
+ }
95
+ }
96
+ };
97
+ SideScrollerPlayer = __decorate([
98
+ ENGINE.GameClass()
99
+ ], SideScrollerPlayer);
100
+ export { SideScrollerPlayer };
@@ -0,0 +1,30 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+ import { ThirdPersonPlayer } from './player.js';
4
+ import './auto-imports.js';
5
+ class ThirdPersonGame extends ENGINE.BaseGameLoop {
6
+ pawn = null;
7
+ controller = null;
8
+ createLoadingScreen() {
9
+ // enable the default loading screen
10
+ return new ENGINE.DefaultLoadingScreen();
11
+ }
12
+ async preStart() {
13
+ // default spawn location
14
+ const position = new THREE.Vector3(0, ENGINE.CHARACTER_HEIGHT / 2, 0);
15
+ // create the pawn
16
+ this.pawn = ThirdPersonPlayer.create({ position });
17
+ // create the controller and possess the pawn
18
+ this.controller = ENGINE.PlayerController.create();
19
+ this.controller.possess(this.pawn);
20
+ // add both to the world
21
+ this.world.addActors(this.pawn, this.controller);
22
+ }
23
+ }
24
+ export function main(container, gameId) {
25
+ const game = new ThirdPersonGame(container, {
26
+ ...ENGINE.BaseGameLoop.DEFAULT_OPTIONS,
27
+ gameId
28
+ });
29
+ return game;
30
+ }
@@ -0,0 +1,58 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import * as ENGINE from '@gnsx/genesys.js';
8
+ import * as THREE from 'three';
9
+ /**
10
+ * A third person player class.
11
+ *
12
+ * Key points:
13
+ * - No need to provide movementComponent and camera, they are created internally
14
+ * - The pawn is set to be transient so it's never saved in the level
15
+ * - The directional light follows the player for consistent shadows
16
+ *
17
+ */
18
+ let ThirdPersonPlayer = class ThirdPersonPlayer extends ENGINE.ThirdPersonCharacterPawn {
19
+ // Omit all the options that are created internally
20
+ constructor() {
21
+ super();
22
+ // simple camera component - contains a perspective camera by default
23
+ const camera = new THREE.PerspectiveCamera(ENGINE.CAMERA_FOV, 1, 0.1, 1000);
24
+ // set camera position for third person view
25
+ camera.position.set(0, ENGINE.CHARACTER_HEIGHT * 1.3, ENGINE.CHARACTER_HEIGHT * 2);
26
+ camera.lookAt(0, 0, 0);
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
+ this.setRootComponent(rootComponent, true);
39
+ // use third person movement mechanics
40
+ const movementComponent = ENGINE.CharacterMovementComponent.create({
41
+ movementType: ENGINE.CharacterMovementType.ThirdPerson,
42
+ });
43
+ this.movementComponent = movementComponent;
44
+ rootComponent.add(camera);
45
+ this.enableDirectionalLightFollowing = true;
46
+ // Character model settings
47
+ this.configUrl = '@engine/assets/character/config/mannequin-anim.json';
48
+ this.meshComponent.position.copy(new THREE.Vector3(0, -ENGINE.CHARACTER_HEIGHT / 2, 0));
49
+ this.meshComponent.rotation.copy(new THREE.Euler(0, Math.PI, 0));
50
+ rootComponent.add(this.meshComponent);
51
+ // set the pawn to be transient so it's never saved in the level
52
+ this.setTransient(true);
53
+ }
54
+ };
55
+ ThirdPersonPlayer = __decorate([
56
+ ENGINE.GameClass()
57
+ ], ThirdPersonPlayer);
58
+ export { ThirdPersonPlayer };
@@ -0,0 +1,122 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+ import { VehiclePlayer } from './player.js';
4
+ /**
5
+ * Base vehicle class that provides common vehicle functionality
6
+ *
7
+ * Key points:
8
+ * - Handles camera setup with follow component for smooth third-person view
9
+ * - Manages player interaction (enter/exit vehicle with proximity detection)
10
+ * - Provides E key input handler for vehicle exit
11
+ * - Calculates safe exit position when player leaves vehicle
12
+ * - Automatically switches possession between player and vehicle
13
+ *
14
+ */
15
+ export class BaseVehicle extends ENGINE.Pawn {
16
+ interactionDistance = 5;
17
+ nearbyPlayer = null;
18
+ enteredPlayer = null;
19
+ doBeginPlay() {
20
+ super.doBeginPlay();
21
+ this.setupCamera();
22
+ this.setupVicinityCheck();
23
+ this.setupExitHandler();
24
+ }
25
+ setupCamera() {
26
+ // create a follow component for smooth camera movement
27
+ const followComponent = ENGINE.FollowComponent.create({
28
+ positionOffset: new THREE.Vector3(0, 4, 10),
29
+ positionDamping: 0.02,
30
+ lookAtOffset: new THREE.Vector3(0, 0, 0),
31
+ target: this.rootComponent,
32
+ useCameraLookAtConvention: true,
33
+ });
34
+ this.rootComponent.add(followComponent);
35
+ // create a camera and add it to the follow component
36
+ const camera = new THREE.PerspectiveCamera(ENGINE.CAMERA_FOV, 1, ENGINE.CAMERA_NEAR, ENGINE.CAMERA_FAR);
37
+ followComponent.add(camera);
38
+ }
39
+ setupVicinityCheck() {
40
+ // Check every frame for nearby players using tick delegate
41
+ this.onTickPostPhysics.add(() => {
42
+ this.checkForNearbyPlayer();
43
+ });
44
+ }
45
+ checkForNearbyPlayer() {
46
+ const players = this.world.getActors(VehiclePlayer);
47
+ let foundNearbyPlayer = false;
48
+ for (const player of players) {
49
+ const distance = this.rootComponent.position.distanceTo(player.rootComponent.position);
50
+ if (distance <= this.interactionDistance) {
51
+ this.nearbyPlayer = player;
52
+ foundNearbyPlayer = true;
53
+ break;
54
+ }
55
+ }
56
+ if (!foundNearbyPlayer) {
57
+ this.nearbyPlayer = null;
58
+ }
59
+ }
60
+ canBeEntered() {
61
+ return this.nearbyPlayer !== null;
62
+ }
63
+ getNearbyPlayer() {
64
+ return this.nearbyPlayer;
65
+ }
66
+ setupExitHandler() {
67
+ // Add E key input handler for vehicle exit
68
+ this.onKeyDown.add((e) => {
69
+ if (e.key.toLowerCase() === 'e' && this.enteredPlayer) {
70
+ this.exitVehicle();
71
+ return true;
72
+ }
73
+ else if (e.key.toLowerCase() === 'f') {
74
+ if (this.movementComponent instanceof ENGINE.VehicleMovementComponent) {
75
+ this.movementComponent.flipVehicle(false);
76
+ }
77
+ return true;
78
+ }
79
+ return false;
80
+ });
81
+ }
82
+ setEnteredPlayer(player) {
83
+ this.enteredPlayer = player;
84
+ }
85
+ exitVehicle() {
86
+ if (!this.enteredPlayer)
87
+ return;
88
+ // Calculate exit position (to the side of the vehicle)
89
+ const exitPosition = this.calculateExitPosition();
90
+ // Get the current player controller
91
+ const controller = this.getPlayerController();
92
+ if (controller) {
93
+ // Move player to exit position
94
+ this.enteredPlayer.rootComponent.position.copy(exitPosition);
95
+ // Make player face the same direction as the vehicle (yaw only)
96
+ this.enteredPlayer.rootComponent.rotation.x = 0;
97
+ this.enteredPlayer.rootComponent.rotation.z = 0;
98
+ this.enteredPlayer.rootComponent.rotation.y = this.rootComponent.rotation.y;
99
+ // Restore player visibility and physics
100
+ this.enteredPlayer.rootComponent.visible = true;
101
+ // Only the root component has physics, do not propagate to children
102
+ this.enteredPlayer.rootComponent.setPhysicsEnabled(true, false);
103
+ // Switch possession back to player
104
+ controller.unpossess();
105
+ controller.possess(this.enteredPlayer);
106
+ // Clear entered player reference
107
+ this.enteredPlayer = null;
108
+ }
109
+ }
110
+ calculateExitPosition() {
111
+ // Calculate a position to the right side of the vehicle
112
+ const vehiclePosition = this.rootComponent.position.clone();
113
+ const vehicleRotation = this.rootComponent.rotation;
114
+ // Create a vector pointing to the right of the vehicle
115
+ const rightVector = new THREE.Vector3(1, 0, 0);
116
+ rightVector.applyEuler(vehicleRotation);
117
+ // Position the player 2 units to the right and slightly forward
118
+ const exitOffset = rightVector.multiplyScalar(2);
119
+ exitOffset.y = 1; // Raise slightly above ground
120
+ return vehiclePosition.add(exitOffset);
121
+ }
122
+ }