Skip to content

Commit dd4d9c6

Browse files
authored
feat: re-implement game loop in World and expose hooks as EE (#168)
1 parent a5c1817 commit dd4d9c6

File tree

8 files changed

+319
-45
lines changed

8 files changed

+319
-45
lines changed

.changeset/jolly-bushes-find.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@jolly-pixel/runtime": minor
3+
"@jolly-pixel/engine": minor
4+
---
5+
6+
Re-implement part of the game loop into World and expose hooks as EE

packages/engine/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ import { Systems, Actor } from "@jolly-pixel/engine";
6666

6767
const sceneManager = new Systems.SceneManager();
6868
const renderer = new Systems.ThreeRenderer(canvas, {
69-
scene: sceneManager,
69+
sceneManager,
7070
renderMode: "direct"
7171
});
7272
const game = new Systems.World(renderer, {

packages/engine/docs/systems/world.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121

2222
const canvas = document.querySelector("canvas")!;
2323
const sceneManager = new SceneManager();
24-
const renderer = new ThreeRenderer(canvas, { scene: sceneManager });
24+
const renderer = new ThreeRenderer(canvas, { sceneManager });
2525

2626
const game = new World(renderer, { sceneManager });
2727
```

packages/engine/src/systems/World.ts

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Import Third-party Dependencies
22
import * as THREE from "three";
3+
import { EventEmitter } from "@posva/event-emitter";
34

45
// Import Internal Dependencies
56
import {
@@ -18,6 +19,14 @@ import {
1819
type GlobalsAdapter,
1920
BrowserGlobalsAdapter
2021
} from "../adapters/global.ts";
22+
import { FixedTimeStep } from "./FixedTimeStep.ts";
23+
24+
export type WorldEvents = {
25+
beforeFixedUpdate: [number];
26+
afterFixedUpdate: [number];
27+
beforeUpdate: [number];
28+
afterUpdate: [number];
29+
};
2130

2231
export interface WorldOptions<
2332
TContext = WorldDefaultContext
@@ -39,18 +48,21 @@ export interface WorldDefaultContext {
3948
export class World<
4049
T = THREE.WebGLRenderer,
4150
TContext = WorldDefaultContext
42-
> {
51+
> extends EventEmitter<WorldEvents> {
4352
renderer: Renderer<T>;
4453
input: Input;
4554
loadingManager: THREE.LoadingManager = new THREE.LoadingManager();
4655
sceneManager: SceneContract;
4756
audio: GlobalAudio;
4857
context: TContext;
58+
loop: FixedTimeStep;
4959

5060
constructor(
5161
renderer: Renderer<T>,
5262
options: WorldOptions<TContext>
5363
) {
64+
super();
65+
5466
const {
5567
sceneManager,
5668
input = new Input(renderer.canvas, { enableOnExit: options.enableOnExit ?? false }),
@@ -64,6 +76,7 @@ export class World<
6476
this.input = input;
6577
this.audio = audio;
6678
this.context = context;
79+
this.loop = new FixedTimeStep();
6780

6881
globalsAdapter.setGame(this);
6982
}
@@ -101,24 +114,54 @@ export class World<
101114
return this;
102115
}
103116

104-
beginFrame() {
105-
this.input.update();
106-
this.sceneManager.beginFrame();
117+
start() {
118+
this.loop.start();
119+
120+
return this;
107121
}
108122

109-
fixedUpdate(
110-
deltaTime: number
111-
) {
112-
this.sceneManager.fixedUpdate(deltaTime);
123+
stop() {
124+
this.loop.stop();
125+
126+
return this;
113127
}
114128

115-
update(
116-
deltaTime: number
129+
setFps(
130+
fps: number,
131+
fixedFps?: number
117132
) {
118-
this.sceneManager.update(deltaTime);
133+
this.loop.setFps(fps, fixedFps);
134+
135+
return this;
136+
}
137+
138+
tick() {
139+
this.#beginFrame();
140+
this.loop.tick({
141+
fixedUpdate: (fixedDelta) => {
142+
const dt = fixedDelta / 1000;
143+
this.emit("beforeFixedUpdate", dt);
144+
this.sceneManager.fixedUpdate(dt);
145+
this.emit("afterFixedUpdate", dt);
146+
},
147+
update: (_interpolation, delta) => {
148+
const dt = delta / 1000;
149+
this.emit("beforeUpdate", dt);
150+
this.sceneManager.update(dt);
151+
this.renderer.draw();
152+
this.emit("afterUpdate", dt);
153+
}
154+
});
155+
156+
return this.#endFrame();
119157
}
120158

121-
endFrame(): boolean {
159+
#beginFrame() {
160+
this.input.update();
161+
this.sceneManager.beginFrame();
162+
}
163+
164+
#endFrame(): boolean {
122165
this.sceneManager.endFrame();
123166

124167
if (this.input.exited) {
@@ -129,8 +172,4 @@ export class World<
129172

130173
return false;
131174
}
132-
133-
render() {
134-
this.renderer.draw();
135-
}
136175
}

packages/engine/src/systems/rendering/ThreeRenderer.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface ThreeRendererOptions {
2525
* @default "direct"
2626
*/
2727
renderMode: RenderMode;
28-
scene: SceneContract;
28+
sceneManager: SceneContract;
2929
}
3030

3131
export class ThreeRenderer extends EventEmitter<
@@ -35,7 +35,7 @@ export class ThreeRenderer extends EventEmitter<
3535
renderComponents: RenderComponent[] = [];
3636
renderStrategy: RenderStrategy;
3737
ratio: number | null = null;
38-
scene: SceneContract;
38+
sceneManager: SceneContract;
3939

4040
#resizeObserver: ResizeObserver | null = null;
4141
#pendingResizeWidth = 0;
@@ -47,9 +47,9 @@ export class ThreeRenderer extends EventEmitter<
4747
options: ThreeRendererOptions
4848
) {
4949
super();
50-
const { scene, renderMode = "direct" } = options;
50+
const { sceneManager, renderMode = "direct" } = options;
5151

52-
this.scene = scene;
52+
this.sceneManager = sceneManager;
5353
this.webGLRenderer = createWebGLRenderer(canvas);
5454
this.setRenderMode(renderMode);
5555
}
@@ -67,7 +67,7 @@ export class ThreeRenderer extends EventEmitter<
6767
): void {
6868
this.renderComponents.push(component);
6969
if (this.renderStrategy instanceof ComposerRenderStrategy) {
70-
const renderPass = new RenderPass(this.scene.getSource(), component);
70+
const renderPass = new RenderPass(this.sceneManager.getSource(), component);
7171
this.renderStrategy.addEffect(renderPass);
7272
}
7373
}
@@ -98,7 +98,7 @@ export class ThreeRenderer extends EventEmitter<
9898
else {
9999
const composer = new EffectComposer(this.webGLRenderer);
100100

101-
const scene = this.scene.getSource();
101+
const scene = this.sceneManager.getSource();
102102
for (const renderComponent of this.renderComponents) {
103103
const renderPass = new RenderPass(scene, renderComponent);
104104
composer.addPass(renderPass);
@@ -213,7 +213,7 @@ export class ThreeRenderer extends EventEmitter<
213213
this.clear();
214214

215215
this.renderStrategy.render(
216-
this.scene.getSource(),
216+
this.sceneManager.getSource(),
217217
this.renderComponents
218218
);
219219
this.emit("draw", { source: this.webGLRenderer });

0 commit comments

Comments
 (0)