This commit is contained in:
schmelczerandras 2020-08-04 22:07:39 +02:00
commit 345e183e34
30 changed files with 614 additions and 173 deletions

View file

@ -6,8 +6,26 @@ import './styles/main.scss';
glMatrix.setMatrixArrayType(Array);
applyArrayPlugins();
/*
const tree = new BoundingBoxTree([
new BoundingBox(300, 550, 150, 550, 'A'),
new BoundingBox(400, 800, 50, 200, 'B'),
new BoundingBox(450, 500, 175, 185, 'C'),
new BoundingBox(100, 200, 100, 500, 'D'),
new BoundingBox(750, 950, 450, 600, 'E'),
new BoundingBox(940, 1000, -2, 180, 'F'),
new BoundingBox(960, 1050, 50, 190, 'G'),
new BoundingBox(150, 900, 0, 575, 'H'),
new BoundingBox(-10000, 10000, -10000, 10000, 'I'),
]);
tree.print();
console.log(tree.findIntersecting(new BoundingBox(960, 1050, 50, 190, 'G')));
*/
try {
new Game();
} catch (e) {
console.error(e);
alert(e);
}

View file

@ -1,7 +1,11 @@
import { vec2 } from 'gl-matrix';
import { ImmutableBoundingBox } from '../../physics/containers/immutable-bounding-box';
import { GameObject } from '../../objects/game-object';
export interface IDrawable {
serializeToUniforms(uniforms: any): void;
distance(target: vec2): number;
minimumDistance(target: vec2): number;
readonly owner: GameObject;
readonly boundingBox: ImmutableBoundingBox;
}

View file

