diff options
Diffstat (limited to 'src/controller')
-rw-r--r-- | src/controller/collision.ts | 65 | ||||
-rw-r--r-- | src/controller/entitycontext.ts | 47 | ||||
-rw-r--r-- | src/controller/gamecontext.ts | 174 |
3 files changed, 0 insertions, 286 deletions
diff --git a/src/controller/collision.ts b/src/controller/collision.ts deleted file mode 100644 index a44ffa4..0000000 --- a/src/controller/collision.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Collision } from '../model/data/collision'; - -import { Circle } from '../math/circle'; -import { Collidable } from '../math/collision'; -import { LineSegment, Movement } from '../math/line'; -import { Point } from '../math/point'; - -import { vec2 } from 'gl-matrix'; - -export function mkCollision(collision: Collision[]): Collidable[] { - const ret: Collidable[] = []; - - for (const c of collision) { - switch (c.type) { - case 'polygon': - if (!c.vertices.length) - continue; - - let prev = c.vertices[c.vertices.length - 1]; - - for (const v of c.vertices) { - ret.push(LineSegment.fromPoints(vec2.clone(prev), vec2.clone(v))); - prev = v; - } - - for (const v of c.vertices) { - ret.push(new Point(vec2.clone(v))); - prev = v; - } - break; - - case 'circle': - ret.push(new Circle(vec2.clone(c.center), c.radius)); - break; - } - } - - return ret; -} - -export interface CollidableGroup { - getTranslation(): vec2|null; - getCollidables(): Collidable[]; -} - -export function collide(collision: CollidableGroup, out: vec2, move: Movement, radius: number): boolean { - const t = collision.getTranslation(); - if (t) - move = move.translate(vec2.negate(vec2.create(), t)); - - for (const c of collision.getCollidables()) { - if (!c.collide(out, move, radius)) - continue; - - if (vec2.squaredDistance(move.src, out) >= vec2.squaredDistance(move.src, move.dest)) - continue; - - if (t) - vec2.add(out, out, t); - - return true; - } - - return false; -} diff --git a/src/controller/entitycontext.ts b/src/controller/entitycontext.ts deleted file mode 100644 index 434bf9b..0000000 --- a/src/controller/entitycontext.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { EntityView } from '../view/entity'; -import { Renderer } from '../view/renderer/renderer'; - -import { Collidable } from '../math/collision'; - -import { CollidableGroup, mkCollision } from './collision'; - -import { vec2 } from 'gl-matrix'; - -export class EntityContext implements CollidableGroup { - public static async load(renderer: Renderer, name: string): Promise<EntityContext> { - return new EntityContext( - renderer, - name, - await EntityView.load(renderer, name), - ); - } - - public readonly pos: vec2 = vec2.create(); - - private readonly collision: Collidable[]; - - private constructor( - private readonly renderer: Renderer, - private readonly name: string, - private readonly view: EntityView, - ) { - this.collision = mkCollision(view.data.collision); - } - - public render(time: number) { - this.renderer.setTranslation(this.pos); - this.view.renderByTime(time); - } - - public getTranslation(): vec2 { - return this.pos; - } - - public getCollidables(): Collidable[] { - return this.collision; - } - - public interact() { - alert(`You've interacted with ${this.name}!`); - } -} diff --git a/src/controller/gamecontext.ts b/src/controller/gamecontext.ts deleted file mode 100644 index 9a68e25..0000000 --- a/src/controller/gamecontext.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { CollidableGroup, collide, mkCollision } from './collision'; -import { EntityContext } from './entitycontext'; - -import { MapData } from '../model/data/map'; - -import { ButtonCode, GameInputHandler } from '../view/input/gameinput'; -import { MapView } from '../view/map'; -import { Renderer } from '../view/renderer/renderer'; - -import { Collidable } from '../math/collision'; -import { Movement } from '../math/line'; -import { getJSON, nextAnimationFrame } from '../util'; - -import { vec2 } from 'gl-matrix'; - -export class GameContext implements CollidableGroup { - public static async load(renderer: Renderer): Promise<GameContext> { - const map = this.loadMap(renderer, 'test'); - const loadPlayer = EntityContext.load(renderer, 'green_circle'); - const loadEntity = EntityContext.load(renderer, 'red_circle'); - - const [mapView, mapCollision] = await map; - const player = await loadPlayer; - const entity = await loadEntity; - - vec2.set(player.pos, 7, 6); - vec2.set(entity.pos, 4, 3); - - return new GameContext( - renderer, - mapView, - player, - [entity], - mapCollision, - ); - } - - private static async loadMap(renderer: Renderer, name: string): Promise<[MapView, Collidable[]]> { - const map = new MapData(await getJSON(`resources/map/${name}.json`)); - return [await MapView.load(renderer, map), mkCollision(map.collision)]; - } - - private readonly initTime: number = performance.now(); - private time = 0; - - private readonly tick = 10; // ms per tick - private readonly maxSpeed = 0.04; // movement per tick - private readonly maxSkip = 20; // maximum ticks to process in a single render step - - private readonly input: GameInputHandler; - - private readonly playerDir: vec2 = vec2.fromValues(0, 1); - private speed: number = 0; - - private readonly collisionRadius = 15 / 32; - private readonly interactLength = 1 / 32; - - private constructor( - private readonly renderer: Renderer, - private readonly mapView: MapView, - private readonly player: EntityContext, - private readonly entities: EntityContext[], - private readonly collision: Collidable[], - ) { - this.input = new GameInputHandler(); - this.input.addListener((input) => { - switch (input.type) { - case 'button': - if (input.button === ButtonCode.Action) - this.interact(); - break; - - case 'direction': - if (vec2.sqrLen(input.direction) > 0) { - vec2.copy(this.playerDir, input.direction); - this.speed = this.maxSpeed; - } else { - this.speed = 0; - } - break; - } - }); - - this.renderLoop(); - } - - public getTranslation(): null { - return null; - } - - public getCollidables(): Collidable[] { - return this.collision; - } - - private updateTime(time: number): number { - const diff = Math.round(time / this.tick) - Math.round(this.time / this.tick); - this.time = time; - - return diff; - } - - private canInteract(c: CollidableGroup): boolean { - const dest = vec2.scaleAndAdd(vec2.create(), this.player.pos, this.playerDir, this.interactLength); - const move = new Movement(this.player.pos, dest); - - return collide(c, vec2.create(), move, this.collisionRadius); - } - - private interact(): void { - for (const e of this.entities) { - if (!this.canInteract(e)) - continue; - - e.interact(); - break; - } - } - - private updateStepCollide(out: vec2, dest: vec2): boolean { - const move = new Movement(this.player.pos, dest); - - for (const c of [this, ...this.entities]) { - if (collide(c, out, move, this.collisionRadius)) { - if (vec2.squaredDistance(move.src, out) >= vec2.squaredDistance(move.src, move.dest)) - continue; - - return true; - } - } - - return false; - } - - private updateStep(): void { - const dest = vec2.scaleAndAdd(vec2.create(), this.player.pos, this.playerDir, this.speed); - const newDest = vec2.create(); - - while (this.updateStepCollide(newDest, dest)) { - if (vec2.equals(newDest, this.player.pos)) - return; - - vec2.copy(dest, newDest); - } - - vec2.copy(this.player.pos, dest); - } - - private update(time: number): void { - const diff = Math.min(this.maxSkip, this.updateTime(time)); - - if (!this.speed) { - this.renderer.snapToGrid(this.player.pos, this.player.pos); - return; - } - - for (let i = 0; i < diff; i++) - this.updateStep(); - } - - private render(): void { - this.renderer.setCenter(this.player.pos); - this.renderer.clear(); - - for (const r of [this.mapView, ...this.entities, this.player]) - r.render(this.time); - } - - private async renderLoop(): Promise<void> { - while (true) { - this.update(await nextAnimationFrame() - this.initTime); - this.render(); - } - } -} |