@directivegames/genesys.sdk 4.0.2 → 4.1.0

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 (45) hide show
  1. package/dist/src/core/tools/new-project.js +1 -0
  2. package/dist/src/dependencies.js +1 -1
  3. package/dist/src/templates/scripts/genesys/migrate-scenes-and-prefabs.js +179 -0
  4. package/dist/src/templates/scripts/genesys/post-install.js +10 -2
  5. package/dist/src/templates/src/templates/firstPerson/src/game.js +1 -1
  6. package/dist/src/templates/src/templates/firstPerson/src/player.js +8 -13
  7. package/dist/src/templates/src/templates/fps/src/game.js +1 -1
  8. package/dist/src/templates/src/templates/fps/src/player.js +8 -12
  9. package/dist/src/templates/src/templates/fps/src/weapon.js +30 -38
  10. package/dist/src/templates/src/templates/freeCamera/src/game.js +1 -1
  11. package/dist/src/templates/src/templates/freeCamera/src/player.js +6 -11
  12. package/dist/src/templates/src/templates/sideScroller/src/game.js +2 -3
  13. package/dist/src/templates/src/templates/sideScroller/src/level-generator.js +17 -17
  14. package/dist/src/templates/src/templates/sideScroller/src/player.js +12 -17
  15. package/dist/src/templates/src/templates/thirdPerson/src/game.js +1 -1
  16. package/dist/src/templates/src/templates/thirdPerson/src/player.js +13 -18
  17. package/dist/src/templates/src/templates/vehicle/src/base-vehicle.js +1 -1
  18. package/dist/src/templates/src/templates/vehicle/src/game.js +2 -2
  19. package/dist/src/templates/src/templates/vehicle/src/mesh-vehicle.js +8 -9
  20. package/dist/src/templates/src/templates/vehicle/src/player.js +13 -18
  21. package/dist/src/templates/src/templates/vehicle/src/primitive-vehicle.js +11 -12
  22. package/dist/src/templates/src/templates/vehicle/src/ui-hints.js +2 -2
  23. package/dist/src/templates/src/templates/vr-game/src/game.js +5 -5
  24. package/package.json +2 -2
  25. package/src/templates/scripts/genesys/migrate-scenes-and-prefabs.ts +212 -0
  26. package/src/templates/scripts/genesys/post-install.ts +11 -2
  27. package/src/templates/src/templates/firstPerson/src/game.ts +1 -1
  28. package/src/templates/src/templates/firstPerson/src/player.ts +10 -14
  29. package/src/templates/src/templates/fps/src/game.ts +1 -1
  30. package/src/templates/src/templates/fps/src/player.ts +10 -13
  31. package/src/templates/src/templates/fps/src/weapon.ts +31 -38
  32. package/src/templates/src/templates/freeCamera/src/game.ts +1 -1
  33. package/src/templates/src/templates/freeCamera/src/player.ts +7 -12
  34. package/src/templates/src/templates/sideScroller/src/game.ts +2 -3
  35. package/src/templates/src/templates/sideScroller/src/level-generator.ts +21 -21
  36. package/src/templates/src/templates/sideScroller/src/player.ts +15 -17
  37. package/src/templates/src/templates/thirdPerson/src/game.ts +1 -1
  38. package/src/templates/src/templates/thirdPerson/src/player.ts +16 -19
  39. package/src/templates/src/templates/vehicle/src/base-vehicle.ts +1 -1
  40. package/src/templates/src/templates/vehicle/src/game.ts +2 -2
  41. package/src/templates/src/templates/vehicle/src/mesh-vehicle.ts +8 -10
  42. package/src/templates/src/templates/vehicle/src/player.ts +16 -19
  43. package/src/templates/src/templates/vehicle/src/primitive-vehicle.ts +11 -13
  44. package/src/templates/src/templates/vehicle/src/ui-hints.ts +2 -2
  45. package/src/templates/src/templates/vr-game/src/game.ts +5 -5
@@ -27,6 +27,7 @@ export const projectFiles = {
27
27
  'remove-engine-comments': 'pnpm exec tsx ./scripts/genesys/remove-engine-comments.ts',
28
28
  'dev': 'pnpm exec tsx ./scripts/genesys/dev/generate-manifest.ts && vite',
29
29
  'validate-prefabs': 'pnpm exec tsx ./scripts/genesys/validate-prefabs.ts',
30
+ 'migrate-scenes-and-prefabs': 'pnpm exec tsx ./scripts/genesys/migrate-scenes-and-prefabs.ts'
30
31
  },
