Skip to content

Commit

Permalink
feat: refactor into resource pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
phisn committed Sep 28, 2024
1 parent 6e20a21 commit 632e41e
Show file tree
Hide file tree
Showing 27 changed files with 475 additions and 436 deletions.
1 change: 0 additions & 1 deletion CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

## Now

- I renamed web-game to game-player. Have to fix everything upon that.
- We wanted to extract reset functionality into game-web. Game should stay more pure. Especially since we have the bug that camera does not reset on reset call. For this also take a look if player in web should actually have such deep access. ALl sketchy.

## Next
Expand Down
1 change: 1 addition & 0 deletions packages/game-player/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"@dimforge/rapier2d": "^0.14",
"@petamoriken/float16": "^3.8.7",
"eslint-config-custom": "*",
"game": "*",
"lil-gui": "^0.19.2",
Expand Down
17 changes: 7 additions & 10 deletions packages/game-player/src/game-player-loop.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { GameInterface } from "./game-player"
export interface GameLoopRunnable {
onFixedUpdate(last: boolean): void
onUpdate(delta: number, overstep: number): void
}

export class GameLoop {
private animationFrame: number | undefined
Expand All @@ -7,7 +10,7 @@ export class GameLoop {

private tickrate = 1000 / 60

constructor(private game: GameInterface) {
constructor(private runnable: GameLoopRunnable) {
this.tick = this.tick.bind(this)
}

Expand All @@ -22,10 +25,6 @@ export class GameLoop {
}
}

public getGame() {
return this.game
}

private tick(time: DOMHighResTimeStamp) {
this.animationFrame = requestAnimationFrame(this.tick)

Expand All @@ -37,13 +36,11 @@ export class GameLoop {
this.loop_time += delta
this.previous_time = time

this.game.onPreFixedUpdate(delta)

while (this.loop_time > this.tickrate) {
this.loop_time -= this.tickrate
this.game.onFixedUpdate(this.loop_time <= this.tickrate)
this.runnable.onFixedUpdate(this.loop_time <= this.tickrate)
}

this.game.onUpdate(delta, this.loop_time / this.tickrate)
this.runnable.onUpdate(delta, this.loop_time / this.tickrate)
}
}
143 changes: 40 additions & 103 deletions packages/game-player/src/game-player.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,25 @@
import { ReplayCaptureService } from "game/src/model/replay/replay-capture-service"
import {
Color,
Mesh,
MeshBasicMaterial,
Object3D,
Shape,
ShapeGeometry,
Vector2,
Vector3,
WebGLRenderer,
} from "three"
import { Color, WebGLRenderer } from "three"
import { GameLoopRunnable } from "./game-player-loop"
import { GameSettings } from "./model/settings"
import { GamePlayerStore } from "./model/store"
import { ModuleCamera } from "./modules/module-camera"
import { ModuleHookHandler } from "./modules/module-hook-handler"
import { ModuleInput } from "./modules/module-input/module-input"
import { ModuleInterpolation } from "./modules/module-interpolation"
import { ModuleLobby } from "./modules/module-lobby/module-lobby"
import { ModuleParticles } from "./modules/module-particles/module-particles"
import { ModuleUI } from "./modules/module-ui/module-ui"
import { ModuleVisual } from "./modules/module-visual/module-visual"

export interface GameInterface {
dispose(): void
export class WebGame implements GameLoopRunnable {
public store: GamePlayerStore

onPreFixedUpdate(delta: number): void
onFixedUpdate(last: boolean): void
onUpdate(delta: number, overstep: number): void
}

export class WebGame implements GameInterface {
store: GamePlayerStore

replayCapture: ReplayCaptureService

moduleCamera: ModuleCamera
moduleHooks: ModuleHookHandler
moduleInput: ModuleInput
moduleLobby?: ModuleLobby
moduleParticles: ModuleParticles
moduleUI: ModuleUI
moduleVisual: ModuleVisual

private test: Object3D
private moduleCamera: ModuleCamera
private moduleInput: ModuleInput
private moduleInterpolation: ModuleInterpolation
private moduleLobby?: ModuleLobby
private moduleParticles: ModuleParticles
private moduleUI: ModuleUI
private moduleVisual: ModuleVisual

constructor(settings: GameSettings) {
const renderer = new WebGLRenderer({
Expand All @@ -54,107 +32,66 @@ export class WebGame implements GameInterface {

this.store = new GamePlayerStore(settings, renderer)

this.replayCapture = new ReplayCaptureService()

this.moduleCamera = new ModuleCamera(this.store)
this.moduleHooks = new ModuleHookHandler(this.store)
this.moduleInput = new ModuleInput(this.store)
this.moduleInterpolation = new ModuleInterpolation(this.store)
this.moduleParticles = new ModuleParticles(this.store)
this.moduleUI = new ModuleUI(this.store)
this.moduleVisual = new ModuleVisual(this.store)

if (settings.instanceType === "play" && settings.lobby) {
this.moduleLobby = new ModuleLobby(this.store)
}

this.onCanvasResize = this.onCanvasResize.bind(this)
const observer = new ResizeObserver(this.onCanvasResize)
observer.observe(renderer.domElement)

// red rectangle
this.test = new Mesh(
new ShapeGeometry(
new Shape([
new Vector2(-1, -1),
new Vector2(1, -1),
new Vector2(1, 1),
new Vector2(-1, 1),
]),
),
new MeshBasicMaterial({ color: 0xff0000 }),
)
this.test.position.z = 1

this.store.scene.add(this.test)
}

dispose() {
this.moduleInput.dispose()
this.store.renderer.dispose()
this.moduleLobby?.dispose()
}

private onCanvasResize() {
const width = this.store.renderer.domElement.clientWidth
const height = this.store.renderer.domElement.clientHeight
onDispose() {
const renderer = this.store.resources.get("renderer")
renderer.dispose()

this.moduleCamera.onCanvasResize(width, height)
this.moduleInput.onDispose()
this.moduleLobby?.onDispose()
}

onPreFixedUpdate(delta: number) {
this.moduleInput.onPreFixedUpdate(delta)
onReset() {
this.store.game.onReset()

this.moduleCamera.onReset()
this.moduleInput.onReset()
}

onFixedUpdate(last: boolean) {
this.moduleInput.onFixedUpdate()

const input = {
thrust: this.moduleInput.thrust(),
rotation: this.moduleInput.rotation(),
}

const rotationWithError = this.replayCapture.captureFrame(input)
input.rotation = rotationWithError

this.store.game.onUpdate(input)

if (last) {
this.store.interpolation.onLastFixedUpdate()
}
this.tickGame()
this.moduleInterpolation.onFixedUpdate(last)

this.moduleLobby?.onFixedUpdate()
this.moduleUI.onFixedUpdate()
}

onUpdate(delta: number, overstep: number) {
const vec = new Vector3() // create once and reuse

vec.set(
(this.moduleInput.mouse.x / window.innerWidth) * 2 - 1,
-(this.moduleInput.mouse.y / window.innerHeight) * 2 + 1,
0.5,
)

vec.unproject(this.moduleCamera)

this.test.position.x = vec.x - 1
this.test.position.y = vec.y - 1

this.store.interpolation.onUpdate(delta, overstep)
this.moduleInterpolation.onUpdate(overstep)

this.moduleCamera.onUpdate(delta)
this.moduleLobby?.onUpdate(overstep)
this.moduleParticles.update(delta)
this.moduleVisual.onUpdate()
this.moduleLobby?.onUpdate(overstep)
this.moduleCamera.onUpdate(delta)

this.store.renderer.clear()
this.render()

this.moduleCamera.updateViewport()
this.store.renderer.render(this.store.scene, this.moduleCamera)
this.moduleUI.onUpdate()
}

domElement() {
return this.store.renderer.domElement
private tickGame() {
const inputCapture = this.store.resources.get("inputCapture")
this.store.game.onUpdate(inputCapture.currentInput)
}

private render() {
const renderer = this.store.resources.get("renderer")
const scene = this.store.resources.get("scene")

renderer.clear()
renderer.render(scene, this.moduleCamera)
}
}
16 changes: 6 additions & 10 deletions packages/game-player/src/model/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ import { EventStore } from "game/src/framework/event"
import { ResourceStore } from "game/src/framework/resource"
import { Game } from "game/src/game"
import { Scene, WebGLRenderer } from "three"
import { InterpolationStore } from "./interpolation"
import { InputCaptureResource } from "../modules/module-input/module-input"
import { InterpolationResource } from "../modules/module-interpolation"
import { VisualsResource } from "../modules/module-visual/module-visual"
import { GameSettings } from "./settings"

export class GamePlayerStore {
public interpolation: InterpolationStore
public renderer: WebGLRenderer
public scene: Scene

public events: EventStore<GamePlayerEvents>
public game: Game
public resources: ResourceStore<GamePlayerResources>
Expand All @@ -25,17 +23,15 @@ export class GamePlayerStore {
renderer,
scene: new Scene(),
})

this.interpolation = new InterpolationStore(this.game)
this.scene = new Scene()
this.renderer = renderer
}
}

export interface GamePlayerResources {
inputCapture: InputCaptureResource
interpolation: InterpolationResource
renderer: WebGLRenderer
scene: Scene
interpolation: InterpolationStore
visuals: VisualsResource
}

export interface GamePlayerEvents {}
Loading

0 comments on commit 632e41e

Please sign in to comment.