@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.
- package/dist/src/core/tools/new-project.js +1 -0
- package/dist/src/dependencies.js +1 -1
- package/dist/src/templates/scripts/genesys/migrate-scenes-and-prefabs.js +179 -0
- package/dist/src/templates/scripts/genesys/post-install.js +10 -2
- package/dist/src/templates/src/templates/firstPerson/src/game.js +1 -1
- package/dist/src/templates/src/templates/firstPerson/src/player.js +8 -13
- package/dist/src/templates/src/templates/fps/src/game.js +1 -1
- package/dist/src/templates/src/templates/fps/src/player.js +8 -12
- package/dist/src/templates/src/templates/fps/src/weapon.js +30 -38
- package/dist/src/templates/src/templates/freeCamera/src/game.js +1 -1
- package/dist/src/templates/src/templates/freeCamera/src/player.js +6 -11
- package/dist/src/templates/src/templates/sideScroller/src/game.js +2 -3
- package/dist/src/templates/src/templates/sideScroller/src/level-generator.js +17 -17
- package/dist/src/templates/src/templates/sideScroller/src/player.js +12 -17
- package/dist/src/templates/src/templates/thirdPerson/src/game.js +1 -1
- package/dist/src/templates/src/templates/thirdPerson/src/player.js +13 -18
- package/dist/src/templates/src/templates/vehicle/src/base-vehicle.js +1 -1
- package/dist/src/templates/src/templates/vehicle/src/game.js +2 -2
- package/dist/src/templates/src/templates/vehicle/src/mesh-vehicle.js +8 -9
- package/dist/src/templates/src/templates/vehicle/src/player.js +13 -18
- package/dist/src/templates/src/templates/vehicle/src/primitive-vehicle.js +11 -12
- package/dist/src/templates/src/templates/vehicle/src/ui-hints.js +2 -2
- package/dist/src/templates/src/templates/vr-game/src/game.js +5 -5
- package/package.json +2 -2
- package/src/templates/scripts/genesys/migrate-scenes-and-prefabs.ts +212 -0
- package/src/templates/scripts/genesys/post-install.ts +11 -2
- package/src/templates/src/templates/firstPerson/src/game.ts +1 -1
- package/src/templates/src/templates/firstPerson/src/player.ts +10 -14
- package/src/templates/src/templates/fps/src/game.ts +1 -1
- package/src/templates/src/templates/fps/src/player.ts +10 -13
- package/src/templates/src/templates/fps/src/weapon.ts +31 -38
- package/src/templates/src/templates/freeCamera/src/game.ts +1 -1
- package/src/templates/src/templates/freeCamera/src/player.ts +7 -12
- package/src/templates/src/templates/sideScroller/src/game.ts +2 -3
- package/src/templates/src/templates/sideScroller/src/level-generator.ts +21 -21
- package/src/templates/src/templates/sideScroller/src/player.ts +15 -17
- package/src/templates/src/templates/thirdPerson/src/game.ts +1 -1
- package/src/templates/src/templates/thirdPerson/src/player.ts +16 -19
- package/src/templates/src/templates/vehicle/src/base-vehicle.ts +1 -1
- package/src/templates/src/templates/vehicle/src/game.ts +2 -2
- package/src/templates/src/templates/vehicle/src/mesh-vehicle.ts +8 -10
- package/src/templates/src/templates/vehicle/src/player.ts +16 -19
- package/src/templates/src/templates/vehicle/src/primitive-vehicle.ts +11 -13
- package/src/templates/src/templates/vehicle/src/ui-hints.ts +2 -2
- 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',
|
package/dist/src/dependencies.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
41
|
-
...ENGINE.CharacterMovementComponent.DEFAULT_OPTIONS,
|
|
43
|
+
const movementComponent = ENGINE.CharacterMovementComponent.create({
|
|
42
44
|
movementType: ENGINE.CharacterMovementType.FirstPerson,
|
|
43
45
|
});
|
|
44
|
-
|
|
45
|
-
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
46
|
-
...ENGINE.CharacterMovementComponent.DEFAULT_OPTIONS,
|
|
48
|
+
const movementComponent = ENGINE.CharacterMovementComponent.create({
|
|
47
49
|
movementType: ENGINE.CharacterMovementType.FirstPerson,
|
|
48
50
|
});
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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 =
|
|
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(
|
|
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 =
|
|
24
|
-
...ENGINE.CharacterMovementComponent.DEFAULT_OPTIONS,
|
|
25
|
+
const movementComponent = ENGINE.CharacterMovementComponent.create({
|
|
25
26
|
// disable the character controller
|
|
26
27
|
characterControllerOptions: null,
|
|
27
28
|
});
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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 =
|
|
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
|
-
|
|
152
|
-
|
|
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
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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 =
|
|
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
|
-
|
|
195
|
-
|
|
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(
|
|
19
|
+
constructor() {
|
|
20
|
+
super();
|
|
20
21
|
// Create directional movement component for side-scrolling
|
|
21
|
-
const movementComponent =
|
|
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 =
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
39
|
-
...ENGINE.CharacterMovementComponent.DEFAULT_OPTIONS,
|
|
40
|
+
const movementComponent = ENGINE.CharacterMovementComponent.create({
|
|
40
41
|
movementType: ENGINE.CharacterMovementType.ThirdPerson,
|
|
41
42
|
});
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
}
|