31
32
  keywords: [],
32
33
  type: 'module',
@@ -9,7 +9,7 @@
9
9
  * Includes dependencies from the main package.
10
10
  */
11
11
  export const DEPENDENCIES = {
12
- '@directivegames/genesys.js': '4.0.2',
12
+ '@directivegames/genesys.js': '4.1.0',
13
13
  '@electron/rebuild': '4.0.1',
14
14
  '@emotion/react': '11.14.0',
15
15
  '@emotion/styled': '11.14.1',
@@ -0,0 +1,179 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import * as ENGINE from '@directivegames/genesys.js';
4
+ import { getProjectRoot } from './common.js';
5
+ import { mockBrowserEnvironment } from './mock.js';
6
+ import { StorageProvider } from './storageProvider.js';
7
+ // Import game module to register game classes (tsx compiles TypeScript on the fly)
8
+ import '../../src/game.js';
9
+ // Set up browser environment for Node.js (provides document, window, etc.)
10
+ mockBrowserEnvironment();
11
+ // Set up storage provider for file operations
12
+ const storageProvider = new StorageProvider();
13
+ ENGINE.projectContext({ project: 'local-project', storageProvider: storageProvider });
14
+ const defaultWorldOptions = {
15
+ rendererDomElement: document.createElement('div'),
16
+ gameContainer: document.createElement('div'),
17
+ backgroundColor: 0x2E2E2E,
18
+ physicsOptions: {
19
+ engine: ENGINE.PhysicsEngine.Rapier,
20
+ gravity: ENGINE.MathHelpers.makeVector({ up: -9.81 }),
21
+ },
22
+ navigationOptions: {
23
+ engine: ENGINE.NavigationEngine.RecastNavigation,
24
+ },
25
+ useManifold: true
26
+ };
27
+ function findScenesAndPrefabs(dir, files = []) {
28
+ const entries = fs.readdirSync(dir);
29
+ for (const entry of entries) {
30
+ const fullPath = path.join(dir, entry);
31
+ const stat = fs.statSync(fullPath);
32
+ if (stat.isDirectory()) {
33
+ // Skip common directories that shouldn't be processed
34
+ if (entry === 'node_modules' || entry === 'dist' || entry === '.git' || entry === '.engine') {
35
+ continue;
36
+ }
37
+ findScenesAndPrefabs(fullPath, files);
38
+ }
39
+ else if (entry.endsWith('.genesys-scene')) {
40
+ files.push({ path: fullPath, type: 'scene' });
41
+ }
42
+ else if (entry.endsWith('.prefab.json')) {
43
+ files.push({ path: fullPath, type: 'prefab' });
44
+ }
45
+ }
46
+ return files;
47
+ }
48
+ async function migrateSceneFile(filePath, relativePath) {
49
+ // Read the file content
50
+ const content = fs.readFileSync(filePath, 'utf-8');
51
+ const data = JSON.parse(content);
52
+ // Create a world and load into it
53
+ const world = new ENGINE.World(defaultWorldOptions);
54
+ if (ENGINE.isLegacyData(data)) {
55
+ // Use WorldSerializer for legacy data
56
+ await ENGINE.WorldSerializer.loadWorld(world, data);
57
+ }
58
+ else {
59
+ // Use Loader for new format
60
+ const loader = new ENGINE.Loader();
61
+ await loader.loadToInstanceAsync(data, world);
62
+ }
63
+ // Dump the world
64
+ const dumper = new ENGINE.Dumper();
65
+ const newData = dumper.dump(world);
66
+ // Write back to file
67
+ const newContent = JSON.stringify(newData, null, 2);
68
+ fs.writeFileSync(filePath, newContent, 'utf-8');
69
+ console.log(`✅ ${relativePath}`);
70
+ return true;
71
+ }
72
+ async function migratePrefabFile(filePath, relativePath) {
73
+ // Read the file content
74
+ const content = fs.readFileSync(filePath, 'utf-8');
75
+ const data = JSON.parse(content);
76
+ let instance;
77
+ if (ENGINE.isLegacyData(data)) {
78
+ // Use WorldSerializer for legacy data
79
+ console.log(`🔍 Migrating legacy prefab: ${relativePath}`);
80
+ instance = await ENGINE.WorldSerializer.loadActor(data);
81
+ console.log(`✅ ${relativePath}`);
82
+ }
83
+ else {
84
+ // Use Loader for new format
85
+ const loader = new ENGINE.Loader();
86
+ instance = await loader.loadAsync(data);
87
+ }
88
+ if (!instance) {
89
+ console.log(`⚠️ ${relativePath}: Loaded instance is null, skipping`);
90
+ return false;
91
+ }
92
+ // Dump using Dumper
93
+ const dumper = new ENGINE.Dumper();
94
+ const newData = dumper.dump(instance);
95
+ // Write back to file
96
+ const newContent = JSON.stringify(newData, null, 2);
97
+ fs.writeFileSync(filePath, newContent, 'utf-8');
98
+ console.log(`✅ ${relativePath}`);
99
+ return true;
100
+ }
101
+ async function migrateFile(fileInfo) {
102
+ const { path: filePath, type } = fileInfo;
103
+ const relativePath = path.relative(getProjectRoot(), filePath);
104
+ try {
105
+ if (type === 'scene') {
106
+ return await migrateSceneFile(filePath, relativePath);
107
+ }
108
+ else {
109
+ return await migratePrefabFile(filePath, relativePath);
110
+ }
111
+ }
112
+ catch (error) {
113
+ console.error(`❌ ${relativePath}: ${error instanceof Error ? error.message : String(error)}`);
114
+ return false;
115
+ }
116
+ finally {
117
+ console.log('');
118
+ }
119
+ }
120
+ async function main() {
121
+ // Game classes are registered via the top-level import of dist/src/game.js
122
+ // Make sure to run `pnpm build` first!
123
+ const projectRoot = getProjectRoot();
124
+ const assetsFolder = path.join(projectRoot, 'assets');
125
+ console.log(`📁 Scanning for scene and prefab files in: ${assetsFolder}\n`);
126
+ // Find all scene and prefab files in the assets folder
127
+ const files = findScenesAndPrefabs(assetsFolder);
128
+ if (files.length === 0) {
129
+ console.log('⚠️ No scene or prefab files found.');
130
+ return;
131
+ }
132
+ const prefabFiles = files.filter(f => f.type === 'prefab');
133
+ const sceneFiles = files.filter(f => f.type === 'scene');
134
+ console.log(`Found ${files.length} files to migrate:`);
135
+ console.log(` - ${prefabFiles.length} prefab file(s)`);
136
+ console.log(` - ${sceneFiles.length} scene file(s)\n`);
137
+ let successCount = 0;
138
+ let failCount = 0;
139
+ // Migrate prefabs first
140
+ if (prefabFiles.length > 0) {
141
+ console.log('📦 Migrating prefabs...\n');
142
+ for (const fileInfo of prefabFiles) {
143
+ const success = await migrateFile(fileInfo);
144
+ if (success) {
145
+ successCount++;
146
+ }
147
+ else {
148
+ failCount++;
149
+ }
150
+ }
151
+ }
152
+ // Then migrate scenes
153
+ if (sceneFiles.length > 0) {
154
+ console.log('\n🌍 Migrating scenes...\n');
155
+ for (const fileInfo of sceneFiles) {
156
+ const success = await migrateFile(fileInfo);
157
+ if (success) {
158
+ successCount++;
159
+ }
160
+ else {
161
+ failCount++;
162
+ }
163
+ }
164
+ }
165
+ console.log(`\n${'='.repeat(60)}`);
166
+ console.log('Migration Summary:');
167
+ console.log(` Total files: ${files.length}`);
168
+ console.log(` Successful: ${successCount}`);
169
+ console.log(` Failed: ${failCount}`);
170
+ console.log(`${'='.repeat(60)}`);
171
+ if (failCount > 0) {
172
+ process.exit(1);
173
+ }
174
+ }
175
+ main().catch((error) => {
176
+ console.error('Unexpected error:', error);
177
+ console.error('Stack trace:', error.stack);
178
+ process.exit(1);
179
+ });
@@ -13,13 +13,21 @@ async function main() {
13
13
  fs.mkdirSync(copiedEngineFolder, { recursive: true });
14
14
  const foldersToCopy = [
15
15
  'games/examples',
16
- 'src',
17
- 'docs',
16
+ 'src'
18
17
  ];
19
18
  for (const folder of foldersToCopy) {
20
19
  const engineFolderPath = path.join(engineInstallFolder, folder);
21
20
  const localFolderPath = path.join(copiedEngineFolder, folder);
22
21
  fs.cpSync(engineFolderPath, localFolderPath, { recursive: true });
23
22
  }
23
+ // copy all *.md files
24
+ const files = fs.readdirSync(engineInstallFolder);
25
+ for (const file of files) {
26
+ if (file.endsWith('.md')) {
27
+ const engineFilePath = path.join(engineInstallFolder, file);
28
+ const localFilePath = path.join(copiedEngineFolder, file);
29
+ fs.copyFileSync(engineFilePath, localFilePath);
30
+ }
31
+ }
24
32
  }
25
33
  main();
@@ -13,7 +13,7 @@ class FirstPersonGame extends ENGINE.BaseGameLoop {
13
13
  // default spawn location
14
14
  const position = new THREE.Vector3(0, ENGINE.CHARACTER_HEIGHT / 2, 0);
15
15
  // now create the pawn
16
- this.pawn = new FirstPersonPlayer({ position });
16
+ this.pawn = FirstPersonPlayer.create({ position });
17
17
  // create the controller and possess the pawn
18
18
  this.controller = new ENGINE.PlayerController();
19
19
  this.controller.possess(this.pawn);
@@ -17,13 +17,14 @@ import * as THREE from 'three';
17
17
  */
18
18
  let FirstPersonPlayer = class FirstPersonPlayer extends ENGINE.Pawn {
19
19
  // No need to provide rootComponent, movementComponent and camera, they are created internally
20
- constructor(options) {
20
+ constructor() {
21
+ super();
21
22
  // simple camera component - contains a perspective camera by default
22
23
  const camera = new THREE.PerspectiveCamera(ENGINE.CAMERA_FOV, 1, ENGINE.CAMERA_NEAR, ENGINE.CAMERA_FAR);
23
24
  // set proper camera height for first person view
24
25
  camera.position.set(0, ENGINE.CHARACTER_HEIGHT * 0.4, 0);
25
26
  // use a simple capsule geometry as the root component
26
- const rootComponent = new ENGINE.MeshComponent({
27
+ const rootComponent = ENGINE.MeshComponent.create({
27
28
  geometry: ENGINE.GameBuilder.createDefaultPawnCapsuleGeometry(),
28
29
  material: new THREE.MeshStandardMaterial({ color: ENGINE.Color.YELLOW }),
29
30
  physicsOptions: {
@@ -36,20 +37,14 @@ let FirstPersonPlayer = class FirstPersonPlayer extends ENGINE.Pawn {
36
37
  });
37
38
  // hide the mesh in first person view
38
39
  rootComponent.getMesh().visible = false;
40
+ this.setRootComponent(rootComponent, true);
41
+ this.rootComponent.add(camera);
39
42
  // create the movement component
40
- const movementComponent = new ENGINE.CharacterMovementComponent({
41
- ...ENGINE.CharacterMovementComponent.DEFAULT_OPTIONS,
43
+ const movementComponent = ENGINE.CharacterMovementComponent.create({
42
44
  movementType: ENGINE.CharacterMovementType.FirstPerson,
43
45
  });
44
- // construct the pawn
45
- super({
46
- ...options,
47
- rootComponent,
48
- movementComponent,
49
- // make sure the directional light follows the player for consistent shadows
50
- enableDirectionalLightFollowing: true,
51
- camera,
52
- });
46
+ this.movementComponent = movementComponent;
47
+ this.enableDirectionalLightFollowing = true;
53
48
  // set the pawn to be transient so it's never saved in the level
54
49
  this.setTransient(true);
55
50
  }
@@ -13,7 +13,7 @@ class MyGame extends ENGINE.BaseGameLoop {
13
13
  // default spawn location
14
14
  const position = new THREE.Vector3(0, ENGINE.CHARACTER_HEIGHT / 2, 0);
15
15
  // now create the pawn
16
- this.pawn = new FPSPlayer({ position });
16
+ this.pawn = FPSPlayer.create({ position });
17
17
  // create the controller and possess the pawn
18
18
  this.controller = new ENGINE.PlayerController();
19
19
  this.controller.possess(this.pawn);
@@ -19,7 +19,8 @@ import { DefaultWeapon } from './weapon.js';
19
19
  */
20
20
  let FPSPlayer = class FPSPlayer extends ENGINE.Pawn {
21
21
  // No need to provide rootComponent, movementComponent and camera, they are created internally
22
- constructor(options) {
22
+ constructor() {
23
+ super();
23
24
  // simple camera component - contains a perspective camera by default
24
25
  const camera = new THREE.PerspectiveCamera(ENGINE.CAMERA_FOV, 1, ENGINE.CAMERA_NEAR, ENGINE.CAMERA_FAR);
25
26
  // set proper camera height for first person view
@@ -28,7 +29,7 @@ let FPSPlayer = class FPSPlayer extends ENGINE.Pawn {
28
29
  const weapon = new DefaultWeapon();
29
30
  camera.add(weapon);
30
31
  // use a simple capsule geometry as the root component
31
- const rootComponent = new ENGINE.MeshComponent({
32
+ const rootComponent = ENGINE.MeshComponent.create({
32
33
  geometry: ENGINE.GameBuilder.createDefaultPawnCapsuleGeometry(),
33
34
  material: new THREE.MeshStandardMaterial({ color: ENGINE.Color.YELLOW }),
34
35
  physicsOptions: {
@@ -41,19 +42,14 @@ let FPSPlayer = class FPSPlayer extends ENGINE.Pawn {
41
42
  });
42
43
  // hide the mesh in first person view
43
44
  rootComponent.getMesh().visible = false;
45
+ this.setRootComponent(rootComponent, true);
46
+ this.rootComponent.add(camera);
44
47
  // create the movement component
45
- const movementComponent = new ENGINE.CharacterMovementComponent({
46
- ...ENGINE.CharacterMovementComponent.DEFAULT_OPTIONS,
48
+ const movementComponent = ENGINE.CharacterMovementComponent.create({
47
49
  movementType: ENGINE.CharacterMovementType.FirstPerson,
48
50
  });
49
- super({
50
- ...options,
51
- rootComponent,
52
- movementComponent,
53
- // make sure the directional light follows the player for consistent shadows
54
- enableDirectionalLightFollowing: true,
55
- camera
56
- });
51
+ this.movementComponent = movementComponent;
52
+ this.enableDirectionalLightFollowing = true;
57
53
  // set the pawn to be transient so it's never saved in the level
58
54
  this.setTransient(true);
59
55
  }
@@ -15,44 +15,36 @@ import * as THREE from 'three';
15
15
  */
16
16
  let DefaultWeapon = class DefaultWeapon extends ENGINE.ProjectileWeaponComponent {
17
17
  constructor() {
18
- super({
19
- // firing params
20
- fireInterval: 0.1,
21
- isSingleFire: false,
22
- // configure the weapon model
23
- modelPath: '@project/assets/models/SM_Rifle.glb',
24
- modelTransform: {
25
- position: new THREE.Vector3(0.1, -0.1, -0.25),
26
- rotation: new THREE.Euler(0, THREE.MathUtils.degToRad(-90), 0),
27
- },
28
- // tweak the projectile spawn offset so it matches the muzzle position
29
- projectileSpawnOffset: new THREE.Vector3(0.8, -0.6, -2),
30
- // add a crosshair
31
- crosshairOptions: {
32
- size: 10,
33
- color: 'white',
34
- style: 'dot'
35
- },
36
- // add weapon sounds
37
- fireSoundUrl: '@engine/assets/sounds/game-gun-short.mp3',
38
- // enable recoil
39
- recoilOptions: {
40
- weaponRecoilDistance: 0.05,
41
- weaponRecoilDuration: 0.05,
42
- weaponRecoilRotation: 0,
43
- },
44
- // configure the projectile
45
- projectileOptions: {
46
- velocity: 100,
47
- range: 500,
48
- lifeSpan: 3,
49
- useOverlapCheck: false,
50
- destroyOnHit: false,
51
- gravityScale: 0,
52
- geometry: new THREE.SphereGeometry(0.1),
53
- material: new THREE.MeshStandardMaterial({ color: ENGINE.Color.YELLOW })
54
- }
55
- });
18
+ super();
19
+ this.fireInterval = 0.1;
20
+ this.isSingleFire = false;
21
+ this.modelPath = '@project/assets/models/SM_Rifle.glb';
22
+ this.modelTransform = {
23
+ position: new THREE.Vector3(0.1, -0.1, -0.25),
24
+ rotation: new THREE.Euler(0, THREE.MathUtils.degToRad(-90), 0),
25
+ };
26
+ this.projectileSpawnOffset = new THREE.Vector3(0.8, -0.6, -2);
27
+ this.crosshairOptions = {
28
+ size: 10,
29
+ color: 'white',
30
+ style: 'dot'
31
+ };
32
+ this.fireSoundUrl = '@engine/assets/sounds/game-gun-short.mp3';
33
+ this.recoilOptions = {
34
+ weaponRecoilDistance: 0.05,
35
+ weaponRecoilDuration: 0.05,
36
+ weaponRecoilRotation: 0,
37
+ };
38
+ this.projectileOptions = {
39
+ velocity: 100,
40
+ range: 500,
41
+ lifeSpan: 3,
42
+ useOverlapCheck: false,
43
+ destroyOnHit: false,
44
+ gravityScale: 0,
45
+ geometry: new THREE.SphereGeometry(0.1),
46
+ material: new THREE.MeshStandardMaterial({ color: ENGINE.Color.YELLOW })
47
+ };
56
48
  this.setTransient(true);
57
49
  }
58
50
  };
@@ -13,7 +13,7 @@ class MyGame extends ENGINE.BaseGameLoop {
13
13
  // default spawn location
14
14
  const position = new THREE.Vector3(0, ENGINE.CHARACTER_HEIGHT, 0);
15
15
  // create the pawn with simple movement mechanics
16
- this.pawn = new FreeCameraPlayer({ position });
16
+ this.pawn = FreeCameraPlayer.create({ position });
17
17
  // create the controller and possess the pawn
18
18
  this.controller = new ENGINE.PlayerController();
19
19
  this.controller.possess(this.pawn);
@@ -16,23 +16,18 @@ import * as THREE from 'three';
16
16
  *
17
17
  */
18
18
  let FreeCameraPlayer = class FreeCameraPlayer extends ENGINE.Pawn {
19
- constructor(options) {
19
+ constructor() {
20
+ super();
20
21
  // simple perspective camera
21
22
  const camera = new THREE.PerspectiveCamera(ENGINE.CAMERA_FOV, 1, ENGINE.CAMERA_NEAR, ENGINE.CAMERA_FAR);
23
+ this.rootComponent.add(camera);
22
24
  // simple movement component, do not use the character controller
23
- const movementComponent = new ENGINE.CharacterMovementComponent({
24
- ...ENGINE.CharacterMovementComponent.DEFAULT_OPTIONS,
25
+ const movementComponent = ENGINE.CharacterMovementComponent.create({
25
26
  // disable the character controller
26
27
  characterControllerOptions: null,
27
28
  });
28
- // construct the pawn
29
- super({
30
- ...options,
31
- camera,
32
- movementComponent,
33
- // make sure the directional light follows the player for consistent shadows
34
- enableDirectionalLightFollowing: true,
35
- });
29
+ this.movementComponent = movementComponent;
30
+ this.enableDirectionalLightFollowing = true;
36
31
  // set the pawn to be transient so it's never saved in the level
37
32
  this.setTransient(true);
38
33
  }
@@ -30,9 +30,8 @@ class SideScrollerGame extends ENGINE.BaseGameLoop {
30
30
  createPlayer() {
31
31
  const spawnX = 1;
32
32
  // Create the side-scroller player with animated character
33
- this.player = new SideScrollerPlayer({
34
- position: new THREE.Vector3(spawnX, ENGINE.CHARACTER_HEIGHT / 2, 0),
35
- });
33
+ const position = new THREE.Vector3(spawnX, ENGINE.CHARACTER_HEIGHT / 2, 0);
34
+ this.player = SideScrollerPlayer.create({ position });
36
35
  // Create the controller and possess the player
37
36
  this.playerController = new ENGINE.PlayerController();
38
37
  this.playerController.possess(this.player);
@@ -135,7 +135,7 @@ export class SideScrollerLevelGenerator {
135
135
  createElementActor(element) {
136
136
  const geometry = new THREE.BoxGeometry(element.width, element.height, LEVEL_CONFIG.DEPTH);
137
137
  const material = new THREE.MeshStandardMaterial({ color: element.type === 'platform' ? COLORS.PLATFORM : COLORS.OBSTACLE });
138
- const meshComponent = new ENGINE.MeshComponent({
138
+ const meshComponent = ENGINE.MeshComponent.create({
139
139
  geometry: geometry,
140
140
  material: material,
141
141
  position: new THREE.Vector3(element.x, element.height / 2, 0), // Position at ground level
@@ -148,9 +148,9 @@ export class SideScrollerLevelGenerator {
148
148
  // Enable shadow casting and receiving
149
149
  meshComponent.castShadow = true;
150
150
  meshComponent.receiveShadow = true;
151
- return new ENGINE.Actor({
152
- rootComponent: meshComponent,
153
- });
151
+ const actor = new ENGINE.Actor();
152
+ actor.setRootComponent(meshComponent, false);
153
+ return actor;
154
154
  }
155
155
  createChunkDebugVisualization(startX) {
156
156
  // Create a wireframe box to visualize chunk boundaries
@@ -161,16 +161,16 @@ export class SideScrollerLevelGenerator {
161
161
  wireframe: true,
162
162
  transparent: true,
163
163
  });
164
- const chunkBoundary = new ENGINE.Actor({
165
- rootComponent: new ENGINE.MeshComponent({
166
- geometry: chunkGeometry,
167
- material: chunkMaterial,
168
- position: new THREE.Vector3(startX + this.chunkSize / 2, LEVEL_CONFIG.DEBUG_VISUALIZATION_HEIGHT / 2, 0),
169
- physicsOptions: {
170
- enabled: false,
171
- },
172
- }),
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
+ },
173
171
  });
172
+ const chunkBoundary = new ENGINE.Actor();
173
+ chunkBoundary.setRootComponent(meshComponent, false);
174
174
  return chunkBoundary;
175
175
  }
176
176
  createGroundMeshActor(startX) {
@@ -179,7 +179,7 @@ export class SideScrollerLevelGenerator {
179
179
  const groundMaterial = new THREE.MeshStandardMaterial({
180
180
  color: COLORS.GROUND,
181
181
  });
182
- const groundMeshComponent = new ENGINE.MeshComponent({
182
+ const groundMeshComponent = ENGINE.MeshComponent.create({
183
183
  geometry: groundGeometry,
184
184
  material: groundMaterial,
185
185
  position: new THREE.Vector3(startX + this.chunkSize / 2, LEVEL_CONFIG.GROUND_Y_POSITION, 0),
@@ -191,9 +191,9 @@ export class SideScrollerLevelGenerator {
191
191
  });
192
192
  // Enable shadow receiving
193
193
  groundMeshComponent.receiveShadow = true;
194
- return new ENGINE.Actor({
195
- rootComponent: groundMeshComponent,
196
- });
194
+ const actor = new ENGINE.Actor();
195
+ actor.setRootComponent(groundMeshComponent, false);
196
+ return actor;
197
197
  }
198
198
  /**
199
199
  * Gets detailed geometry information at a given x location.
@@ -16,10 +16,10 @@ let SideScrollerPlayer = class SideScrollerPlayer extends ENGINE.ThirdPersonChar
16
16
  characterMesh = null;
17
17
  smoothedCameraHeight = null;
18
18
  smoothedLookAtHeight = null;
19
- constructor(options) {
19
+ constructor() {
20
+ super();
20
21
  // Create directional movement component for side-scrolling
21
- const movementComponent = new ENGINE.DirectionalCharacterMovementComponent({
22
- ...ENGINE.DirectionalCharacterMovementComponent.DEFAULT_OPTIONS,
22
+ const movementComponent = ENGINE.DirectionalCharacterMovementComponent.create({
23
23
  direction: ENGINE.CharacterMovementDirection.LeftRight, // Move left and right
24
24
  autoMove: false, // Player controls movement manually
25
25
  jumpSpeed: PLAYER_MOVEMENT.JUMP_SPEED,
@@ -39,7 +39,7 @@ let SideScrollerPlayer = class SideScrollerPlayer extends ENGINE.ThirdPersonChar
39
39
  generateCollisionEvents: true,
40
40
  };
41
41
  // Create root component with physics (invisible capsule for collision)
42
- const rootComponent = new ENGINE.MeshComponent({
42
+ const rootComponent = ENGINE.MeshComponent.create({
43
43
  geometry: new THREE.CapsuleGeometry(ENGINE.CHARACTER_WIDTH / 2, ENGINE.CHARACTER_HEIGHT - ENGINE.CHARACTER_WIDTH),
44
44
  material: new THREE.MeshStandardMaterial({
45
45
  color: 0xffff00,
@@ -49,22 +49,17 @@ let SideScrollerPlayer = class SideScrollerPlayer extends ENGINE.ThirdPersonChar
49
49
  }),
50
50
  physicsOptions,
51
51
  });
52
- super({
53
- ...options,
54
- // Use default character model from engine assets
55
- modelUrl: '@engine/assets/character/mannequinG.glb',
56
- configUrl: '@engine/assets/character/config/mannequin-anim.json',
57
- meshPosition: new THREE.Vector3(0, -ENGINE.CHARACTER_HEIGHT / 2, 0),
58
- meshRotation: new THREE.Euler(0, Math.PI, 0), // Face right initially
59
- meshScale: new THREE.Vector3(1, 1, 1),
60
- movementComponent,
61
- camera: null, // Do not attach the camera to the player directly
62
- rootComponent,
63
- enableDirectionalLightFollowing: true,
64
- });
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);
65
59
  // Enable shadow casting for the character
66
60
  this.rootComponent.castShadow = true;
67
61
  this.camera = new THREE.PerspectiveCamera(CAMERA_SETTINGS.FOV, 1, CAMERA_SETTINGS.NEAR, CAMERA_SETTINGS.FAR);
62
+ this.enableDirectionalLightFollowing = true;
68
63
  }
69
64
  async doBeginPlay() {
70
65
  await super.doBeginPlay();
@@ -13,7 +13,7 @@ class ThirdPersonGame extends ENGINE.BaseGameLoop {
13
13
  // default spawn location
14
14
  const position = new THREE.Vector3(0, ENGINE.CHARACTER_HEIGHT / 2, 0);
15
15
  // create the pawn
16
- this.pawn = new ThirdPersonPlayer({ position });
16
+ this.pawn = ThirdPersonPlayer.create({ position });
17
17
  // create the controller and possess the pawn
18
18
  this.controller = new ENGINE.PlayerController();
19
19
  this.controller.possess(this.pawn);
@@ -17,14 +17,15 @@ import * as THREE from 'three';
17
17
  */
18
18
  let ThirdPersonPlayer = class ThirdPersonPlayer extends ENGINE.ThirdPersonCharacterPawn {
19
19
  // Omit all the options that are created internally
20
- constructor(options) {
20
+ constructor() {
21
+ super();
21
22
  // simple camera component - contains a perspective camera by default
22
23
  const camera = new THREE.PerspectiveCamera(ENGINE.CAMERA_FOV, 1, 0.1, 1000);
23
24
  // set camera position for third person view
24
25
  camera.position.set(0, ENGINE.CHARACTER_HEIGHT * 1.3, ENGINE.CHARACTER_HEIGHT * 2);
25
26
  camera.lookAt(0, 0, 0);
26
27
  // use capsule root component for collision
27
- const rootComponent = new ENGINE.MeshComponent({
28
+ const rootComponent = ENGINE.MeshComponent.create({
28
29
  geometry: ENGINE.GameBuilder.createDefaultPawnCapsuleGeometry(),
29
30
  material: new THREE.MeshStandardMaterial({ color: ENGINE.Color.YELLOW, visible: false, transparent: true, opacity: 0.5 }),
30
31
  physicsOptions: {
@@ -34,25 +35,19 @@ let ThirdPersonPlayer = class ThirdPersonPlayer extends ENGINE.ThirdPersonCharac
34
35
  collisionProfile: ENGINE.DefaultCollisionProfile.Character,
35
36
  },
36
37
  });
38
+ this.setRootComponent(rootComponent, true);
37
39
  // use third person movement mechanics
38
- const movementComponent = new ENGINE.CharacterMovementComponent({
39
- ...ENGINE.CharacterMovementComponent.DEFAULT_OPTIONS,
40
+ const movementComponent = ENGINE.CharacterMovementComponent.create({
40
41
  movementType: ENGINE.CharacterMovementType.ThirdPerson,
41
42
  });
42
- // construct the pawn
43
- super({
44
- ...options,
45
- rootComponent,
46
- movementComponent,
47
- camera,
48
- // make sure the directional light follows the player for consistent shadows
49
- enableDirectionalLightFollowing: true,
50
- modelUrl: '@engine/assets/character/mannequinG.glb',
51
- configUrl: '@engine/assets/character/config/mannequin-anim.json',
52
- meshPosition: new THREE.Vector3(0, -ENGINE.CHARACTER_HEIGHT / 2, 0),
53
- meshRotation: new THREE.Euler(0, Math.PI, 0),
54
- meshScale: new THREE.Vector3(1, 1, 1),
55
- });
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);
56
51
  // set the pawn to be transient so it's never saved in the level
57
52
  this.setTransient(true);
58
53
  }