@directivegames/genesys.sdk 3.2.2
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/README.md +60 -0
- package/dist/src/asset-pack/eslint.config.js +43 -0
- package/dist/src/asset-pack/scripts/postinstall.js +64 -0
- package/dist/src/asset-pack/src/index.js +1 -0
- package/dist/src/core/cli.js +306 -0
- package/dist/src/core/common.js +324 -0
- package/dist/src/core/index.js +6 -0
- package/dist/src/core/tools/build-project.js +450 -0
- package/dist/src/core/tools/index.js +2 -0
- package/dist/src/core/tools/new-asset-pack.js +150 -0
- package/dist/src/core/tools/new-project.js +292 -0
- package/dist/src/core/types.js +1 -0
- package/dist/src/dependencies.js +82 -0
- package/dist/src/electron/IpcSerializableError.js +38 -0
- package/dist/src/electron/api.js +7 -0
- package/dist/src/electron/backend/actions.js +56 -0
- package/dist/src/electron/backend/handler.js +441 -0
- package/dist/src/electron/backend/logging.js +41 -0
- package/dist/src/electron/backend/main.js +315 -0
- package/dist/src/electron/backend/menu.js +208 -0
- package/dist/src/electron/backend/state.js +201 -0
- package/dist/src/electron/backend/tools/const.js +9 -0
- package/dist/src/electron/backend/tools/file-server.js +383 -0
- package/dist/src/electron/backend/tools/open-project.js +261 -0
- package/dist/src/electron/backend/window.js +161 -0
- package/dist/src/templates/eslint.config.js +43 -0
- package/dist/src/templates/scripts/genesys/build-project.js +42 -0
- package/dist/src/templates/scripts/genesys/calc-bounding-box.js +205 -0
- package/dist/src/templates/scripts/genesys/common.js +36 -0
- package/dist/src/templates/scripts/genesys/const.js +9 -0
- package/dist/src/templates/scripts/genesys/dev/dump-default-scene.js +8 -0
- package/dist/src/templates/scripts/genesys/dev/generate-manifest.js +116 -0
- package/dist/src/templates/scripts/genesys/dev/launcher.js +39 -0
- package/dist/src/templates/scripts/genesys/dev/storage-provider.js +188 -0
- package/dist/src/templates/scripts/genesys/dev/update-template-scenes.js +67 -0
- package/dist/src/templates/scripts/genesys/doc-server.js +12 -0
- package/dist/src/templates/scripts/genesys/genesys-mcp.js +413 -0
- package/dist/src/templates/scripts/genesys/mcp/doc-tools.js +70 -0
- package/dist/src/templates/scripts/genesys/mcp/editor-functions.js +123 -0
- package/dist/src/templates/scripts/genesys/mcp/editor-tools.js +51 -0
- package/dist/src/templates/scripts/genesys/mcp/get-scene-state.js +26 -0
- package/dist/src/templates/scripts/genesys/mcp/run-subprocess.js +23 -0
- package/dist/src/templates/scripts/genesys/mcp/search-actors.js +703 -0
- package/dist/src/templates/scripts/genesys/mcp/search-assets.js +296 -0
- package/dist/src/templates/scripts/genesys/mcp/utils.js +234 -0
- package/dist/src/templates/scripts/genesys/misc.js +32 -0
- package/dist/src/templates/scripts/genesys/mock.js +5 -0
- package/dist/src/templates/scripts/genesys/place-actors.js +112 -0
- package/dist/src/templates/scripts/genesys/post-install.js +25 -0
- package/dist/src/templates/scripts/genesys/remove-engine-comments.js +113 -0
- package/dist/src/templates/scripts/genesys/storageProvider.js +146 -0
- package/dist/src/templates/scripts/genesys/validate-prefabs.js +115 -0
- package/dist/src/templates/src/index.js +20 -0
- package/dist/src/templates/src/templates/firstPerson/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/firstPerson/src/game.js +30 -0
- package/dist/src/templates/src/templates/firstPerson/src/player.js +60 -0
- package/dist/src/templates/src/templates/fps/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/fps/src/game.js +30 -0
- package/dist/src/templates/src/templates/fps/src/player.js +64 -0
- package/dist/src/templates/src/templates/fps/src/weapon.js +62 -0
- package/dist/src/templates/src/templates/freeCamera/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/freeCamera/src/game.js +30 -0
- package/dist/src/templates/src/templates/freeCamera/src/player.js +43 -0
- package/dist/src/templates/src/templates/sideScroller/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/sideScroller/src/const.js +43 -0
- package/dist/src/templates/src/templates/sideScroller/src/game.js +103 -0
- package/dist/src/templates/src/templates/sideScroller/src/level-generator.js +249 -0
- package/dist/src/templates/src/templates/sideScroller/src/player.js +105 -0
- package/dist/src/templates/src/templates/thirdPerson/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/thirdPerson/src/game.js +30 -0
- package/dist/src/templates/src/templates/thirdPerson/src/player.js +63 -0
- package/dist/src/templates/src/templates/vehicle/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/vehicle/src/base-vehicle.js +122 -0
- package/dist/src/templates/src/templates/vehicle/src/game.js +33 -0
- package/dist/src/templates/src/templates/vehicle/src/mesh-vehicle.js +189 -0
- package/dist/src/templates/src/templates/vehicle/src/player.js +102 -0
- package/dist/src/templates/src/templates/vehicle/src/primitive-vehicle.js +259 -0
- package/dist/src/templates/src/templates/vehicle/src/ui-hints.js +100 -0
- package/dist/src/templates/src/templates/vr-game/src/auto-imports.js +1 -0
- package/dist/src/templates/src/templates/vr-game/src/game.js +55 -0
- package/dist/src/templates/src/templates/vr-game/src/sample-vr-actor.js +29 -0
- package/dist/src/templates/vite.config.js +46 -0
- package/package.json +176 -0
- package/scripts/post-install.ts +143 -0
- package/src/asset-pack/.gitattributes +89 -0
- package/src/asset-pack/eslint.config.js +45 -0
- package/src/asset-pack/gitignore +11 -0
- package/src/asset-pack/scripts/postinstall.ts +81 -0
- package/src/asset-pack/src/index.ts +0 -0
- package/src/asset-pack/tsconfig.json +34 -0
- package/src/templates/.cursor/mcp.json +20 -0
- package/src/templates/.cursorignore +2 -0
- package/src/templates/.gitattributes +89 -0
- package/src/templates/.vscode/settings.json +6 -0
- package/src/templates/AGENTS.md +86 -0
- package/src/templates/CLAUDE.md +1 -0
- package/src/templates/README.md +24 -0
- package/src/templates/eslint.config.js +45 -0
- package/src/templates/gitignore +11 -0
- package/src/templates/index.html +34 -0
- package/src/templates/pnpm-lock.yaml +3676 -0
- package/src/templates/scripts/genesys/build-project.ts +51 -0
- package/src/templates/scripts/genesys/calc-bounding-box.ts +272 -0
- package/src/templates/scripts/genesys/common.ts +46 -0
- package/src/templates/scripts/genesys/const.ts +9 -0
- package/src/templates/scripts/genesys/dev/dump-default-scene.ts +11 -0
- package/src/templates/scripts/genesys/dev/generate-manifest.ts +146 -0
- package/src/templates/scripts/genesys/dev/launcher.ts +46 -0
- package/src/templates/scripts/genesys/dev/storage-provider.ts +229 -0
- package/src/templates/scripts/genesys/dev/update-template-scenes.ts +84 -0
- package/src/templates/scripts/genesys/doc-server.ts +16 -0
- package/src/templates/scripts/genesys/genesys-mcp.ts +526 -0
- package/src/templates/scripts/genesys/mcp/doc-tools.ts +86 -0
- package/src/templates/scripts/genesys/mcp/editor-functions.ts +151 -0
- package/src/templates/scripts/genesys/mcp/editor-tools.ts +73 -0
- package/src/templates/scripts/genesys/mcp/get-scene-state.ts +35 -0
- package/src/templates/scripts/genesys/mcp/run-subprocess.ts +30 -0
- package/src/templates/scripts/genesys/mcp/search-actors.ts +858 -0
- package/src/templates/scripts/genesys/mcp/search-assets.ts +380 -0
- package/src/templates/scripts/genesys/mcp/utils.ts +281 -0
- package/src/templates/scripts/genesys/misc.ts +42 -0
- package/src/templates/scripts/genesys/mock.ts +6 -0
- package/src/templates/scripts/genesys/place-actors.ts +179 -0
- package/src/templates/scripts/genesys/post-install.ts +30 -0
- package/src/templates/scripts/genesys/prefab.schema.json +85 -0
- package/src/templates/scripts/genesys/remove-engine-comments.ts +135 -0
- package/src/templates/scripts/genesys/run-mcp-inspector.bat +5 -0
- package/src/templates/scripts/genesys/storageProvider.ts +182 -0
- package/src/templates/scripts/genesys/validate-prefabs.ts +138 -0
- package/src/templates/src/index.ts +22 -0
- package/src/templates/src/templates/firstPerson/assets/default.genesys-scene +166 -0
- package/src/templates/src/templates/firstPerson/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/firstPerson/src/game.ts +39 -0
- package/src/templates/src/templates/firstPerson/src/player.ts +63 -0
- package/src/templates/src/templates/fps/assets/default.genesys-scene +9460 -0
- package/src/templates/src/templates/fps/assets/models/SM_Beam_400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_ChamferCube.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thick_400x400_Orange.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Floor_Thin_400x400_Orange.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Ramp_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Rifle.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x200_Orange.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400.glb +0 -0
- package/src/templates/src/templates/fps/assets/models/SM_Wall_Thin_400x400_Orange.glb +0 -0
- package/src/templates/src/templates/fps/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/fps/src/game.ts +39 -0
- package/src/templates/src/templates/fps/src/player.ts +69 -0
- package/src/templates/src/templates/fps/src/weapon.ts +54 -0
- package/src/templates/src/templates/freeCamera/assets/default.genesys-scene +166 -0
- package/src/templates/src/templates/freeCamera/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/freeCamera/src/game.ts +39 -0
- package/src/templates/src/templates/freeCamera/src/player.ts +45 -0
- package/src/templates/src/templates/sideScroller/assets/default.genesys-scene +122 -0
- package/src/templates/src/templates/sideScroller/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/sideScroller/src/const.ts +46 -0
- package/src/templates/src/templates/sideScroller/src/game.ts +122 -0
- package/src/templates/src/templates/sideScroller/src/level-generator.ts +361 -0
- package/src/templates/src/templates/sideScroller/src/player.ts +125 -0
- package/src/templates/src/templates/thirdPerson/assets/default.genesys-scene +166 -0
- package/src/templates/src/templates/thirdPerson/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/thirdPerson/src/game.ts +39 -0
- package/src/templates/src/templates/thirdPerson/src/player.ts +61 -0
- package/src/templates/src/templates/vehicle/assets/default.genesys-scene +226 -0
- package/src/templates/src/templates/vehicle/assets/models/cyberTruck/chassis.glb +0 -0
- package/src/templates/src/templates/vehicle/assets/models/cyberTruck/wheel.glb +0 -0
- package/src/templates/src/templates/vehicle/src/auto-imports.ts +0 -0
- package/src/templates/src/templates/vehicle/src/base-vehicle.ts +145 -0
- package/src/templates/src/templates/vehicle/src/game.ts +43 -0
- package/src/templates/src/templates/vehicle/src/mesh-vehicle.ts +191 -0
- package/src/templates/src/templates/vehicle/src/player.ts +109 -0
- package/src/templates/src/templates/vehicle/src/primitive-vehicle.ts +266 -0
- package/src/templates/src/templates/vehicle/src/ui-hints.ts +101 -0
- package/src/templates/src/templates/vr-game/assets/default.genesys-scene +247 -0
- package/src/templates/src/templates/vr-game/src/auto-imports.ts +1 -0
- package/src/templates/src/templates/vr-game/src/game.ts +66 -0
- package/src/templates/src/templates/vr-game/src/sample-vr-actor.ts +26 -0
- package/src/templates/tsconfig.json +35 -0
- package/src/templates/vite.config.ts +52 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as ENGINE from 'genesys.js';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { FreeCameraPlayer } from './player.js';
|
|
4
|
+
import './auto-imports.js';
|
|
5
|
+
class MyGame extends ENGINE.BaseGameLoop {
|
|
6
|
+
pawn = null;
|
|
7
|
+
controller = null;
|
|
8
|
+
createLoadingScreen() {
|
|
9
|
+
// enable the default loading screen
|
|
10
|
+
return new ENGINE.DefaultLoadingScreen();
|
|
11
|
+
}
|
|
12
|
+
async preStart() {
|
|
13
|
+
// default spawn location
|
|
14
|
+
const position = new THREE.Vector3(0, ENGINE.CHARACTER_HEIGHT, 0);
|
|
15
|
+
// create the pawn with simple movement mechanics
|
|
16
|
+
this.pawn = new FreeCameraPlayer({ position });
|
|
17
|
+
// create the controller and possess the pawn
|
|
18
|
+
this.controller = new ENGINE.PlayerController();
|
|
19
|
+
this.controller.possess(this.pawn);
|
|
20
|
+
// add both to the world
|
|
21
|
+
this.world.addActors(this.pawn, this.controller);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function main(container, gameId) {
|
|
25
|
+
const game = new MyGame(container, {
|
|
26
|
+
...ENGINE.BaseGameLoop.DEFAULT_OPTIONS,
|
|
27
|
+
gameId
|
|
28
|
+
});
|
|
29
|
+
return game;
|
|
30
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import * as ENGINE from 'genesys.js';
|
|
8
|
+
import * as THREE from 'three';
|
|
9
|
+
/**
|
|
10
|
+
* A free camera player class.
|
|
11
|
+
*
|
|
12
|
+
* Key points:
|
|
13
|
+
* - No need to provide movementComponent and camera, they are created internally
|
|
14
|
+
* - The pawn is set to be transient so it's never saved in the level
|
|
15
|
+
* - The directional light follows the player for consistent shadows
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
let FreeCameraPlayer = class FreeCameraPlayer extends ENGINE.Pawn {
|
|
19
|
+
constructor(options) {
|
|
20
|
+
// simple perspective camera
|
|
21
|
+
const camera = new THREE.PerspectiveCamera(ENGINE.CAMERA_FOV, 1, ENGINE.CAMERA_NEAR, ENGINE.CAMERA_FAR);
|
|
22
|
+
// simple movement component, do not use the character controller
|
|
23
|
+
const movementComponent = new ENGINE.CharacterMovementComponent({
|
|
24
|
+
...ENGINE.CharacterMovementComponent.DEFAULT_OPTIONS,
|
|
25
|
+
// disable the character controller
|
|
26
|
+
characterControllerOptions: null,
|
|
27
|
+
});
|
|
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
|
+
});
|
|
36
|
+
// set the pawn to be transient so it's never saved in the level
|
|
37
|
+
this.setTransient(true);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
FreeCameraPlayer = __decorate([
|
|
41
|
+
ENGINE.GameClass()
|
|
42
|
+
], FreeCameraPlayer);
|
|
43
|
+
export { FreeCameraPlayer };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Player Movement Configuration
|
|
2
|
+
export const PLAYER_MOVEMENT = {
|
|
3
|
+
JUMP_SPEED: 20,
|
|
4
|
+
MAX_SPEED: 8,
|
|
5
|
+
MAX_MID_AIR_JUMPS: 1,
|
|
6
|
+
ACCELERATION_LAMBDA: 15,
|
|
7
|
+
DECELERATION_LAMBDA: 10,
|
|
8
|
+
MID_AIR_ACCELERATION_LAMBDA: 8,
|
|
9
|
+
MID_AIR_DECELERATION_LAMBDA: 2,
|
|
10
|
+
};
|
|
11
|
+
// Camera Settings
|
|
12
|
+
export const CAMERA_SETTINGS = {
|
|
13
|
+
FOV: 60,
|
|
14
|
+
NEAR: 0.1,
|
|
15
|
+
FAR: 1000,
|
|
16
|
+
DAMP_FACTOR: 10,
|
|
17
|
+
POSITION: { x: 0, y: 1, z: 8 },
|
|
18
|
+
LOOK_AT: { x: 0, y: 0, z: 0 },
|
|
19
|
+
};
|
|
20
|
+
// Level Generation Configuration
|
|
21
|
+
export const LEVEL_CONFIG = {
|
|
22
|
+
DEPTH: 3,
|
|
23
|
+
CHUNK_SIZE: 50,
|
|
24
|
+
CHUNKS_AHEAD: 2,
|
|
25
|
+
CHUNKS_BEHIND: 2,
|
|
26
|
+
PLATFORM_HEIGHT_RANGE: { min: 1, max: 2 },
|
|
27
|
+
PLATFORM_WIDTH_RANGE: { min: 3, max: 6 },
|
|
28
|
+
OBSTACLE_HEIGHT_RANGE: { min: 3, max: 6 },
|
|
29
|
+
OBSTACLE_WIDTH_RANGE: { min: 1.5, max: 3.0 },
|
|
30
|
+
CHUNK_MARGIN: 0,
|
|
31
|
+
MIN_SPACING: 4,
|
|
32
|
+
DEBUG_VISUALIZATION_HEIGHT: 6,
|
|
33
|
+
DEBUG_VISUALIZATION_OPACITY: 0.3,
|
|
34
|
+
DEBUG_VISUALIZATION_COLOR: 0x00ff00,
|
|
35
|
+
GROUND_HEIGHT: 0.1,
|
|
36
|
+
GROUND_Y_POSITION: -0.05,
|
|
37
|
+
};
|
|
38
|
+
// Material colors
|
|
39
|
+
export const COLORS = {
|
|
40
|
+
PLATFORM: 0x90ee90, // Light green
|
|
41
|
+
OBSTACLE: 0xff4444, // Red
|
|
42
|
+
GROUND: 0x8b4513, // Brown
|
|
43
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as ENGINE from 'genesys.js';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import './auto-imports.js';
|
|
4
|
+
import { SideScrollerLevelGenerator } from './level-generator.js';
|
|
5
|
+
import { SideScrollerPlayer } from './player.js';
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// SIDE SCROLLER GAME
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// A side-scroller game with animated character and infinite level generation
|
|
10
|
+
class SideScrollerGame extends ENGINE.BaseGameLoop {
|
|
11
|
+
player = null;
|
|
12
|
+
playerController = null;
|
|
13
|
+
levelGenerator = null;
|
|
14
|
+
createLoadingScreen() {
|
|
15
|
+
// enable the default loading screen
|
|
16
|
+
return new ENGINE.DefaultLoadingScreen();
|
|
17
|
+
}
|
|
18
|
+
async preStart() {
|
|
19
|
+
this.createLevel();
|
|
20
|
+
this.createPlayer();
|
|
21
|
+
}
|
|
22
|
+
tick(tickTime) {
|
|
23
|
+
super.tick(tickTime);
|
|
24
|
+
// Update level generation based on player position
|
|
25
|
+
if (this.player && this.levelGenerator) {
|
|
26
|
+
const playerPosition = this.player.getWorldPosition();
|
|
27
|
+
this.levelGenerator.updateLevel(playerPosition.x);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
createPlayer() {
|
|
31
|
+
const spawnX = 1;
|
|
32
|
+
// Create the side-scroller player with animated character
|
|
33
|
+
this.player = new SideScrollerPlayer({
|
|
34
|
+
position: new THREE.Vector3(spawnX, ENGINE.CHARACTER_HEIGHT / 2, 0),
|
|
35
|
+
});
|
|
36
|
+
// Create the controller and possess the player
|
|
37
|
+
this.playerController = new ENGINE.PlayerController();
|
|
38
|
+
this.playerController.possess(this.player);
|
|
39
|
+
// Add both to the world
|
|
40
|
+
this.world.addActors(this.player, this.playerController);
|
|
41
|
+
this.setInitialPlayerPosition(spawnX);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Sets the initial position of the player.
|
|
45
|
+
* @param spawnX - The X coordinate to spawn the player at.
|
|
46
|
+
*/
|
|
47
|
+
setInitialPlayerPosition(spawnX) {
|
|
48
|
+
const groundHeight = this.findGroundHeightAtX(spawnX);
|
|
49
|
+
if (groundHeight !== null) {
|
|
50
|
+
this.player?.setWorldPosition(new THREE.Vector3(spawnX, groundHeight + ENGINE.CHARACTER_HEIGHT / 2, 0));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
this.world.runInNextTick(() => {
|
|
54
|
+
this.setInitialPlayerPosition(spawnX);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Performs a hit test to find the ground height at a given X coordinate
|
|
60
|
+
* @param x - The X coordinate to test
|
|
61
|
+
* @returns The Y coordinate of the ground surface, or 0 if no ground is found
|
|
62
|
+
*/
|
|
63
|
+
findGroundHeightAtX(x) {
|
|
64
|
+
// Start from a high position and cast downward
|
|
65
|
+
const rayOrigin = new THREE.Vector3(x, 50, 0);
|
|
66
|
+
const rayDirection = new THREE.Vector3(0, -1, 0); // Downward
|
|
67
|
+
const hitResults = this.world.getPhysicsEngine()?.performHitTest({
|
|
68
|
+
origin: rayOrigin,
|
|
69
|
+
direction: rayDirection,
|
|
70
|
+
maxDistance: 100, // Cast ray down 100 units
|
|
71
|
+
stopOnFirstHit: true,
|
|
72
|
+
});
|
|
73
|
+
if (hitResults && hitResults.length > 0) {
|
|
74
|
+
// Return the Y coordinate of the hit location (top surface of ground)
|
|
75
|
+
return hitResults[0].hitLocation.y;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
createLevel() {
|
|
80
|
+
// Create the level generator
|
|
81
|
+
this.levelGenerator = new SideScrollerLevelGenerator(this.world, {
|
|
82
|
+
enableDebugVisualization: false,
|
|
83
|
+
createGroundMesh: true
|
|
84
|
+
});
|
|
85
|
+
// Generate initial level chunks
|
|
86
|
+
this.levelGenerator.updateLevel(0);
|
|
87
|
+
}
|
|
88
|
+
destroy() {
|
|
89
|
+
// Clean up level generator
|
|
90
|
+
if (this.levelGenerator) {
|
|
91
|
+
this.levelGenerator.cleanup();
|
|
92
|
+
this.levelGenerator = null;
|
|
93
|
+
}
|
|
94
|
+
super.destroy();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export function main(container, gameId) {
|
|
98
|
+
const game = new SideScrollerGame(container, {
|
|
99
|
+
...ENGINE.BaseGameLoop.DEFAULT_OPTIONS,
|
|
100
|
+
gameId
|
|
101
|
+
});
|
|
102
|
+
return game;
|
|
103
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import * as ENGINE from 'genesys.js';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { COLORS, LEVEL_CONFIG } from './const.js';
|
|
4
|
+
// Simple level generator for infinite side-scrolling levels
|
|
5
|
+
export class SideScrollerLevelGenerator {
|
|
6
|
+
world;
|
|
7
|
+
// Map of chunk index to chunk
|
|
8
|
+
levelChunks = new Map();
|
|
9
|
+
// Configuration
|
|
10
|
+
chunkSize;
|
|
11
|
+
chunksAhead;
|
|
12
|
+
chunksBehind;
|
|
13
|
+
platformHeightRange;
|
|
14
|
+
platformWidthRange;
|
|
15
|
+
obstacleHeightRange;
|
|
16
|
+
chunkMargin;
|
|
17
|
+
minSpacing;
|
|
18
|
+
debugVisualizationEnabled = false;
|
|
19
|
+
createGroundMesh = false;
|
|
20
|
+
constructor(world, options = {}) {
|
|
21
|
+
this.world = world;
|
|
22
|
+
// Apply defaults
|
|
23
|
+
this.chunkSize = options.chunkSize ?? LEVEL_CONFIG.CHUNK_SIZE;
|
|
24
|
+
this.chunksAhead = options.chunksAhead ?? LEVEL_CONFIG.CHUNKS_AHEAD;
|
|
25
|
+
this.chunksBehind = options.chunksBehind ?? LEVEL_CONFIG.CHUNKS_BEHIND;
|
|
26
|
+
this.platformHeightRange = options.platformHeightRange ?? LEVEL_CONFIG.PLATFORM_HEIGHT_RANGE;
|
|
27
|
+
this.platformWidthRange = options.platformWidthRange ?? LEVEL_CONFIG.PLATFORM_WIDTH_RANGE;
|
|
28
|
+
this.obstacleHeightRange = options.obstacleHeightRange ?? LEVEL_CONFIG.OBSTACLE_HEIGHT_RANGE;
|
|
29
|
+
this.chunkMargin = options.chunkMargin ?? LEVEL_CONFIG.CHUNK_MARGIN;
|
|
30
|
+
this.minSpacing = options.minSpacing ?? LEVEL_CONFIG.MIN_SPACING;
|
|
31
|
+
this.debugVisualizationEnabled = options.enableDebugVisualization ?? false;
|
|
32
|
+
this.createGroundMesh = options.createGroundMesh ?? false;
|
|
33
|
+
}
|
|
34
|
+
updateLevel(playerX) {
|
|
35
|
+
const playerChunkIndex = this.getChunkIndex(playerX);
|
|
36
|
+
const validChunkIndices = [];
|
|
37
|
+
for (let i = playerChunkIndex - this.chunksBehind; i <= playerChunkIndex + this.chunksAhead; i++) {
|
|
38
|
+
this.generateChunk(i);
|
|
39
|
+
validChunkIndices.push(i);
|
|
40
|
+
}
|
|
41
|
+
for (const index of this.levelChunks.keys()) {
|
|
42
|
+
if (!validChunkIndices.includes(index)) {
|
|
43
|
+
this.removeChunk(index);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
getChunkIndex(x) {
|
|
48
|
+
return Math.floor(x / this.chunkSize);
|
|
49
|
+
}
|
|
50
|
+
removeChunk(index) {
|
|
51
|
+
const chunk = this.levelChunks.get(index);
|
|
52
|
+
if (chunk) {
|
|
53
|
+
this.world.removeActors(...chunk.actors);
|
|
54
|
+
this.levelChunks.delete(index);
|
|
55
|
+
console.log(`[LevelGenerator] Removed chunk at index ${index}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
generateChunk(index) {
|
|
59
|
+
if (this.levelChunks.has(index)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const startX = index * this.chunkSize;
|
|
63
|
+
const endX = startX + this.chunkSize;
|
|
64
|
+
const actors = [];
|
|
65
|
+
// Generate positioned elements for this chunk
|
|
66
|
+
const elements = this.generatePositionedElements(startX);
|
|
67
|
+
// Create actors for each element
|
|
68
|
+
for (const element of elements) {
|
|
69
|
+
const actor = this.createElementActor(element);
|
|
70
|
+
actors.push(actor);
|
|
71
|
+
}
|
|
72
|
+
// Add ground mesh if enabled
|
|
73
|
+
if (this.createGroundMesh) {
|
|
74
|
+
const groundActor = this.createGroundMeshActor(startX);
|
|
75
|
+
actors.push(groundActor);
|
|
76
|
+
}
|
|
77
|
+
// Add debug visualization if enabled
|
|
78
|
+
if (this.debugVisualizationEnabled) {
|
|
79
|
+
const debugActor = this.createChunkDebugVisualization(startX);
|
|
80
|
+
actors.push(debugActor);
|
|
81
|
+
}
|
|
82
|
+
// Add all actors to the world
|
|
83
|
+
this.world.addActors(...actors);
|
|
84
|
+
// Track this chunk
|
|
85
|
+
const chunk = {
|
|
86
|
+
startX,
|
|
87
|
+
endX,
|
|
88
|
+
actors,
|
|
89
|
+
};
|
|
90
|
+
console.log(`[LevelGenerator] Generated chunk at index ${index}`);
|
|
91
|
+
this.levelChunks.set(index, chunk);
|
|
92
|
+
}
|
|
93
|
+
generatePositionedElements(startX) {
|
|
94
|
+
const elements = [];
|
|
95
|
+
const usableWidth = this.chunkSize - (this.chunkMargin * 2); // Leave margin on each side
|
|
96
|
+
const usableStartX = startX + this.chunkMargin;
|
|
97
|
+
// Simple alternating generation: platform -> obstacle -> platform -> obstacle...
|
|
98
|
+
const totalElements = Math.floor(usableWidth / this.minSpacing); // How many elements can fit
|
|
99
|
+
let currentX = usableStartX;
|
|
100
|
+
let isPlatform = true; // Start with platform
|
|
101
|
+
for (let i = 0; i < totalElements && currentX < (startX + this.chunkSize - this.chunkMargin); i++) {
|
|
102
|
+
if (isPlatform) {
|
|
103
|
+
// Generate platform with random dimensions
|
|
104
|
+
const width = this.platformWidthRange.min +
|
|
105
|
+
Math.random() * (this.platformWidthRange.max - this.platformWidthRange.min);
|
|
106
|
+
const height = this.platformHeightRange.min +
|
|
107
|
+
Math.random() * (this.platformHeightRange.max - this.platformHeightRange.min);
|
|
108
|
+
elements.push({
|
|
109
|
+
x: currentX + width / 2, // Center position
|
|
110
|
+
width: width,
|
|
111
|
+
height: height,
|
|
112
|
+
type: 'platform'
|
|
113
|
+
});
|
|
114
|
+
currentX += width + this.minSpacing; // Move past this platform plus spacing
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// Generate obstacle with random dimensions
|
|
118
|
+
const width = LEVEL_CONFIG.OBSTACLE_WIDTH_RANGE.min +
|
|
119
|
+
Math.random() * (LEVEL_CONFIG.OBSTACLE_WIDTH_RANGE.max - LEVEL_CONFIG.OBSTACLE_WIDTH_RANGE.min);
|
|
120
|
+
const height = this.obstacleHeightRange.min +
|
|
121
|
+
Math.random() * (this.obstacleHeightRange.max - this.obstacleHeightRange.min);
|
|
122
|
+
elements.push({
|
|
123
|
+
x: currentX + width / 2, // Center position
|
|
124
|
+
width: width,
|
|
125
|
+
height: height,
|
|
126
|
+
type: 'obstacle'
|
|
127
|
+
});
|
|
128
|
+
currentX += width + this.minSpacing; // Move past this obstacle plus spacing
|
|
129
|
+
}
|
|
130
|
+
// Alternate between platform and obstacle
|
|
131
|
+
isPlatform = !isPlatform;
|
|
132
|
+
}
|
|
133
|
+
return elements;
|
|
134
|
+
}
|
|
135
|
+
createElementActor(element) {
|
|
136
|
+
const geometry = new THREE.BoxGeometry(element.width, element.height, LEVEL_CONFIG.DEPTH);
|
|
137
|
+
const material = new THREE.MeshStandardMaterial({ color: element.type === 'platform' ? COLORS.PLATFORM : COLORS.OBSTACLE });
|
|
138
|
+
const meshComponent = new ENGINE.MeshComponent({
|
|
139
|
+
geometry: geometry,
|
|
140
|
+
material: material,
|
|
141
|
+
position: new THREE.Vector3(element.x, element.height / 2, 0), // Position at ground level
|
|
142
|
+
physicsOptions: {
|
|
143
|
+
enabled: true,
|
|
144
|
+
motionType: ENGINE.PhysicsMotionType.Static,
|
|
145
|
+
collisionProfile: ENGINE.DefaultCollisionProfile.BlockAll,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
// Enable shadow casting and receiving
|
|
149
|
+
meshComponent.castShadow = true;
|
|
150
|
+
meshComponent.receiveShadow = true;
|
|
151
|
+
return new ENGINE.Actor({
|
|
152
|
+
rootComponent: meshComponent,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
createChunkDebugVisualization(startX) {
|
|
156
|
+
// Create a wireframe box to visualize chunk boundaries
|
|
157
|
+
const chunkGeometry = new THREE.BoxGeometry(this.chunkSize, LEVEL_CONFIG.DEBUG_VISUALIZATION_HEIGHT, LEVEL_CONFIG.DEPTH);
|
|
158
|
+
const chunkMaterial = new THREE.MeshBasicMaterial({
|
|
159
|
+
color: LEVEL_CONFIG.DEBUG_VISUALIZATION_COLOR,
|
|
160
|
+
opacity: LEVEL_CONFIG.DEBUG_VISUALIZATION_OPACITY,
|
|
161
|
+
wireframe: true,
|
|
162
|
+
transparent: true,
|
|
163
|
+
});
|
|
164
|
+
const 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
|
+
}),
|
|
173
|
+
});
|
|
174
|
+
return chunkBoundary;
|
|
175
|
+
}
|
|
176
|
+
createGroundMeshActor(startX) {
|
|
177
|
+
// Create a ground mesh that spans the entire chunk width
|
|
178
|
+
const groundGeometry = new THREE.BoxGeometry(this.chunkSize, LEVEL_CONFIG.GROUND_HEIGHT, LEVEL_CONFIG.DEPTH);
|
|
179
|
+
const groundMaterial = new THREE.MeshStandardMaterial({
|
|
180
|
+
color: COLORS.GROUND,
|
|
181
|
+
});
|
|
182
|
+
const groundMeshComponent = new ENGINE.MeshComponent({
|
|
183
|
+
geometry: groundGeometry,
|
|
184
|
+
material: groundMaterial,
|
|
185
|
+
position: new THREE.Vector3(startX + this.chunkSize / 2, LEVEL_CONFIG.GROUND_Y_POSITION, 0),
|
|
186
|
+
physicsOptions: {
|
|
187
|
+
enabled: true,
|
|
188
|
+
motionType: ENGINE.PhysicsMotionType.Static,
|
|
189
|
+
collisionProfile: ENGINE.DefaultCollisionProfile.BlockAll,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
// Enable shadow receiving
|
|
193
|
+
groundMeshComponent.receiveShadow = true;
|
|
194
|
+
return new ENGINE.Actor({
|
|
195
|
+
rootComponent: groundMeshComponent,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Gets detailed geometry information at a given x location.
|
|
200
|
+
* @param x - The x coordinate to query
|
|
201
|
+
* @returns An array of geometry info objects at the given x location
|
|
202
|
+
*/
|
|
203
|
+
getGeometryInfoAtX(x) {
|
|
204
|
+
const chunkIndex = this.getChunkIndex(x);
|
|
205
|
+
const chunk = this.levelChunks.get(chunkIndex);
|
|
206
|
+
if (!chunk) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
const geometries = [];
|
|
210
|
+
// Add ground mesh if enabled
|
|
211
|
+
if (this.createGroundMesh) {
|
|
212
|
+
const groundCenterY = LEVEL_CONFIG.GROUND_Y_POSITION;
|
|
213
|
+
const groundHalfHeight = LEVEL_CONFIG.GROUND_HEIGHT / 2;
|
|
214
|
+
geometries.push({
|
|
215
|
+
type: 'ground',
|
|
216
|
+
topY: groundCenterY + groundHalfHeight,
|
|
217
|
+
bottomY: groundCenterY - groundHalfHeight,
|
|
218
|
+
width: this.chunkSize,
|
|
219
|
+
centerX: chunk.startX + this.chunkSize / 2
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
// Add positioned elements (platforms and obstacles)
|
|
223
|
+
const elements = this.generatePositionedElements(chunk.startX);
|
|
224
|
+
for (const element of elements) {
|
|
225
|
+
const elementLeft = element.x - element.width / 2;
|
|
226
|
+
const elementRight = element.x + element.width / 2;
|
|
227
|
+
// Check if the x coordinate falls within this element's bounds
|
|
228
|
+
if (x >= elementLeft && x <= elementRight) {
|
|
229
|
+
geometries.push({
|
|
230
|
+
type: element.type,
|
|
231
|
+
topY: element.height,
|
|
232
|
+
bottomY: 0, // Elements are positioned at ground level (y=0 bottom)
|
|
233
|
+
width: element.width,
|
|
234
|
+
centerX: element.x
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// Sort by top height (highest first)
|
|
239
|
+
geometries.sort((a, b) => b.topY - a.topY);
|
|
240
|
+
return geometries;
|
|
241
|
+
}
|
|
242
|
+
cleanup() {
|
|
243
|
+
for (const index of this.levelChunks.keys()) {
|
|
244
|
+
this.removeChunk(index);
|
|
245
|
+
}
|
|
246
|
+
this.levelChunks.clear();
|
|
247
|
+
console.log('[LevelGenerator] Cleaned up');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import * as ENGINE from 'genesys.js';
|
|
8
|
+
import * as THREE from 'three';
|
|
9
|
+
import { CAMERA_SETTINGS, PLAYER_MOVEMENT } from './const.js';
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// PLAYER PAWN
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// The side-scroller player pawn class using animated character.
|
|
14
|
+
let SideScrollerPlayer = class SideScrollerPlayer extends ENGINE.ThirdPersonCharacterPawn {
|
|
15
|
+
camera;
|
|
16
|
+
characterMesh = null;
|
|
17
|
+
smoothedCameraHeight = null;
|
|
18
|
+
smoothedLookAtHeight = null;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
// Create directional movement component for side-scrolling
|
|
21
|
+
const movementComponent = new ENGINE.DirectionalCharacterMovementComponent({
|
|
22
|
+
...ENGINE.DirectionalCharacterMovementComponent.DEFAULT_OPTIONS,
|
|
23
|
+
direction: ENGINE.CharacterMovementDirection.LeftRight, // Move left and right
|
|
24
|
+
autoMove: false, // Player controls movement manually
|
|
25
|
+
jumpSpeed: PLAYER_MOVEMENT.JUMP_SPEED,
|
|
26
|
+
maxSpeed: PLAYER_MOVEMENT.MAX_SPEED,
|
|
27
|
+
maxMidAirJumps: PLAYER_MOVEMENT.MAX_MID_AIR_JUMPS, // Allow double jump
|
|
28
|
+
// Physics and movement settings for platformer feel
|
|
29
|
+
accelerationLambda: PLAYER_MOVEMENT.ACCELERATION_LAMBDA,
|
|
30
|
+
decelerationLambda: PLAYER_MOVEMENT.DECELERATION_LAMBDA,
|
|
31
|
+
midAirAccelerationLambda: PLAYER_MOVEMENT.MID_AIR_ACCELERATION_LAMBDA,
|
|
32
|
+
midAirDecelerationLambda: PLAYER_MOVEMENT.MID_AIR_DECELERATION_LAMBDA,
|
|
33
|
+
});
|
|
34
|
+
// Physics options for the player
|
|
35
|
+
const physicsOptions = {
|
|
36
|
+
enabled: true,
|
|
37
|
+
motionType: ENGINE.PhysicsMotionType.KinematicVelocityBased,
|
|
38
|
+
collisionProfile: ENGINE.DefaultCollisionProfile.Character,
|
|
39
|
+
generateCollisionEvents: true,
|
|
40
|
+
};
|
|
41
|
+
// Create root component with physics (invisible capsule for collision)
|
|
42
|
+
const rootComponent = new ENGINE.MeshComponent({
|
|
43
|
+
geometry: new THREE.CapsuleGeometry(ENGINE.CHARACTER_WIDTH / 2, ENGINE.CHARACTER_HEIGHT - ENGINE.CHARACTER_WIDTH),
|
|
44
|
+
material: new THREE.MeshStandardMaterial({
|
|
45
|
+
color: 0xffff00,
|
|
46
|
+
visible: false, // Make invisible since we'll have the animated character mesh
|
|
47
|
+
transparent: true,
|
|
48
|
+
opacity: 0.5
|
|
49
|
+
}),
|
|
50
|
+
physicsOptions,
|
|
51
|
+
});
|
|
52
|
+
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
|
+
});
|
|
65
|
+
// Enable shadow casting for the character
|
|
66
|
+
this.rootComponent.castShadow = true;
|
|
67
|
+
this.camera = new THREE.PerspectiveCamera(CAMERA_SETTINGS.FOV, 1, CAMERA_SETTINGS.NEAR, CAMERA_SETTINGS.FAR);
|
|
68
|
+
}
|
|
69
|
+
async doBeginPlay() {
|
|
70
|
+
await super.doBeginPlay();
|
|
71
|
+
this.getWorld()?.setOverrideCamera(this.camera);
|
|
72
|
+
}
|
|
73
|
+
tickPrePhysics(deltaTime) {
|
|
74
|
+
super.tickPrePhysics(deltaTime);
|
|
75
|
+
// manually update the camera position
|
|
76
|
+
const desiredCameraPosition = this.rootComponent.localToWorld(new THREE.Vector3(CAMERA_SETTINGS.POSITION.x, CAMERA_SETTINGS.POSITION.y, CAMERA_SETTINGS.POSITION.z));
|
|
77
|
+
const desiredCameraLookAt = this.rootComponent.localToWorld(new THREE.Vector3(CAMERA_SETTINGS.LOOK_AT.x, CAMERA_SETTINGS.LOOK_AT.y, CAMERA_SETTINGS.LOOK_AT.z));
|
|
78
|
+
this.smoothedCameraHeight ??= desiredCameraPosition.y;
|
|
79
|
+
this.smoothedLookAtHeight ??= desiredCameraLookAt.y;
|
|
80
|
+
this.smoothedCameraHeight = THREE.MathUtils.damp(this.smoothedCameraHeight, desiredCameraPosition.y, CAMERA_SETTINGS.DAMP_FACTOR, deltaTime);
|
|
81
|
+
this.smoothedLookAtHeight = THREE.MathUtils.damp(this.smoothedLookAtHeight, desiredCameraLookAt.y, CAMERA_SETTINGS.DAMP_FACTOR, deltaTime);
|
|
82
|
+
this.camera.position.x = desiredCameraPosition.x;
|
|
83
|
+
this.camera.position.y = this.smoothedCameraHeight;
|
|
84
|
+
this.camera.position.z = desiredCameraPosition.z;
|
|
85
|
+
this.camera.lookAt(new THREE.Vector3(desiredCameraLookAt.x, this.smoothedLookAtHeight, desiredCameraLookAt.z));
|
|
86
|
+
// change the direction of the character mesh based on the movement direction
|
|
87
|
+
this.characterMesh ??= this.getComponent(ENGINE.GLTFMeshComponent);
|
|
88
|
+
if (this.characterMesh && this.movementComponent) {
|
|
89
|
+
const movementComponent = this.movementComponent;
|
|
90
|
+
const velocity = movementComponent.getVelocities();
|
|
91
|
+
if (velocity.right > 0.1) {
|
|
92
|
+
// Moving right - face right (90 degrees)
|
|
93
|
+
this.characterMesh.rotation.set(0, Math.PI / 2, 0);
|
|
94
|
+
}
|
|
95
|
+
else if (velocity.right < -0.1) {
|
|
96
|
+
// Moving left - face left (-90 degrees)
|
|
97
|
+
this.characterMesh.rotation.set(0, -Math.PI / 2, 0);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
SideScrollerPlayer = __decorate([
|
|
103
|
+
ENGINE.GameClass()
|
|
104
|
+
], SideScrollerPlayer);
|
|
105
|
+
export { SideScrollerPlayer };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as ENGINE from 'genesys.js';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { ThirdPersonPlayer } from './player.js';
|
|
4
|
+
import './auto-imports.js';
|
|
5
|
+
class ThirdPersonGame extends ENGINE.BaseGameLoop {
|
|
6
|
+
pawn = null;
|
|
7
|
+
controller = null;
|
|
8
|
+
createLoadingScreen() {
|
|
9
|
+
// enable the default loading screen
|
|
10
|
+
return new ENGINE.DefaultLoadingScreen();
|
|
11
|
+
}
|
|
12
|
+
async preStart() {
|
|
13
|
+
// default spawn location
|
|
14
|
+
const position = new THREE.Vector3(0, ENGINE.CHARACTER_HEIGHT / 2, 0);
|
|
15
|
+
// create the pawn
|
|
16
|
+
this.pawn = new ThirdPersonPlayer({ position });
|
|
17
|
+
// create the controller and possess the pawn
|
|
18
|
+
this.controller = new ENGINE.PlayerController();
|
|
19
|
+
this.controller.possess(this.pawn);
|
|
20
|
+
// add both to the world
|
|
21
|
+
this.world.addActors(this.pawn, this.controller);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function main(container, gameId) {
|
|
25
|
+
const game = new ThirdPersonGame(container, {
|
|
26
|
+
...ENGINE.BaseGameLoop.DEFAULT_OPTIONS,
|
|
27
|
+
gameId
|
|
28
|
+
});
|
|
29
|
+
return game;
|
|
30
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import * as ENGINE from 'genesys.js';
|
|
8
|
+
import * as THREE from 'three';
|
|
9
|
+
/**
|
|
10
|
+
* A third person player class.
|
|
11
|
+
*
|
|
12
|
+
* Key points:
|
|
13
|
+
* - No need to provide movementComponent and camera, they are created internally
|
|
14
|
+
* - The pawn is set to be transient so it's never saved in the level
|
|
15
|
+
* - The directional light follows the player for consistent shadows
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
let ThirdPersonPlayer = class ThirdPersonPlayer extends ENGINE.ThirdPersonCharacterPawn {
|
|
19
|
+
// Omit all the options that are created internally
|
|
20
|
+
constructor(options) {
|
|
21
|
+
// simple camera component - contains a perspective camera by default
|
|
22
|
+
const camera = new THREE.PerspectiveCamera(ENGINE.CAMERA_FOV, 1, 0.1, 1000);
|
|
23
|
+
// set camera position for third person view
|
|
24
|
+
camera.position.set(0, ENGINE.CHARACTER_HEIGHT * 1.3, ENGINE.CHARACTER_HEIGHT * 2);
|
|
25
|
+
camera.lookAt(0, 0, 0);
|
|
26
|
+
// use capsule root component for collision
|
|
27
|
+
const rootComponent = new ENGINE.MeshComponent({
|
|
28
|
+
geometry: ENGINE.GameBuilder.createDefaultPawnCapsuleGeometry(),
|
|
29
|
+
material: new THREE.MeshStandardMaterial({ color: ENGINE.Color.YELLOW, visible: false, transparent: true, opacity: 0.5 }),
|
|
30
|
+
physicsOptions: {
|
|
31
|
+
enabled: true,
|
|
32
|
+
// KinematicVelocityBased is required to use the physics character controller
|
|
33
|
+
motionType: ENGINE.PhysicsMotionType.KinematicVelocityBased,
|
|
34
|
+
collisionProfile: ENGINE.DefaultCollisionProfile.Character,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
// use third person movement mechanics
|
|
38
|
+
const movementComponent = new ENGINE.CharacterMovementComponent({
|
|
39
|
+
...ENGINE.CharacterMovementComponent.DEFAULT_OPTIONS,
|
|
40
|
+
movementType: ENGINE.CharacterMovementType.ThirdPerson,
|
|
41
|
+
});
|
|
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
|
+
});
|
|
56
|
+
// set the pawn to be transient so it's never saved in the level
|
|
57
|
+
this.setTransient(true);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
ThirdPersonPlayer = __decorate([
|
|
61
|
+
ENGINE.GameClass()
|
|
62
|
+
], ThirdPersonPlayer);
|
|
63
|
+
export { ThirdPersonPlayer };
|