@ -2,6 +2,8 @@ import { ILight } from './i-light';
import { vec2, vec3 } from 'gl-matrix';
import { settings } from '../../settings';
import { IDrawableDescriptor } from '../i-drawable-descriptor';
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
import { GameObject } from '../../../objects/game-object';
export class CircleLight implements ILight {
public static descriptor: IDrawableDescriptor = {
@ -11,12 +13,15 @@ export class CircleLight implements ILight {
};
constructor(
public readonly owner: GameObject,
public center: vec2,
public radius: number,
public color: vec3,
public lightness: number
) {}
boundingBox: ImmutableBoundingBox;
public distance(target: vec2): number {
return 0;
}

View file

@ -2,6 +2,8 @@ import { ILight } from './i-light';
import { vec2, vec3 } from 'gl-matrix';
import { IDrawableDescriptor } from '../i-drawable-descriptor';
import { settings } from '../../settings';
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
import { GameObject } from '../../../objects/game-object';
export class PointLight implements ILight {
public static descriptor: IDrawableDescriptor = {
@ -10,13 +12,16 @@ export class PointLight implements ILight {
shaderCombinationSteps: settings.shaderCombinations.pointLightSteps,
};
constructor(
public constructor(
public readonly owner: GameObject,
public center: vec2,
public radius: number,
public color: vec3,
public lightness: number
) {}
boundingBox: ImmutableBoundingBox;
public distance(target: vec2): number {
return vec2.distance(this.center, target) - this.radius;
}

View file

@ -1,8 +1,14 @@
import { vec2 } from 'gl-matrix';
import { IPrimitive } from './i-primitive';
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
import { GameObject } from '../../../objects/game-object';
export class Circle implements IPrimitive {
public constructor(public center = vec2.create(), public radius = 0) {}
public constructor(
public readonly owner: GameObject,
public center = vec2.create(),
public radius = 0
) {}
public serializeToUniforms(uniforms: any): void {
throw new Error('Method not implemented.');
@ -16,6 +22,16 @@ export class Circle implements IPrimitive {
return vec2.distance(this.center, target) - this.radius;
}
public get boundingBox(): ImmutableBoundingBox {
return new ImmutableBoundingBox(
this,
this.center.x - this.radius,
this.center.x + this.radius,
this.center.y - this.radius,
this.center.y + this.radius
);
}
public isInside(target: vec2): boolean {
return this.distance(target) < 0;
}

View file

@ -1,18 +0,0 @@
import { vec2 } from 'gl-matrix';
export class Rectangle {
public constructor(
public topLeft: vec2 = vec2.create(),
public size: vec2 = vec2.create()
) {}
public isInside(position: vec2): boolean {
const translated = vec2.subtract(vec2.create(), position, this.topLeft);
return (
0 <= translated.x &&
translated.x < this.size.x &&
0 <= translated.y &&
translated.y < this.size.y
);
}
}

View file

@ -1,10 +1,12 @@
import { vec2 } from 'gl-matrix';
import { clamp01 } from '../../../helper/clamp';
import { mix } from '../../../helper/mix';
import { Circle } from './circle';
import { IPrimitive } from './i-primitive';
import { IDrawableDescriptor } from '../i-drawable-descriptor';
import { IPrimitive } from './i-primitive';
import { settings } from '../../settings';
import { Circle } from './circle';
import { mix } from '../../../helper/mix';
import { clamp01 } from '../../../helper/clamp';
import { ImmutableBoundingBox } from '../../../physics/containers/immutable-bounding-box';
import { GameObject } from '../../../objects/game-object';
export class TunnelShape implements IPrimitive {
public static descriptor: IDrawableDescriptor = {
@ -14,11 +16,11 @@ export class TunnelShape implements IPrimitive {
};
public readonly toFromDelta: vec2;
private toFromDeltaLength: number;
private boundingCircle: Circle;
constructor(
public readonly owner: GameObject,
public readonly from: vec2,
public readonly to: vec2,
public readonly fromRadius: number,
@ -27,12 +29,13 @@ export class TunnelShape implements IPrimitive {
this.toFromDelta = vec2.subtract(vec2.create(), to, from);
this.boundingCircle = new Circle(
this.owner,
vec2.fromValues(from.x / 2 + to.x / 2, from.y / 2 + to.y / 2),
Math.max(fromRadius, toRadius) + vec2.distance(from, to)
);
}
serializeToUniforms(uniforms: any): void {
public serializeToUniforms(uniforms: any): void {
if (!uniforms.hasOwnProperty(TunnelShape.descriptor.uniformName)) {
uniforms[TunnelShape.descriptor.uniformName] = [];
}
@ -45,6 +48,27 @@ export class TunnelShape implements IPrimitive {
});
}
public get boundingBox(): ImmutableBoundingBox {
const xMin = Math.min(
this.from.x - this.fromRadius,
this.to.x - this.toRadius
);
const yMin = Math.min(
this.from.y - this.fromRadius,
this.to.y - this.toRadius
);
const xMax = Math.max(
this.from.x + this.fromRadius,
this.to.x + this.toRadius
);
const yMax = Math.max(
this.from.y + this.fromRadius,
this.to.y + this.toRadius
);
return new ImmutableBoundingBox(this, xMin, xMax, yMin, yMax);
}
public distance(target: vec2): number {
const targetFromDelta = vec2.subtract(vec2.create(), target, this.from);
const h = clamp01(

View file

@ -1,7 +1,7 @@
import { vec2 } from 'gl-matrix';
export interface IProgram {
setDrawingRectangle(topLeft: vec2, size: vec2): void;
setDrawingRectangle(bottomLeft: vec2, size: vec2): void;
bindAndSetUniforms(values: { [name: string]: any }): void;
draw(): void;
delete(): void;

View file

@ -32,10 +32,10 @@ export abstract class Program implements IProgram {
this.setUniforms({ modelTransform: this.modelTransform, ...values });
}
public setDrawingRectangle(topLeft: vec2, size: vec2) {
public setDrawingRectangle(bottomLeft: vec2, size: vec2) {
mat2d.invert(this.modelTransform, this.ndcToUv);
mat2d.translate(this.modelTransform, this.modelTransform, topLeft);
mat2d.translate(this.modelTransform, this.modelTransform, bottomLeft);
mat2d.scale(this.modelTransform, this.modelTransform, size);
mat2d.multiply(this.modelTransform, this.modelTransform, this.ndcToUv);
}

View file

@ -1,6 +1,6 @@
import { vec2 } from 'gl-matrix';
import { IPrimitive } from './drawables/primitives/i-primitive';
import { ILight } from './drawables/lights/i-light';
import { IPrimitive } from './drawables/primitives/i-primitive';
export interface IRenderer {
startFrame(deltaTime: DOMHighResTimeStamp): void;

View file

@ -1,11 +1,10 @@
import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
import { IDrawable } from '../drawables/i-drawable';
import { settings } from '../settings';
import { vec2 } from 'gl-matrix';
import { Circle } from '../drawables/primitives/circle';
import { InfoText } from '../../objects/types/info-text';
import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program';
import { IDrawable } from '../drawables/i-drawable';
import { IDrawableDescriptor } from '../drawables/i-drawable-descriptor';
import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program';
import { settings } from '../settings';
export class RenderingPass {
private drawables: Array<IDrawable> = [];
@ -30,7 +29,8 @@ export class RenderingPass {
public render(
commonUniforms: any,
viewCircle: Circle,
viewBoxCenter: vec2,
viewBoxRadius: number,
inputTexture?: WebGLTexture
) {
this.frame.bindAndClear(inputTexture);
@ -38,7 +38,7 @@ export class RenderingPass {
const tileUvSize = vec2.fromValues(q, q);
const possiblyOnScreenDrawables = this.drawables.filter(
(p) => p.minimumDistance(viewCircle.center) < viewCircle.radius
(p) => p.minimumDistance(viewBoxCenter) < viewBoxRadius
);
const origin = vec2.transformMat2d(

View file

@ -1,34 +1,29 @@
import { mat2d, vec2, vec3 } from 'gl-matrix';
import { CircleLight } from '../drawables/lights/circle-light';
import { ILight } from '../drawables/lights/i-light';
import { PointLight } from '../drawables/lights/point-light';
import { IPrimitive } from '../drawables/primitives/i-primitive';
import { TunnelShape } from '../drawables/primitives/tunnel-shape';
// import lightsShader from '../shaders/rainbow-shading-fs.glsl';
import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer';
import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer';
import { getWebGl2Context } from '../graphics-library/helper/get-webgl2-context';
import { WebGlStopwatch } from '../graphics-library/helper/stopwatch';
import { IRenderer } from '../i-renderer';
import caveFragmentShader from '../shaders/cave-distance-fs.glsl';
import lightsFragmentShader from '../shaders/lights-shading-fs.glsl';
import caveVertexShader from '../shaders/passthrough-distance-vs.glsl';
import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl';
// import lightsShader from '../shaders/rainbow-shading-fs.glsl';
import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer';
import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer';
import { WebGlStopwatch } from '../graphics-library/helper/stopwatch';
import { IProgram } from '../graphics-library/program/i-program';
import { UniformArrayAutoScalingProgram } from '../graphics-library/program/uniform-array-autoscaling-program';
import { IRenderer } from '../i-renderer';
import { Circle } from '../drawables/primitives/circle';
import { IPrimitive } from '../drawables/primitives/i-primitive';
import { Rectangle } from '../drawables/primitives/rectangle';
import { ILight } from '../drawables/lights/i-light';
import { settings } from '../settings';
import { FpsAutoscaler } from './fps-autoscaler';
import { getWebGl2Context } from '../graphics-library/helper/get-webgl2-context';
import { RenderingPass } from './rendering-pass';
import { TunnelShape } from '../drawables/primitives/tunnel-shape';
import { CircleLight } from '../drawables/lights/circle-light';
import { PointLight } from '../drawables/lights/point-light';
export class WebGl2Renderer implements IRenderer {
private gl: WebGL2RenderingContext;
private stopwatch?: WebGlStopwatch;
private viewBox: Rectangle = new Rectangle();
private viewCircle: Circle = new Circle();
private viewBoxBottomLeft = vec2.create();
private cameraPosition = vec2.create();
private viewBoxSize = vec2.create();
private cursorPosition = vec2.create();
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
@ -38,6 +33,15 @@ export class WebGl2Renderer implements IRenderer {
private autoscaler: FpsAutoscaler;
private matrices: {
distanceScreenToWorld?: mat2d;
worldToDistanceUV?: mat2d;
cursorPosition?: mat2d;
ndcToUv?: mat2d;
uvToWorld?: mat2d;
viewBoxSize?: mat2d;
} = { ndcToUv: mat2d.fromValues(0.5, 0, 0, 0.5, 0.5, 0.5) };
constructor(private canvas: HTMLCanvasElement, private overlay: HTMLElement) {
this.gl = getWebGl2Context(canvas);
@ -85,64 +89,69 @@ export class WebGl2Renderer implements IRenderer {
}
public finishFrame() {
const uniforms = this.calculateOwnUniforms();
this.lightingPass.addDrawable(
new PointLight(uniforms.cursorPosition, 200, vec3.fromValues(1, 1, 0), 1)
);
this.distancePass.render(uniforms, this.viewCircle);
this.lightingPass.render(
uniforms,
this.viewCircle,
this.distanceFieldFrameBuffer.colorTexture
);
this.stopwatch?.stop();
}
private calculateOwnUniforms(): any {
const distanceScreenToWorld = this.getScreenToWorldTransform(
this.distanceFieldFrameBuffer.getSize()
);
const uvToWorld = mat2d.fromTranslation(
mat2d.create(),
this.viewBox.topLeft
);
mat2d.scale(uvToWorld, uvToWorld, this.viewBox.size);
const worldToDistanceUV = mat2d.scale(
mat2d.create(),
distanceScreenToWorld,
this.distanceFieldFrameBuffer.getSize()
);
mat2d.invert(worldToDistanceUV, worldToDistanceUV);
const ndcToUv = mat2d.fromScaling(
mat2d.create(),
vec2.fromValues(0.5, 0.5)
);
mat2d.translate(ndcToUv, ndcToUv, vec2.fromValues(1, 1));
this.calculateMatrices();
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition);
return {
distanceScreenToWorld,
worldToDistanceUV,
cursorPosition,
ndcToUv,
uvToWorld,
viewBoxSize: this.viewBox.size,
};
this.lightingPass.addDrawable(
new PointLight(null, cursorPosition, 200, vec3.fromValues(1, 1, 0), 1)
);
const viewBoxRadius = vec2.length(
vec2.scale(vec2.create(), this.viewBoxSize, 0.5)
);
this.distancePass.render(
{ ...this.matrices, cursorPosition },
this.cameraPosition,
viewBoxRadius
);
this.lightingPass.render(
{ ...this.matrices, cursorPosition },
this.cameraPosition,
viewBoxRadius,
this.distanceFieldFrameBuffer.colorTexture
);
this.stopwatch?.stop();
}
private calculateMatrices() {
this.matrices.uvToWorld = mat2d.fromTranslation(
mat2d.create(),
this.viewBoxBottomLeft
);
mat2d.scale(
this.matrices.uvToWorld,
this.matrices.uvToWorld,
this.viewBoxSize
);
this.matrices.distanceScreenToWorld = this.getScreenToWorldTransform(
this.distanceFieldFrameBuffer.getSize()
);
this.matrices.worldToDistanceUV = mat2d.scale(
mat2d.create(),
this.matrices.distanceScreenToWorld,
this.distanceFieldFrameBuffer.getSize()
);
mat2d.invert(
this.matrices.worldToDistanceUV,
this.matrices.worldToDistanceUV
);
}
private getScreenToWorldTransform(screenSize: vec2) {
const transform = mat2d.fromTranslation(
mat2d.create(),
this.viewBox.topLeft
this.viewBoxBottomLeft
);
mat2d.scale(
transform,
transform,
vec2.divide(vec2.create(), this.viewBox.size, screenSize)
vec2.divide(vec2.create(), this.viewBoxSize, screenSize)
);
mat2d.translate(transform, transform, vec2.fromValues(0.5, 0.5));
@ -160,12 +169,10 @@ export class WebGl2Renderer implements IRenderer {
}
public setCameraPosition(position: vec2) {
this.viewBox.topLeft = position;
const halfDiagonal = vec2.scale(vec2.create(), this.viewBox.size, 0.5);
this.viewCircle.center = vec2.add(
vec2.create(),
this.viewBox.topLeft,
halfDiagonal
this.cameraPosition = position;
this.viewBoxBottomLeft = vec2.fromValues(
this.cameraPosition.x - this.viewBoxSize.x / 2,
this.cameraPosition.y - this.viewBoxSize.y / 2
);
}
@ -177,21 +184,10 @@ export class WebGl2Renderer implements IRenderer {
const canvasAspectRatio =
this.canvas.clientWidth / this.canvas.clientHeight;
this.viewBox.size = vec2.fromValues(
return (this.viewBoxSize = vec2.fromValues(
Math.sqrt(size * canvasAspectRatio),
Math.sqrt(size / canvasAspectRatio)
);
const halfDiagonal = vec2.scale(vec2.create(), this.viewBox.size, 0.5);
this.viewCircle.center = vec2.add(
vec2.create(),
this.viewBox.topLeft,
halfDiagonal
);
this.viewCircle.radius = vec2.length(halfDiagonal);
return this.viewBox.size;
));
}
public drawInfoText(text: string) {

View file

@ -6,6 +6,7 @@ precision mediump float;
#define POINT_LIGHT_COUNT {pointLightCount}
uniform mat3 modelTransform;
uniform mat3 cameraTransform;
uniform mat3 ndcToUv;
uniform mat3 uvToWorld;

View file

@ -6,16 +6,22 @@ import { timeIt } from './helper/timing';
import { KeyboardListener } from './input/keyboard-listener';
import { MouseListener } from './input/mouse-listener';
import { TouchListener } from './input/touch-listener';
import { ObjectContainer } from './objects/object-container';
import { Objects } from './objects/objects';
import { InfoText } from './objects/types/info-text';
import { createCharacter } from './objects/world/create-character';
import { createDungeon } from './objects/world/create-dungeon';
import { RenderCommand } from './drawing/commands/render';
import { Physics } from './physics/physics';
import { MoveToCommand } from './physics/commands/move-to';
import { TeleportToCommand } from './physics/commands/teleport-to';
import { IRenderer } from './drawing/i-renderer';
export class Game {
private previousTime?: DOMHighResTimeStamp = null;
private objects: ObjectContainer = new ObjectContainer();
private renderer: WebGl2Renderer;
private objects = new Objects();
private physics = new Physics();
private renderer: IRenderer;
private previousFpsValues: Array<number> = [];
constructor() {
@ -39,14 +45,18 @@ export class Game {
this.renderer = new WebGl2Renderer(canvas, overlay);
this.initializeScene();
this.physics.start();
requestAnimationFrame(this.gameLoop.bind(this));
}
private initializeScene() {
this.objects.addObject(new InfoText());
createCharacter(this.objects);
createDungeon(this.objects);
createDungeon(this.objects);
const start = createDungeon(this.objects, this.physics);
createDungeon(this.objects, this.physics);
const character = createCharacter(this.objects, this.physics);
console.log('start', start.from);
character.sendCommand(new TeleportToCommand(start.from));
}
private handleVisibilityChange() {

View file

@ -1,33 +1,16 @@
import { vec2 } from 'gl-matrix';
import { Command } from '../commands/command';
import { CommandReceiver } from '../commands/command-receiver';
import { IdentityManager } from '../identity/identity-manager';
import { Objects } from './objects';
import { Physics } from '../physics/physics';
export abstract class GameObject implements CommandReceiver {
public readonly id = IdentityManager.generateId();
protected _position = vec2.create();
public get position(): vec2 {
return this._position;
}
protected _boundingBoxSize = vec2.create();
public get boundingBoxSize(): vec2 {
return this._boundingBoxSize;
}
private commandExecutors: {
[commandType: string]: (e: Command) => void;
} = {};
// can only be called inside the constructor
protected addCommandExecutor<T extends Command>(
commandType: new () => T,
handler: (command: T) => void
) {
this.commandExecutors[new commandType().type] = handler;
}
public reactsToCommand(commandType: string): boolean {
return this.commandExecutors.hasOwnProperty(commandType);
}
@ -39,4 +22,12 @@ export abstract class GameObject implements CommandReceiver {
this.commandExecutors[commandType](command);
}
}
// can only be called inside the constructor
protected addCommandExecutor<T extends Command>(
commandType: new () => T,
handler: (command: T) => void
) {
this.commandExecutors[new commandType().type] = handler;
}
}

View file

@ -0,0 +1,49 @@
import { Command } from '../commands/command';
import { CommandReceiver } from '../commands/command-receiver';
import { Id } from '../identity/identity';
import { GameObject } from './game-object';
export class Objects implements CommandReceiver {
private objects: Map<Id, GameObject> = new Map();
private objectsGroupedByAbilities: Map<string, Array<GameObject>> = new Map();
public addObject(o: GameObject) {
this.objects.set(o.id, o);
for (let command of this.objectsGroupedByAbilities.keys()) {
if (o.reactsToCommand(command)) {
this.objectsGroupedByAbilities.get(command).push(o);
}
}
}
public removeObject(o: GameObject) {
this.objects.delete(o.id);
}
public sendCommandToSingleObject(id: Id, e: Command) {
this.objects.get(id)?.sendCommand(e);
}
public sendCommand(e: Command) {
if (!this.objectsGroupedByAbilities.has(e.type)) {
this.createGroupForCommand(e.type);
}
this.objectsGroupedByAbilities
.get(e.type)
.forEach((o, _) => o.sendCommand(e));
}
private createGroupForCommand(commandType: string) {
const objectsReactingToCommand = [];
this.objects.forEach((o, _) => {
if (o.reactsToCommand(commandType)) {
objectsReactingToCommand.push(o);
}
});
this.objectsGroupedByAbilities.set(commandType, objectsReactingToCommand);
}
}

View file

@ -5,14 +5,20 @@ import { GameObject } from '../game-object';
import { Lamp } from './lamp';
import { ZoomCommand } from '../../input/commands/zoom';
import { MoveToCommand } from '../../physics/commands/move-to';
import { BoundingBox } from '../../physics/containers/bounding-box';
import { Physics } from '../../physics/physics';
export class Camera extends GameObject {
private inViewArea = 1920 * 1080 * 5;
private cursorPosition = vec2.create();
private boundingBox: BoundingBox;
constructor(private light: Lamp) {
constructor(physics: Physics, private light: Lamp) {
super();
this.boundingBox = new BoundingBox(null);
physics.addDynamicBoundingBox(this.boundingBox);
this.addCommandExecutor(BeforeRenderCommand, this.draw.bind(this));
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
this.addCommandExecutor(
@ -22,20 +28,27 @@ export class Camera extends GameObject {
this.addCommandExecutor(ZoomCommand, this.zoom.bind(this));
}
public get viewAreaSize(): vec2 {
return this.boundingBox.size;
}
private draw(c: BeforeRenderCommand) {
c.renderer.setCameraPosition(this.position);
console.log('camera', this.boundingBox.topLeft);
c.renderer.setCameraPosition(this.boundingBox.topLeft);
c.renderer.setCursorPosition(this.cursorPosition);
this._boundingBoxSize = c.renderer.setInViewArea(this.inViewArea);
this.boundingBox.size = c.renderer.setInViewArea(this.inViewArea);
}
private moveTo(c: MoveToCommand) {
this._position = c.position;
console.log('camera', c.position);
this.boundingBox.topLeft = c.position;
this.light.sendCommand(
new MoveToCommand(
vec2.add(
vec2.create(),
c.position,
vec2.scale(vec2.create(), this.boundingBoxSize, 0.5)
vec2.scale(vec2.create(), this.boundingBox.size, 0.5)
)
)
);

View file

@ -1,37 +1,80 @@
import { vec2 } from 'gl-matrix';
import { GameObject } from '../game-object';
import { Camera } from './camera';
import { StepCommand } from '../../physics/commands/step';
import { Circle } from '../../drawing/drawables/primitives/circle';
import { KeyDownCommand } from '../../input/commands/key-down';
import { KeyUpCommand } from '../../input/commands/key-up';
import { SwipeCommand } from '../../input/commands/swipe';
import { MoveToCommand } from '../../physics/commands/move-to';
import { StepCommand } from '../../physics/commands/step';
import { TeleportToCommand } from '../../physics/commands/teleport-to';
import { BoundingBox } from '../../physics/containers/bounding-box';
import { Physics } from '../../physics/physics';
import { GameObject } from '../game-object';
import { Camera } from './camera';
export class Character extends GameObject {
private keysDown: Set<string> = new Set();
private static speed = 0.5;
private primitive: Circle;
private static speed = 1.5;
constructor(private camera: Camera) {
constructor(private physics: Physics, private camera: Camera) {
super();
this.primitive = new Circle(this);
this.primitive.radius = 20;
this.addCommandExecutor(StepCommand, this.stepHandler.bind(this));
this.addCommandExecutor(TeleportToCommand, (c) =>
this.setPosition(c.position)
);
this.addCommandExecutor(KeyDownCommand, (c) => this.keysDown.add(c.key));
this.addCommandExecutor(KeyUpCommand, (c) => this.keysDown.delete(c.key));
this.addCommandExecutor(SwipeCommand, (c) =>
this.setPosition(
this.checkAndSetPosition(
vec2.add(
vec2.create(),
this.position,
vec2.multiply(vec2.create(), c.delta, this.camera.boundingBoxSize)
this.primitive.center,
vec2.multiply(vec2.create(), c.delta, this.camera.viewAreaSize)
)
)
);
}
private checkAndSetPosition(value: vec2) {
const size = this.camera.viewAreaSize;
const realCenter = vec2.fromValues(
value.x + size.x / 2,
value.y + size.y / 2
);
const targetBoundingBox = new BoundingBox(
null,
value.x + size.x / 2,
value.x + size.x / 2 + 10,
value.y + size.y / 2,
value.y + size.y / 2 + 10
);
console.log(
this.physics
.findIntersecting(targetBoundingBox)
.map((b) => b.value.distance(realCenter))
);
if (
this.physics
.findIntersecting(targetBoundingBox)
.map((b) => b.value.distance(realCenter))
.find((d) => d < 0) !== undefined
) {
this.setPosition(value);
}
}
private setPosition(value: vec2) {
this._position = value;
this.camera.sendCommand(new MoveToCommand(this.position));
console.log('character', value);
this.primitive.center = value;
this.camera.sendCommand(new MoveToCommand(this.primitive.center));
}
public stepHandler(c: StepCommand) {
@ -44,16 +87,11 @@ export class Character extends GameObject {
const movementVector = vec2.fromValues(right - left, up - down);
if (movementVector.length > 0) {
this.setPosition(
vec2.add(
vec2.create(),
this.position,
vec2.scale(
vec2.create(),
vec2.normalize(movementVector, movementVector),
Character.speed * deltaTime
)
)
vec2.normalize(movementVector, movementVector);
vec2.scale(movementVector, movementVector, Character.speed * deltaTime);
this.checkAndSetPosition(
vec2.add(vec2.create(), this.primitive.center, movementVector)
);
}
}

View file

@ -10,7 +10,7 @@ export class Lamp extends GameObject {
constructor(center: vec2, radius: number, color: vec3, lightness: number) {
super();
this.light = new CircleLight(center, radius, color, lightness);
this.light = new CircleLight(this, center, radius, color, lightness);
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));

View file

@ -1,16 +1,25 @@
import { vec2 } from 'gl-matrix';
import { TunnelShape } from '../../drawing/drawables/primitives/tunnel-shape';
import { GameObject } from '../game-object';
import { RenderCommand } from '../../drawing/commands/render';
import { Physics } from '../../physics/physics';
import { TunnelShape } from '../../drawing/drawables/primitives/tunnel-shape';
export interface Line {}
export class Tunnel extends GameObject {
private primitive: TunnelShape;
constructor(from: vec2, to: vec2, fromRadius: number, toRadius: number) {
constructor(
physics: Physics,
public readonly from: vec2,
to: vec2,
fromRadius: number,
toRadius: number
) {
super();
this.primitive = new TunnelShape(from, to, fromRadius, toRadius);
this.primitive = new TunnelShape(this, from, to, fromRadius, toRadius);
physics.addStaticBoundingBox(this.primitive.boundingBox);
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
}

Some files were not shown because too many files have changed in this diff Show more