Last active
December 9, 2023 03:07
-
-
Save chientrm/a0ae90aede69975f1ffd6cb7ad2c229b to your computer and use it in GitHub Desktop.
Reconcile glitch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { PlayerController } from '@chientrm/game'; | |
import type { GameScene } from './scene'; | |
export class Player { | |
ship: Phaser.GameObjects.Sprite; | |
flame: Phaser.GameObjects.Sprite; | |
controller: PlayerController; | |
anim: string; | |
constructor(scene: GameScene, controller: PlayerController, slot: number) { | |
this.controller = controller; | |
this.ship = scene.add.sprite(controller.schema.x, 550, 'Ships'); | |
this.ship.scale = 4; | |
this.anim = 'middle'; | |
this.ship.play(`${slot}-${this.anim}`); | |
this.flame = scene.add.sprite(controller.schema.x, 550 + 7 * 4, 'Misc'); | |
this.flame.scale = 4; | |
this.flame.play('flame'); | |
controller.onUpdate((state) => { | |
this.ship.x = this.flame.x = state.x; | |
if (this.anim !== state.anim) { | |
this.anim = state.anim; | |
this.ship.play(`${slot}-${this.anim}`); | |
} | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ToJSON } from '@colyseus/schema/lib/types/HelperTypes'; | |
import { CLIENT_TIMESTEP, MAP_WIDTH, SHIP_VX } from '../constants'; | |
import { Anim, InputPayload, PlayerSchema } from '../schemas/player'; | |
type PlayerState = ToJSON<PlayerSchema>; | |
type Callback = (state: PlayerState) => void; | |
export class PlayerController { | |
callback?: Callback; | |
inputPayloads: InputPayload[]; | |
momentum: Anim; | |
sessionId: string; | |
schema: PlayerSchema; | |
constructor(schema: PlayerSchema, sessionId?: string) { | |
this.schema = schema; | |
this.sessionId = sessionId; | |
this.inputPayloads = []; | |
this.shootTick = 0; | |
schema.onChange(() => { | |
if (sessionId === schema.sessionId) { | |
this.reconcile(schema); | |
} else { | |
this.momentum = schema.anim; | |
this.update(() => {}); | |
} | |
}); | |
} | |
update(f: (state: PlayerState) => void) { | |
const state = this.schema.clone().toJSON(); | |
f(state); | |
if ( | |
state.x !== this.schema.x || | |
state.anim !== this.schema.anim || | |
state.tick !== this.schema.tick | |
) { | |
this.schema.x = state.x; | |
this.schema.anim = state.anim; | |
this.schema.tick = state.tick; | |
if (this.callback) this.callback(state); | |
} | |
} | |
tick(dt: number): void { | |
this.update((state) => { | |
if (this.sessionId) { | |
if (this.momentum === 'left') { | |
this.step(state, { left: true, right: false, tick: 0 }); | |
} else if (this.momentum === 'right') { | |
this.step(state, { left: false, right: true, tick: 0 }); | |
} | |
} else { | |
let inputPayload: InputPayload; | |
while ((inputPayload = this.inputPayloads.shift())) | |
this.step(state, inputPayload); | |
} | |
}); | |
} | |
step(state: PlayerState, inputPayload: InputPayload) { | |
let anim = state.anim; | |
let x = state.x; | |
const { left, right, tick } = inputPayload; | |
if (left) { | |
anim = 'left'; | |
x = Math.max(0, x - SHIP_VX * CLIENT_TIMESTEP); | |
} else if (right) { | |
anim = 'right'; | |
x = Math.min(MAP_WIDTH, x + SHIP_VX * CLIENT_TIMESTEP); | |
} else { | |
anim = 'middle'; | |
} | |
state.x = x; | |
state.anim = anim; | |
state.tick = tick; | |
} | |
onUpdate(callback: Callback) { | |
this.callback = callback; | |
} | |
reconcile(schema: PlayerSchema) { | |
while ( | |
this.inputPayloads.length > 0 && | |
this.inputPayloads[0].tick <= schema.tick | |
) { | |
this.inputPayloads.shift(); | |
} | |
this.update((state) => { | |
this.inputPayloads.forEach((inputPayload) => | |
this.step(state, inputPayload) | |
); | |
}); | |
} | |
input(inputPayload: InputPayload) { | |
this.inputPayloads.push(inputPayload); | |
if (this.sessionId) { | |
this.update((state) => this.step(state, inputPayload)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Schema, type } from '@colyseus/schema'; | |
import { MAP_WIDTH } from '../constants'; | |
export class InputPayload { | |
left: boolean; | |
right: boolean; | |
tick: number; | |
} | |
export type Anim = 'middle' | 'left' | 'right'; | |
export class PlayerSchema extends Schema { | |
@type('number') x = Math.random() * MAP_WIDTH; | |
@type('number') tick = 0; | |
@type('string') sessionId?: string; | |
@type('string') anim: Anim = 'middle'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment