@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,121 @@
1
+
2
+ import * as ENGINE from '@gnsx/genesys.js';
3
+ import * as THREE from 'three';
4
+ import './auto-imports.js';
5
+
6
+ import { SideScrollerLevelGenerator } from './level-generator.js';
7
+ import { SideScrollerPlayer } from './player.js';
8
+
9
+ // ============================================================================
10
+ // SIDE SCROLLER GAME
11
+ // ============================================================================
12
+
13
+ // A side-scroller game with animated character and infinite level generation
14
+ class SideScrollerGame extends ENGINE.BaseGameLoop {
15
+ private player: SideScrollerPlayer | null = null;
16
+ private playerController: ENGINE.PlayerController | null = null;
17
+ private levelGenerator: SideScrollerLevelGenerator | null = null;
18
+
19
+ protected override createLoadingScreen(): ENGINE.ILoadingScreen | null {
20
+ // enable the default loading screen
21
+ return new ENGINE.DefaultLoadingScreen();
22
+ }
23
+
24
+ protected override async preStart(): Promise<void> {
25
+ this.createLevel();
26
+ this.createPlayer();
27
+ }
28
+
29
+ protected override tick(tickTime: ENGINE.TickTime): void {
30
+ super.tick(tickTime);
31
+
32
+ // Update level generation based on player position
33
+ if (this.player && this.levelGenerator) {
34
+ const playerPosition = this.player.getWorldPosition();
35
+ this.levelGenerator.updateLevel(playerPosition.x);
36
+ }
37
+ }
38
+
39
+ private createPlayer(): void {
40
+ const spawnX = 1;
41
+ // Create the side-scroller player with animated character
42
+ const position = new THREE.Vector3(spawnX, ENGINE.CHARACTER_HEIGHT / 2, 0);
43
+ this.player = SideScrollerPlayer.create({ position });
44
+
45
+ // Create the controller and possess the player
46
+ this.playerController = ENGINE.PlayerController.create();
47
+ this.playerController.possess(this.player);
48
+
49
+ // Add both to the world
50
+ this.world.addActors(this.player, this.playerController);
51
+ this.setInitialPlayerPosition(spawnX);
52
+ }
53
+
54
+ /**
55
+ * Sets the initial position of the player.
56
+ * @param spawnX - The X coordinate to spawn the player at.
57
+ */
58
+ private setInitialPlayerPosition(spawnX: number): void {
59
+ const groundHeight = this.findGroundHeightAtX(spawnX);
60
+ if (groundHeight !== null) {
61
+ this.player?.setWorldPosition(new THREE.Vector3(spawnX, groundHeight + ENGINE.CHARACTER_HEIGHT / 2, 0));
62
+ } else {
63
+ this.world.runInNextTick(() => {
64
+ this.setInitialPlayerPosition(spawnX);
65
+ });
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Performs a hit test to find the ground height at a given X coordinate
71
+ * @param x - The X coordinate to test
72
+ * @returns The Y coordinate of the ground surface, or 0 if no ground is found
73
+ */
74
+ private findGroundHeightAtX(x: number): number | null {
75
+ // Start from a high position and cast downward
76
+ const rayOrigin = new THREE.Vector3(x, 50, 0);
77
+ const rayDirection = new THREE.Vector3(0, -1, 0); // Downward
78
+
79
+ const hitResults = this.world.getPhysicsEngine()?.performHitTest({
80
+ origin: rayOrigin,
81
+ direction: rayDirection,
82
+ maxDistance: 100, // Cast ray down 100 units
83
+ stopOnFirstHit: true,
84
+ });
85
+
86
+ if (hitResults && hitResults.length > 0) {
87
+ // Return the Y coordinate of the hit location (top surface of ground)
88
+ return hitResults[0].hitLocation.y;
89
+ }
90
+
91
+ return null;
92
+ }
93
+
94
+ private createLevel(): void {
95
+ // Create the level generator
96
+ this.levelGenerator = new SideScrollerLevelGenerator(this.world, {
97
+ enableDebugVisualization: false,
98
+ createGroundMesh: true
99
+ });
100
+
101
+ // Generate initial level chunks
102
+ this.levelGenerator.updateLevel(0);
103
+ }
104
+
105
+ public override destroy(): void {
106
+ // Clean up level generator
107
+ if (this.levelGenerator) {
108
+ this.levelGenerator.cleanup();
109
+ this.levelGenerator = null;
110
+ }
111
+ super.destroy();
112
+ }
113
+ }
114
+
115
+ export function main(container: HTMLElement, gameId: string): ENGINE.IGameLoop {
116
+ const game = new SideScrollerGame(container, {
117
+ ...ENGINE.BaseGameLoop.DEFAULT_OPTIONS,
118
+ gameId
119
+ });
120
+ return game;
121
+ }
@@ -0,0 +1,361 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+
4
+ import { COLORS, LEVEL_CONFIG } from './const.js';
5
+
6
+ // ============================================================================
7
+ // LEVEL GENERATOR
8
+ // ============================================================================
9
+
10
+ // Interface for level generator configuration options
11
+ interface LevelGeneratorOptions {
12
+ chunkSize?: number;
13
+ chunksAhead?: number;
14
+ chunksBehind?: number;
15
+ platformHeightRange?: { min: number; max: number };
16
+ platformWidthRange?: { min: number; max: number };
17
+ obstacleHeightRange?: { min: number; max: number };
18
+ chunkMargin?: number;
19
+ minSpacing?: number;
20
+ enableDebugVisualization?: boolean;
21
+ createGroundMesh?: boolean;
22
+ }
23
+
24
+ // Interface for tracking level chunks
25
+ interface LevelChunk {
26
+ startX: number;
27
+ endX: number;
28
+ actors: ENGINE.Actor[];
29
+ }
30
+
31
+ // Interface for positioned elements within chunks
32
+ interface PositionedElement {
33
+ x: number;
34
+ width: number;
35
+ height: number;
36
+ type: 'platform' | 'obstacle';
37
+ }
38
+
39
+ // Simple level generator for infinite side-scrolling levels
40
+ export class SideScrollerLevelGenerator {
41
+ private world: ENGINE.World;
42
+ // Map of chunk index to chunk
43
+ private levelChunks: Map<number, LevelChunk> = new Map();
44
+
45
+ // Configuration
46
+ private chunkSize: number;
47
+ private chunksAhead: number;
48
+ private chunksBehind: number;
49
+ private platformHeightRange: { min: number; max: number };
50
+ private platformWidthRange: { min: number; max: number };
51
+ private obstacleHeightRange: { min: number; max: number };
52
+ private chunkMargin: number;
53
+ private minSpacing: number;
54
+ private debugVisualizationEnabled: boolean = false;
55
+ private createGroundMesh: boolean = false;
56
+
57
+ constructor(world: ENGINE.World, options: LevelGeneratorOptions = {}) {
58
+ this.world = world;
59
+
60
+ // Apply defaults
61
+ this.chunkSize = options.chunkSize ?? LEVEL_CONFIG.CHUNK_SIZE;
62
+ this.chunksAhead = options.chunksAhead ?? LEVEL_CONFIG.CHUNKS_AHEAD;
63
+ this.chunksBehind = options.chunksBehind ?? LEVEL_CONFIG.CHUNKS_BEHIND;
64
+ this.platformHeightRange = options.platformHeightRange ?? LEVEL_CONFIG.PLATFORM_HEIGHT_RANGE;
65
+ this.platformWidthRange = options.platformWidthRange ?? LEVEL_CONFIG.PLATFORM_WIDTH_RANGE;
66
+ this.obstacleHeightRange = options.obstacleHeightRange ?? LEVEL_CONFIG.OBSTACLE_HEIGHT_RANGE;
67
+ this.chunkMargin = options.chunkMargin ?? LEVEL_CONFIG.CHUNK_MARGIN;
68
+ this.minSpacing = options.minSpacing ?? LEVEL_CONFIG.MIN_SPACING;
69
+ this.debugVisualizationEnabled = options.enableDebugVisualization ?? false;
70
+ this.createGroundMesh = options.createGroundMesh ?? false;
71
+ }
72
+
73
+ public updateLevel(playerX: number): void {
74
+ const playerChunkIndex = this.getChunkIndex(playerX);
75
+
76
+ const validChunkIndices = [];
77
+ for (let i = playerChunkIndex - this.chunksBehind; i <= playerChunkIndex + this.chunksAhead; i++) {
78
+ this.generateChunk(i);
79
+ validChunkIndices.push(i);
80
+ }
81
+
82
+ for (const index of this.levelChunks.keys()) {
83
+ if (!validChunkIndices.includes(index)) {
84
+ this.removeChunk(index);
85
+ }
86
+ }
87
+ }
88
+
89
+ private getChunkIndex(x: number): number {
90
+ return Math.floor(x / this.chunkSize);
91
+ }
92
+
93
+ private removeChunk(index: number): void {
94
+ const chunk = this.levelChunks.get(index);
95
+ if (chunk) {
96
+ this.world.removeActors(...chunk.actors);
97
+ this.levelChunks.delete(index);
98
+ console.log(`[LevelGenerator] Removed chunk at index ${index}`);
99
+ }
100
+ }
101
+
102
+ private generateChunk(index: number): void {
103
+ if (this.levelChunks.has(index)) {
104
+ return;
105
+ }
106
+
107
+ const startX = index * this.chunkSize;
108
+ const endX = startX + this.chunkSize;
109
+ const actors: ENGINE.Actor[] = [];
110
+
111
+ // Generate positioned elements for this chunk
112
+ const elements = this.generatePositionedElements(startX);
113
+
114
+ // Create actors for each element
115
+ for (const element of elements) {
116
+ const actor = this.createElementActor(element);
117
+ actors.push(actor);
118
+ }
119
+
120
+ // Add ground mesh if enabled
121
+ if (this.createGroundMesh) {
122
+ const groundActor = this.createGroundMeshActor(startX);
123
+ actors.push(groundActor);
124
+ }
125
+
126
+ // Add debug visualization if enabled
127
+ if (this.debugVisualizationEnabled) {
128
+ const debugActor = this.createChunkDebugVisualization(startX);
129
+ actors.push(debugActor);
130
+ }
131
+
132
+ // Add all actors to the world
133
+ this.world.addActors(...actors);
134
+
135
+ // Track this chunk
136
+ const chunk: LevelChunk = {
137
+ startX,
138
+ endX,
139
+ actors,
140
+ };
141
+ console.log(`[LevelGenerator] Generated chunk at index ${index}`);
142
+ this.levelChunks.set(index, chunk);
143
+ }
144
+
145
+ private generatePositionedElements(startX: number): PositionedElement[] {
146
+ const elements: PositionedElement[] = [];
147
+ const usableWidth = this.chunkSize - (this.chunkMargin * 2); // Leave margin on each side
148
+ const usableStartX = startX + this.chunkMargin;
149
+
150
+ // Simple alternating generation: platform -> obstacle -> platform -> obstacle...
151
+ const totalElements = Math.floor(usableWidth / this.minSpacing); // How many elements can fit
152
+
153
+ let currentX = usableStartX;
154
+ let isPlatform = true; // Start with platform
155
+
156
+ for (let i = 0; i < totalElements && currentX < (startX + this.chunkSize - this.chunkMargin); i++) {
157
+ if (isPlatform) {
158
+ // Generate platform with random dimensions
159
+ const width = this.platformWidthRange.min +
160
+ Math.random() * (this.platformWidthRange.max - this.platformWidthRange.min);
161
+ const height = this.platformHeightRange.min +
162
+ Math.random() * (this.platformHeightRange.max - this.platformHeightRange.min);
163
+
164
+ elements.push({
165
+ x: currentX + width / 2, // Center position
166
+ width: width,
167
+ height: height,
168
+ type: 'platform'
169
+ });
170
+
171
+ currentX += width + this.minSpacing; // Move past this platform plus spacing
172
+
173
+ } else {
174
+ // Generate obstacle with random dimensions
175
+ const width = LEVEL_CONFIG.OBSTACLE_WIDTH_RANGE.min +
176
+ Math.random() * (LEVEL_CONFIG.OBSTACLE_WIDTH_RANGE.max - LEVEL_CONFIG.OBSTACLE_WIDTH_RANGE.min);
177
+ const height = this.obstacleHeightRange.min +
178
+ Math.random() * (this.obstacleHeightRange.max - this.obstacleHeightRange.min);
179
+
180
+ elements.push({
181
+ x: currentX + width / 2, // Center position
182
+ width: width,
183
+ height: height,
184
+ type: 'obstacle'
185
+ });
186
+
187
+ currentX += width + this.minSpacing; // Move past this obstacle plus spacing
188
+ }
189
+
190
+ // Alternate between platform and obstacle
191
+ isPlatform = !isPlatform;
192
+ }
193
+
194
+ return elements;
195
+ }
196
+
197
+ private createElementActor(element: PositionedElement): ENGINE.Actor {
198
+ const geometry = new THREE.BoxGeometry(element.width, element.height, LEVEL_CONFIG.DEPTH);
199
+ const material = new THREE.MeshStandardMaterial({ color: element.type === 'platform' ? COLORS.PLATFORM : COLORS.OBSTACLE });
200
+
201
+ const meshComponent = ENGINE.MeshComponent.create({
202
+ geometry: geometry,
203
+ material: material,
204
+ position: new THREE.Vector3(element.x, element.height / 2, 0), // Position at ground level
205
+ physicsOptions: {
206
+ enabled: true,
207
+ motionType: ENGINE.PhysicsMotionType.Static,
208
+ collisionProfile: ENGINE.DefaultCollisionProfile.BlockAll,
209
+ },
210
+ });
211
+
212
+ // Enable shadow casting and receiving
213
+ meshComponent.castShadow = true;
214
+ meshComponent.receiveShadow = true;
215
+
216
+ const actor = ENGINE.Actor.create();
217
+ actor.setRootComponent(meshComponent, false);
218
+ return actor;
219
+ }
220
+
221
+ private createChunkDebugVisualization(startX: number): ENGINE.Actor {
222
+ // Create a wireframe box to visualize chunk boundaries
223
+ const chunkGeometry = new THREE.BoxGeometry(
224
+ this.chunkSize,
225
+ LEVEL_CONFIG.DEBUG_VISUALIZATION_HEIGHT,
226
+ LEVEL_CONFIG.DEPTH
227
+ );
228
+
229
+ const chunkMaterial = new THREE.MeshBasicMaterial({
230
+ color: LEVEL_CONFIG.DEBUG_VISUALIZATION_COLOR,
231
+ opacity: LEVEL_CONFIG.DEBUG_VISUALIZATION_OPACITY,
232
+ wireframe: true,
233
+ transparent: true,
234
+ });
235
+
236
+ const meshComponent = ENGINE.MeshComponent.create({
237
+ geometry: chunkGeometry,
238
+ material: chunkMaterial,
239
+ position: new THREE.Vector3(
240
+ startX + this.chunkSize / 2,
241
+ LEVEL_CONFIG.DEBUG_VISUALIZATION_HEIGHT / 2,
242
+ 0
243
+ ),
244
+ physicsOptions: {
245
+ enabled: false,
246
+ },
247
+ });
248
+
249
+ const chunkBoundary = ENGINE.Actor.create();
250
+ chunkBoundary.setRootComponent(meshComponent, false);
251
+ return chunkBoundary;
252
+ }
253
+
254
+ private createGroundMeshActor(startX: number): ENGINE.Actor {
255
+ // Create a ground mesh that spans the entire chunk width
256
+ const groundGeometry = new THREE.BoxGeometry(
257
+ this.chunkSize,
258
+ LEVEL_CONFIG.GROUND_HEIGHT,
259
+ LEVEL_CONFIG.DEPTH
260
+ );
261
+
262
+ const groundMaterial = new THREE.MeshStandardMaterial({
263
+ color: COLORS.GROUND,
264
+ });
265
+
266
+ const groundMeshComponent = ENGINE.MeshComponent.create({
267
+ geometry: groundGeometry,
268
+ material: groundMaterial,
269
+ position: new THREE.Vector3(
270
+ startX + this.chunkSize / 2,
271
+ LEVEL_CONFIG.GROUND_Y_POSITION,
272
+ 0
273
+ ),
274
+ physicsOptions: {
275
+ enabled: true,
276
+ motionType: ENGINE.PhysicsMotionType.Static,
277
+ collisionProfile: ENGINE.DefaultCollisionProfile.BlockAll,
278
+ },
279
+ });
280
+
281
+ // Enable shadow receiving
282
+ groundMeshComponent.receiveShadow = true;
283
+
284
+ const actor = ENGINE.Actor.create();
285
+ actor.setRootComponent(groundMeshComponent, false);
286
+ return actor;
287
+ }
288
+
289
+ /**
290
+ * Gets detailed geometry information at a given x location.
291
+ * @param x - The x coordinate to query
292
+ * @returns An array of geometry info objects at the given x location
293
+ */
294
+ public getGeometryInfoAtX(x: number): Array<{
295
+ type: 'ground' | 'platform' | 'obstacle';
296
+ topY: number;
297
+ bottomY: number;
298
+ width: number;
299
+ centerX: number;
300
+ }> {
301
+ const chunkIndex = this.getChunkIndex(x);
302
+ const chunk = this.levelChunks.get(chunkIndex);
303
+
304
+ if (!chunk) {
305
+ return [];
306
+ }
307
+
308
+ const geometries: Array<{
309
+ type: 'ground' | 'platform' | 'obstacle';
310
+ topY: number;
311
+ bottomY: number;
312
+ width: number;
313
+ centerX: number;
314
+ }> = [];
315
+
316
+ // Add ground mesh if enabled
317
+ if (this.createGroundMesh) {
318
+ const groundCenterY = LEVEL_CONFIG.GROUND_Y_POSITION;
319
+ const groundHalfHeight = LEVEL_CONFIG.GROUND_HEIGHT / 2;
320
+ geometries.push({
321
+ type: 'ground',
322
+ topY: groundCenterY + groundHalfHeight,
323
+ bottomY: groundCenterY - groundHalfHeight,
324
+ width: this.chunkSize,
325
+ centerX: chunk.startX + this.chunkSize / 2
326
+ });
327
+ }
328
+
329
+ // Add positioned elements (platforms and obstacles)
330
+ const elements = this.generatePositionedElements(chunk.startX);
331
+
332
+ for (const element of elements) {
333
+ const elementLeft = element.x - element.width / 2;
334
+ const elementRight = element.x + element.width / 2;
335
+
336
+ // Check if the x coordinate falls within this element's bounds
337
+ if (x >= elementLeft && x <= elementRight) {
338
+ geometries.push({
339
+ type: element.type,
340
+ topY: element.height,
341
+ bottomY: 0, // Elements are positioned at ground level (y=0 bottom)
342
+ width: element.width,
343
+ centerX: element.x
344
+ });
345
+ }
346
+ }
347
+
348
+ // Sort by top height (highest first)
349
+ geometries.sort((a, b) => b.topY - a.topY);
350
+
351
+ return geometries;
352
+ }
353
+
354
+ public cleanup(): void {
355
+ for (const index of this.levelChunks.keys()) {
356
+ this.removeChunk(index);
357
+ }
358
+ this.levelChunks.clear();
359
+ console.log('[LevelGenerator] Cleaned up');
360
+ }
361
+ }
@@ -0,0 +1,123 @@
1
+ import * as ENGINE from '@gnsx/genesys.js';
2
+ import * as THREE from 'three';
3
+
4
+ import { CAMERA_SETTINGS, PLAYER_MOVEMENT } from './const.js';
5
+
6
+ // ============================================================================
7
+ // PLAYER PAWN
8
+ // ============================================================================
9
+
10
+ // The side-scroller player pawn class using animated character.
11
+ @ENGINE.GameClass()
12
+ export class SideScrollerPlayer extends ENGINE.ThirdPersonCharacterPawn {
13
+ camera: THREE.PerspectiveCamera;
14
+ characterMesh: ENGINE.GLTFMeshComponent | null = null;
15
+ smoothedCameraHeight: number | null = null;
16
+ smoothedLookAtHeight: number | null = null;
17
+
18
+ constructor() {
19
+ super();
20
+ // Create directional movement component for side-scrolling
21
+ const movementComponent = ENGINE.DirectionalCharacterMovementComponent.create({
22
+ direction: ENGINE.CharacterMovementDirection.LeftRight, // Move left and right
23
+ autoMove: false, // Player controls movement manually
24
+ jumpSpeed: PLAYER_MOVEMENT.JUMP_SPEED,
25
+ maxSpeed: PLAYER_MOVEMENT.MAX_SPEED,
26
+ maxMidAirJumps: PLAYER_MOVEMENT.MAX_MID_AIR_JUMPS, // Allow double jump
27
+ // Physics and movement settings for platformer feel
28
+ accelerationLambda: PLAYER_MOVEMENT.ACCELERATION_LAMBDA,
29
+ decelerationLambda: PLAYER_MOVEMENT.DECELERATION_LAMBDA,
30
+ midAirAccelerationLambda: PLAYER_MOVEMENT.MID_AIR_ACCELERATION_LAMBDA,
31
+ midAirDecelerationLambda: PLAYER_MOVEMENT.MID_AIR_DECELERATION_LAMBDA,
32
+ });
33
+
34
+ // Physics options for the player
35
+ const physicsOptions: ENGINE.ComponentPhysicsOptions = {
36
+ enabled: true,
37
+ motionType: ENGINE.PhysicsMotionType.KinematicVelocityBased,
38
+ collisionProfile: ENGINE.DefaultCollisionProfile.Character,
39
+ generateCollisionEvents: true,
40
+ };
41
+
42
+ // Create root component with physics (invisible capsule for collision)
43
+ const rootComponent = ENGINE.MeshComponent.create({
44
+ geometry: new THREE.CapsuleGeometry(
45
+ ENGINE.CHARACTER_WIDTH / 2,
46
+ ENGINE.CHARACTER_HEIGHT - ENGINE.CHARACTER_WIDTH
47
+ ),
48
+ material: new THREE.MeshStandardMaterial({
49
+ color: 0xffff00,
50
+ visible: false, // Make invisible since we'll have the animated character mesh
51
+ transparent: true,
52
+ opacity: 0.5
53
+ }),
54
+ physicsOptions,
55
+ });
56
+
57
+ this.setRootComponent(rootComponent, true);
58
+ this.movementComponent = movementComponent;
59
+
60
+ // Use default character model from engine assets
61
+ this.configUrl = '@engine/assets/character/config/mannequin-anim.json';
62
+
63
+ this.meshComponent.position.copy(new THREE.Vector3(0, -ENGINE.CHARACTER_HEIGHT / 2, 0));
64
+ this.meshComponent.rotation.copy(new THREE.Euler(0, Math.PI, 0)); // Face right initially
65
+ rootComponent.add(this.meshComponent);
66
+
67
+ // Enable shadow casting for the character
68
+ this.rootComponent.castShadow = true;
69
+ this.camera = new THREE.PerspectiveCamera(CAMERA_SETTINGS.FOV, 1, CAMERA_SETTINGS.NEAR, CAMERA_SETTINGS.FAR);
70
+
71
+ this.enableDirectionalLightFollowing = true;
72
+ }
73
+
74
+ public override doBeginPlay(): void {
75
+ super.doBeginPlay();
76
+ this.getWorld()?.setOverrideCamera(this.camera);
77
+ }
78
+
79
+ public override tickPrePhysics(deltaTime: number): void {
80
+ super.tickPrePhysics(deltaTime);
81
+
82
+ // manually update the camera position
83
+ const desiredCameraPosition = this.rootComponent.localToWorld(new THREE.Vector3(
84
+ CAMERA_SETTINGS.POSITION.x,
85
+ CAMERA_SETTINGS.POSITION.y,
86
+ CAMERA_SETTINGS.POSITION.z
87
+ ));
88
+ const desiredCameraLookAt = this.rootComponent.localToWorld(new THREE.Vector3(
89
+ CAMERA_SETTINGS.LOOK_AT.x,
90
+ CAMERA_SETTINGS.LOOK_AT.y,
91
+ CAMERA_SETTINGS.LOOK_AT.z
92
+ ));
93
+ this.smoothedCameraHeight ??= desiredCameraPosition.y;
94
+ this.smoothedLookAtHeight ??= desiredCameraLookAt.y;
95
+
96
+ this.smoothedCameraHeight = THREE.MathUtils.damp(this.smoothedCameraHeight, desiredCameraPosition.y, CAMERA_SETTINGS.DAMP_FACTOR, deltaTime);
97
+ this.smoothedLookAtHeight = THREE.MathUtils.damp(this.smoothedLookAtHeight, desiredCameraLookAt.y, CAMERA_SETTINGS.DAMP_FACTOR, deltaTime);
98
+
99
+ this.camera.position.x = desiredCameraPosition.x;
100
+ this.camera.position.y = this.smoothedCameraHeight;
101
+ this.camera.position.z = desiredCameraPosition.z;
102
+
103
+ this.camera.lookAt(new THREE.Vector3(
104
+ desiredCameraLookAt.x,
105
+ this.smoothedLookAtHeight,
106
+ desiredCameraLookAt.z
107
+ ));
108
+
109
+ // change the direction of the character mesh based on the movement direction
110
+ this.characterMesh ??= this.getComponent(ENGINE.GLTFMeshComponent);
111
+ if (this.characterMesh && this.movementComponent) {
112
+ const movementComponent = this.movementComponent as ENGINE.DirectionalCharacterMovementComponent;
113
+ const velocity = movementComponent.getVelocities();
114
+ if (velocity.right > 0.1) {
115
+ // Moving right - face right (90 degrees)
116
+ this.characterMesh.rotation.set(0, Math.PI / 2, 0);
117
+ } else if (velocity.right < -0.1) {
118
+ // Moving left - face left (-90 degrees)
119
+ this.characterMesh.rotation.set(0, -Math.PI / 2, 0);
120
+ }
121
+ }
122
+ }
123
+ }