From 2656a26d72c76be910757433ff7b40d2680260bd Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Sat, 28 Aug 2021 13:05:45 -0500 Subject: [PATCH 01/18] fix: Tweak vector-view to make console output clearer --- src/engine/Algebra.ts | 12 ++++-------- src/engine/Math/vector-view.ts | 7 ++++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/engine/Algebra.ts b/src/engine/Algebra.ts index 6c1cc681b6..44162a2964 100644 --- a/src/engine/Algebra.ts +++ b/src/engine/Algebra.ts @@ -98,7 +98,7 @@ export class Vector implements Clonable { this._y = y; } - private _x = 0; + protected _x = 0; /** * Get the x component of the vector */ @@ -114,7 +114,7 @@ export class Vector implements Clonable { this._x = val; } - private _y = 0; + protected _y = 0; /** * Get the y component of the vector */ @@ -406,7 +406,7 @@ export class Ray { } public intersectPoint(line: Line): Vector { - const time = this.intersect(line); + const time = this.intersect(line); if (time < 0) { return null; } @@ -508,8 +508,7 @@ export class Line { * @param point */ public below(point: Vector): boolean { - const above2 = ((this.end.x - this.begin.x) * (point.y - this.begin.y) - - (this.end.y - this.begin.y) * (point.x - this.begin.x)); + const above2 = (this.end.x - this.begin.x) * (point.y - this.begin.y) - (this.end.y - this.begin.y) * (point.x - this.begin.x); return above2 >= 0; } @@ -522,7 +521,6 @@ export class Line { let dir = sideVector; dir = dir.normalize(); - const near = dir.dot(this.begin) - length; const far = dir.dot(this.end) - length; @@ -713,5 +711,3 @@ export class GlobalCoordinates { export function vec(x: number, y: number): Vector { return new Vector(x, y); } - - diff --git a/src/engine/Math/vector-view.ts b/src/engine/Math/vector-view.ts index f3ca900a61..250f705bc6 100644 --- a/src/engine/Math/vector-view.ts +++ b/src/engine/Math/vector-view.ts @@ -1,6 +1,5 @@ import { Vector } from '../Algebra'; - export interface VectorViewOptions { getX: () => number; getY: () => number; @@ -20,17 +19,19 @@ export class VectorView extends Vector { this._setY = options.setY; } public get x() { - return this._getX(); + return (this._x = this._getX()); } public set x(val) { this._setX(val); + this._x = val; } public get y() { - return this._getY(); + return (this._y = this._getY()); } public set y(val) { this._setY(val); + this._y = val; } } From 9a74ea2170592522c05b5f528ee539349aa57e3e Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Sat, 28 Aug 2021 22:05:13 -0500 Subject: [PATCH 02/18] Add drawLine, drawRectangle, drawCircle to context --- .../Context/ExcaliburGraphicsContextWebGL.ts | 12 ++ .../Graphics/Context/draw-image-command.ts | 93 +++++++++++++++ src/engine/Graphics/Context/image-renderer.ts | 108 +++++++++++++++--- .../Context/shaders/image-fragment.glsl | 36 ++++-- .../Context/shaders/image-vertex.glsl | 5 + src/engine/Resources/Gif.ts | 2 +- src/spec/CompositeColliderSpec.ts | 2 +- src/spec/ExcaliburGraphicsContextSpec.ts | 7 +- wallaby.js | 1 + 9 files changed, 236 insertions(+), 30 deletions(-) diff --git a/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts b/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts index 1e11562cad..19e7d9c620 100644 --- a/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts +++ b/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts @@ -225,6 +225,18 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext { this.__imageRenderer.addImage(image, sx, sy, swidth, sheight, dx, dy, dwidth, dheight); } + public drawLine(start: Vector, end: Vector, color: Color, thickness = 1) { + this.__imageRenderer.addLine(color, start, end, thickness); + } + + public drawRectangle(pos: Vector, width: number, height: number, color: Color) { + this.__imageRenderer.addRectangle(color, pos, width, height); + } + + public drawCircle(pos: Vector, radius: number, color: Color) { + this.__imageRenderer.addCircle(pos, radius, color); + } + debug = new ExcaliburGraphicsContextWebGLDebug(this); public save(): void { diff --git a/src/engine/Graphics/Context/draw-image-command.ts b/src/engine/Graphics/Context/draw-image-command.ts index cf6842b2b6..75a9a51748 100644 --- a/src/engine/Graphics/Context/draw-image-command.ts +++ b/src/engine/Graphics/Context/draw-image-command.ts @@ -3,12 +3,22 @@ import { BoundingBox } from '../../Collision/Index'; import { Color } from '../../Color'; import { Pool, Poolable } from '../../Util/Pool'; import { HTMLImageSource } from './ExcaliburGraphicsContext'; +import { vec, Vector } from '../../Algebra'; + +export enum DrawCommandType { + Image = 'image', + Line = 'line', + Rectangle = 'rectangle', + Circle = 'circle' +} export class DrawImageCommand implements Poolable { _pool: Pool = undefined; public snapToPixel: boolean = true; public image: HTMLImageSource; + public color: Color; + public type = DrawCommandType.Image public opacity: number = 1; public width: number = 0; public height: number = 0; @@ -61,6 +71,7 @@ export class DrawImageCommand implements Poolable { dwidth?: number, dheight?: number ) { + this.type = DrawCommandType.Image; this.image = image; this.width = image?.width || swidth || 0; this.height = image?.height || sheight || 0; @@ -89,6 +100,88 @@ export class DrawImageCommand implements Poolable { } return this; } + public initRect(color: Color, start: Vector, width: number, height: number) { + this.type = DrawCommandType.Rectangle + this.color = color; + this.width = width; + this.height = height; + let index = 0; + this._geom[index++] = [start.x, start.y]; + this._geom[index++] = [start.x, start.y + this.height]; + this._geom[index++] = [start.x + this.width, start.y]; + this._geom[index++] = [start.x + this.width, start.y]; + this._geom[index++] = [start.x, start.y + this.height]; + this._geom[index++] = [start.x + this.width, start.y + this.height]; + + if (this.snapToPixel) { + for (const point of this._geom) { + point[0] = ~~point[0]; + point[1] = ~~point[1]; + } + } + return this; + } + + public initLine(color: Color, start: Vector, end: Vector, thickness: number) { + this.type = DrawCommandType.Line; + this.color = color; + const dir = end.sub(start).normalize(); + const normal = dir.perpendicular(); + const halfThick = thickness / 2; + const startTop = normal.scale(halfThick).add(start); + const startBottom = normal.scale(-halfThick).add(start); + const endTop = normal.scale(halfThick).add(end); + const endBottom = normal.scale(-halfThick).add(end); + + /** + * + * +---------------------^----------------------+ + * | | (normal) | + * (startx, starty)------------------>(endx, endy) + * | | + * + -------------------------------------------+ + */ + + let index = 0; + this._geom[index++] = [startTop.x, startTop.y]; + this._geom[index++] = [endTop.x, endTop.y]; + this._geom[index++] = [startBottom.x, startBottom.y]; + this._geom[index++] = [startBottom.x, startBottom.y]; + this._geom[index++] = [endTop.x, endTop.y]; + this._geom[index++] = [endBottom.x,endBottom.y]; + this._geom;//? + if (this.snapToPixel) { + for (const point of this._geom) { + point[0] = ~~point[0]; + point[1] = ~~point[1]; + } + } + return this; + } + + public initCircle(pos: Vector, radius: number, color: Color) { + this.type = DrawCommandType.Circle; + this.color = color; + let topLeft = pos.add(vec(-radius, -radius)); + let topRight = pos.add(vec(radius, -radius)); + let bottomRight = pos.add(vec(radius, radius)); + let bottomLeft = pos.add(vec(-radius, radius)); + let index = 0; + this._geom[index++] = [topLeft.x, topLeft.y]; + this._geom[index++] = [topRight.x, topRight.y]; + this._geom[index++] = [bottomLeft.x, bottomLeft.y]; + this._geom[index++] = [bottomLeft.x, bottomLeft.y]; + this._geom[index++] = [topRight.x, topRight.y]; + this._geom[index++] = [bottomRight.x, bottomRight.y]; + this._geom;//? + if (this.snapToPixel) { + for (const point of this._geom) { + point[0] = ~~point[0]; + point[1] = ~~point[1]; + } + } + return this; + } public dispose() { this.image = null; diff --git a/src/engine/Graphics/Context/image-renderer.ts b/src/engine/Graphics/Context/image-renderer.ts index da8323bf73..42e9af8c56 100644 --- a/src/engine/Graphics/Context/image-renderer.ts +++ b/src/engine/Graphics/Context/image-renderer.ts @@ -2,13 +2,15 @@ import { Shader } from './shader'; import imageVertexSource from './shaders/image-vertex.glsl'; import imageFragmentSource from './shaders/image-fragment.glsl'; import { BatchCommand } from './batch'; -import { DrawImageCommand } from './draw-image-command'; +import { DrawCommandType, DrawImageCommand } from './draw-image-command'; import { Graphic } from '../Graphic'; import { ensurePowerOfTwo } from './webgl-util'; import { BatchRenderer } from './renderer'; import { WebGLGraphicsContextInfo } from './ExcaliburGraphicsContextWebGL'; import { TextureLoader } from './texture-loader'; import { HTMLImageSource } from './ExcaliburGraphicsContext'; +import { Color } from '../../Color'; +import { Vector } from '../..'; export class BatchImage extends BatchCommand { public textures: WebGLTexture[] = []; @@ -63,9 +65,11 @@ export class BatchImage extends BatchCommand { } add(command: DrawImageCommand) { - const texture = TextureLoader.load(command.image); - if (this.textures.indexOf(texture) === -1) { - this.textures.push(texture); + if ((command.type === DrawCommandType.Image)) { + const texture = TextureLoader.load(command.image); + if (this.textures.indexOf(texture) === -1) { + this.textures.push(texture); + } } this.commands.push(command); @@ -120,6 +124,7 @@ export class ImageRenderer extends BatchRenderer { shader.addAttribute('a_texcoord', 2, gl.FLOAT); shader.addAttribute('a_textureIndex', 1, gl.FLOAT); shader.addAttribute('a_opacity', 1, gl.FLOAT); + shader.addAttribute('a_color', 4, gl.FLOAT); shader.addUniformMatrix('u_matrix', this._contextInfo.matrix.data); // Initialize texture slots to [0, 1, 2, 3, 4, .... maxGPUTextures] shader.addUniformIntegerArray( @@ -133,14 +138,36 @@ export class ImageRenderer extends BatchRenderer { let newSource = source.replace('%%count%%', maxTextures.toString()); let texturePickerBuilder = ''; for (let i = 0; i < maxTextures; i++) { - texturePickerBuilder += ` } else if (v_textureIndex <= ${i}.5) {\n - gl_FragColor = texture2D(u_textures[${i}], v_texcoord);\n - gl_FragColor.w = gl_FragColor.w * v_opacity;\n`; + if (i === 0) { + texturePickerBuilder += `if (v_textureIndex <= ${i}.5) {\n`; + } else { + texturePickerBuilder += ` else if (v_textureIndex <= ${i}.5) {\n` + } + texturePickerBuilder += ` color = texture2D(u_textures[${i}], v_texcoord);\n`; + texturePickerBuilder += ` }\n`; } newSource = newSource.replace('%%texture_picker%%', texturePickerBuilder); return newSource; } + public addCircle(pos: Vector, radius: number, color: Color) { + const command = this.commands.get().initCircle(pos, radius, color); + command.applyTransform(this._contextInfo.transform.current, this._contextInfo.state.current.opacity); + this.addCommand(command); + } + + public addRectangle(color: Color, pos: Vector, width: number, height: number) { + const command = this.commands.get().initRect(color, pos, width, height); + command.applyTransform(this._contextInfo.transform.current, this._contextInfo.state.current.opacity); + this.addCommand(command); + } + + public addLine(color: Color, start: Vector, end: Vector, thickness = 1) { + const command = this.commands.get().initLine(color, start, end, thickness); + command.applyTransform(this._contextInfo.transform.current, this._contextInfo.state.current.opacity); + this.addCommand(command); + } + public addImage( graphic: HTMLImageSource, sx: number, @@ -170,26 +197,41 @@ export class ImageRenderer extends BatchRenderer { let sy: number = 0; let sw: number = 0; let sh: number = 0; - let potWidth: number = 0; - let potHeight: number = 0; + let potWidth: number = 1; + let potHeight: number = 1; let textureId = 0; + let commandColor = Color.Transparent; for (const command of batch.commands) { sx = command.view[0]; sy = command.view[1]; sw = command.view[2]; sh = command.view[3]; - potWidth = ensurePowerOfTwo(command.image.width || command.width); - potHeight = ensurePowerOfTwo(command.image.height || command.height); - - textureId = batch.getBatchTextureId(command); - + potWidth = ensurePowerOfTwo(command.image?.width || command.width);//? + potHeight = ensurePowerOfTwo(command.image?.height || command.height);//? + + textureId = batch.getBatchTextureId(command);//? + if (command.type === DrawCommandType.Line || command.type === DrawCommandType.Rectangle) { + textureId === -1 + commandColor = command.color; + } + if (command.type === DrawCommandType.Circle) { + textureId = -2; + commandColor = command.color; + } + // potential optimization when divding by 2 (bitshift) // Modifying the images to poweroftwo images warp the UV coordinates - const uvx0 = sx / potWidth; - const uvy0 = sy / potHeight; - const uvx1 = (sx + sw) / potWidth; - const uvy1 = (sy + sh) / potHeight; + let uvx0 = sx / potWidth;//? + let uvy0 = sy / potHeight;//? + let uvx1 = (sx + sw) / potWidth;//? + let uvy1 = (sy + sh) / potHeight;//? + if (textureId === -2) { + uvx0 = 0; + uvy0 = 0; + uvx1 = 1; + uvy1 = 1; + } // Quad update // (0, 0, z) z-index doesn't work in batch rendering between batches @@ -204,6 +246,11 @@ export class ImageRenderer extends BatchRenderer { vertexBuffer[vertIndex++] = textureId; // opacity vertexBuffer[vertIndex++] = command.opacity; + // color + vertexBuffer[vertIndex++] = commandColor.r; + vertexBuffer[vertIndex++] = commandColor.g; + vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.a; // (0, 1) vertexBuffer[vertIndex++] = command.geometry[1][0]; // x + 0 * width; @@ -217,6 +264,11 @@ export class ImageRenderer extends BatchRenderer { vertexBuffer[vertIndex++] = textureId; // opacity vertexBuffer[vertIndex++] = command.opacity; + // color + vertexBuffer[vertIndex++] = commandColor.r; + vertexBuffer[vertIndex++] = commandColor.g; + vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.a; // (1, 0) vertexBuffer[vertIndex++] = command.geometry[2][0]; // x + 1 * width; @@ -230,6 +282,11 @@ export class ImageRenderer extends BatchRenderer { vertexBuffer[vertIndex++] = textureId; // opacity vertexBuffer[vertIndex++] = command.opacity; + // color + vertexBuffer[vertIndex++] = commandColor.r; + vertexBuffer[vertIndex++] = commandColor.g; + vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.a; // (1, 0) vertexBuffer[vertIndex++] = command.geometry[3][0]; // x + 1 * width; @@ -243,6 +300,11 @@ export class ImageRenderer extends BatchRenderer { vertexBuffer[vertIndex++] = textureId; // opacity vertexBuffer[vertIndex++] = command.opacity; + // color + vertexBuffer[vertIndex++] = commandColor.r; + vertexBuffer[vertIndex++] = commandColor.g; + vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.a; // (0, 1) vertexBuffer[vertIndex++] = command.geometry[4][0]; // x + 0 * width; @@ -256,6 +318,11 @@ export class ImageRenderer extends BatchRenderer { vertexBuffer[vertIndex++] = textureId; // opacity vertexBuffer[vertIndex++] = command.opacity; + // color + vertexBuffer[vertIndex++] = commandColor.r; + vertexBuffer[vertIndex++] = commandColor.g; + vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.a; // (1, 1) vertexBuffer[vertIndex++] = command.geometry[5][0]; // x + 1 * width; @@ -269,6 +336,11 @@ export class ImageRenderer extends BatchRenderer { vertexBuffer[vertIndex++] = textureId; // opacity vertexBuffer[vertIndex++] = command.opacity; + // color + vertexBuffer[vertIndex++] = commandColor.r; + vertexBuffer[vertIndex++] = commandColor.g; + vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.a; } return vertIndex / this.vertexSize; diff --git a/src/engine/Graphics/Context/shaders/image-fragment.glsl b/src/engine/Graphics/Context/shaders/image-fragment.glsl index ed54dad19c..e54f68dd47 100644 --- a/src/engine/Graphics/Context/shaders/image-fragment.glsl +++ b/src/engine/Graphics/Context/shaders/image-fragment.glsl @@ -1,3 +1,6 @@ +#ifdef GL_OES_standard_derivatives +#extension GL_OES_standard_derivatives : enable +#endif precision mediump float; // UV coord @@ -6,25 +9,40 @@ varying vec2 v_texcoord; // Texture index varying lowp float v_textureIndex; +// Color coord to blend with image +varying lowp vec4 v_color; + // Opacity varying float v_opacity; uniform sampler2D u_textures[%%count%%]; +float circle(in vec2 st, in float radius) { + vec2 dist = st - vec2(0.5); + float r = dot(dist, dist) * 4.0; + float delta = fwidth(r); + return 1.0 - smoothstep(radius - delta, radius + delta, r); +} + void main() { + float r = 0.0, delta = 0.0, alpha = 1.0; // In order to support the most efficient sprite batching, we have multiple // textures loaded into the gpu (usually 8) this picker logic skips over textures // that do not apply to a particular sprite. - // Error color - vec4 color = vec4(1.0, 0.0, 0.0, 1.0); - - // Always at least 1 texture at 0 - if (v_textureIndex <= .5) { - gl_FragColor = texture2D(u_textures[0], v_texcoord); - gl_FragColor.w = gl_FragColor.w * v_opacity; - %%texture_picker%% + vec4 color; + // -1 If there is no texture to sample we are drawing a solid geometry (rectangles) + if (v_textureIndex == -1.0) { + color = v_color; + color.w = color.w * v_opacity; + // -2 If there is no texture we are drawing a circle + } else if (v_textureIndex == -2.0) { + color = v_color; + color.a = color.a * circle(v_texcoord, .95); } else { - gl_FragColor = color; + // GLSL is templated out to pick the right texture and set the vec4 color + %%texture_picker%% } + color.w = color.w * v_opacity; + gl_FragColor = color; } \ No newline at end of file diff --git a/src/engine/Graphics/Context/shaders/image-vertex.glsl b/src/engine/Graphics/Context/shaders/image-vertex.glsl index c34b062d70..989643036f 100644 --- a/src/engine/Graphics/Context/shaders/image-vertex.glsl +++ b/src/engine/Graphics/Context/shaders/image-vertex.glsl @@ -4,6 +4,9 @@ attribute vec4 a_position; attribute float a_opacity; varying float v_opacity; +attribute vec4 a_color; +varying vec4 v_color; + // UV coordinate attribute vec2 a_texcoord; varying vec2 v_texcoord; @@ -25,4 +28,6 @@ void main() { v_texcoord = a_texcoord; // Pass through the texture number to the fragment shader v_textureIndex = a_textureIndex; + // Pass through the color to the fragment shader + v_color = a_color; } \ No newline at end of file diff --git a/src/engine/Resources/Gif.ts b/src/engine/Resources/Gif.ts index baeed260e2..c2cdbdc313 100644 --- a/src/engine/Resources/Gif.ts +++ b/src/engine/Resources/Gif.ts @@ -357,7 +357,7 @@ export class ParseGif { const parseAppExt = (block: any) => { const parseNetscapeExt = (block: any) => { this.checkBytes.push(this._st.readByte()); // Always 3 - block.unknown = this._st.readByte(); // ??? Always 1? What is this? + block.unknown = this._st.readByte(); // Q: Always 1? What is this? block.iterations = this._st.readUnsigned(); block.terminator = this._st.readByte(); if (this._handler.app && this._handler.app.NETSCAPE && this._handler.app.NETSCAPE(block)) { diff --git a/src/spec/CompositeColliderSpec.ts b/src/spec/CompositeColliderSpec.ts index 0fa2c6c1ee..2e556ec52f 100644 --- a/src/spec/CompositeColliderSpec.ts +++ b/src/spec/CompositeColliderSpec.ts @@ -146,7 +146,7 @@ describe('A CompositeCollider', () => { const rayRight = new Ray(vec(-200, 0), Vector.Right); - const leftBox = compCollider.rayCast(rayRight); //? + const leftBox = compCollider.rayCast(rayRight); expect(leftBox).toEqual(vec(-100, 0)); const rayDown = new Ray(vec(0, -200), Vector.Down); diff --git a/src/spec/ExcaliburGraphicsContextSpec.ts b/src/spec/ExcaliburGraphicsContextSpec.ts index 585a34f6c5..4f29541c39 100644 --- a/src/spec/ExcaliburGraphicsContextSpec.ts +++ b/src/spec/ExcaliburGraphicsContextSpec.ts @@ -217,7 +217,7 @@ describe('The ExcaliburGraphicsContext', () => { expect(context.height).toBe(canvas.height); }); - it('can draw a graphic', async () => { + fit('can draw a graphic', async () => { const canvasElement = document.createElement('canvas'); canvasElement.width = 100; canvasElement.height = 100; @@ -235,6 +235,11 @@ describe('The ExcaliburGraphicsContext', () => { sut.clear(); sut.drawImage(rect._bitmap, 20, 20); + // sut.opacity = .5; + // sut.drawCircle(ex.vec(50, 50), 50, ex.Color.Green); + // sut.drawLine(ex.vec(10, 10), ex.vec(90, 90), ex.Color.Red, 5); + // sut.drawLine(ex.vec(90, 10), ex.vec(10, 90), ex.Color.Red, 5); + // sut.drawRectangle(ex.vec(10, 10), 80, 80, ex.Color.Blue); sut.flush(); await expectAsync(flushWebGLCanvasTo2D(canvasElement)).toEqualImage( diff --git a/wallaby.js b/wallaby.js index 412239265b..83eb04046d 100644 --- a/wallaby.js +++ b/wallaby.js @@ -5,6 +5,7 @@ module.exports = function (wallaby) { return { files: [ { pattern: 'src/engine/**/*.ts', load: false }, + { pattern: 'src/engine/**/*.glsl', load: false }, { pattern: 'src/spec/images/**/*.mp3' }, { pattern: 'src/spec/images/**/*.png' }, { pattern: 'src/spec/images/**/*.gif' }, From be32b5dcffdabc761f3a8a13f884ec77155903ca Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Fri, 3 Sep 2021 11:41:06 -0500 Subject: [PATCH 03/18] WIP --- src/engine/Debug.ts | 61 ++++++++++++++ src/engine/Debug/DebugSystem.ts | 78 ++++++++++++++++++ src/engine/EntityComponentSystem/Entity.ts | 8 +- .../EntityComponentSystem/EntityManager.ts | 4 + .../Context/ExcaliburGraphicsContext.ts | 33 ++++++++ .../ExcaliburGraphicsContext2DCanvas.ts | 46 +++++++++++ .../Context/ExcaliburGraphicsContextWebGL.ts | 6 ++ src/engine/Graphics/Context/debug-font.png | Bin 0 -> 1544 bytes src/engine/Graphics/Context/debug-text.ts | 44 ++++++++++ src/engine/Scene.ts | 2 + 10 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 src/engine/Debug/DebugSystem.ts create mode 100644 src/engine/Graphics/Context/debug-font.png create mode 100644 src/engine/Graphics/Context/debug-text.ts diff --git a/src/engine/Debug.ts b/src/engine/Debug.ts index c8d038b9c7..a25e62a0b2 100644 --- a/src/engine/Debug.ts +++ b/src/engine/Debug.ts @@ -1,6 +1,7 @@ import { DebugFlags, ColorBlindFlags } from './DebugFlags'; import { Pair } from './Collision/Detection/Pair'; import { Engine } from './Engine'; +import { Color } from '.'; /** * Debug stats containing current and previous frame statistics @@ -195,6 +196,66 @@ export class Debug implements DebugFlags { * @warning Will reduce FPS. */ public colorBlindMode: ColorBlindFlags; + + public entity = { + showAll: false, + showId: false, + showName: false, + } + + public transform = { + showAll: false, + + showPosition: true, + positionColor: Color.Yellow, + + showScale: false, + scaleColor: Color.Green, + + showRotation: false, + rotationColor: Color.Blue + }; + public graphics = { + showAll: false, + + showBounds: false, + boundsColor: Color.Yellow + }; + public collider = { + showAll: false, + + showBounds: false, + boundsColor: Color.Blue, + + showGeometry: false, + geometryColor: Color.Green, + + }; + public motion = { + showAll: false, + + showVelocity: false, + velocityColor: Color.Yellow, + + showAcceleration: false, + accelerationColor: Color.Red + }; + + public body = { + showAll: false, + + showCollisionGroup: false, + showCollisionType: false + } + public camera = { + showAll: false, + + showFocus: false, + focusColor: Color.Black, + + showZoom: false, + zoomColor: Color.Black + } } /** diff --git a/src/engine/Debug/DebugSystem.ts b/src/engine/Debug/DebugSystem.ts new file mode 100644 index 0000000000..37b57af4ce --- /dev/null +++ b/src/engine/Debug/DebugSystem.ts @@ -0,0 +1,78 @@ +import { BodyComponent, ColliderComponent, Vector } from ".."; +import { Camera } from "../Camera"; +import { Engine } from "../Engine"; +import { Entity, TransformComponent } from "../EntityComponentSystem"; +import { MotionComponent } from "../EntityComponentSystem/Components/MotionComponent"; +import { System, SystemType } from "../EntityComponentSystem/System"; +import { ExcaliburGraphicsContext } from "../Graphics/Context/ExcaliburGraphicsContext"; +import { Scene } from "../Scene"; + + +export class DebugSystem extends System { + public readonly types = ['ex.transform'] as const; + public readonly systemType = SystemType.Draw; + private _graphicsContext: ExcaliburGraphicsContext; + private _camera: Camera; + private _engine: Engine; + + public initialize(scene: Scene): void { + this._graphicsContext = scene.engine.graphicsContext; + this._camera = scene.camera; + this._engine = scene.engine; + } + update(entities: Entity[], _delta: number): void { + if (!this._engine.isDebug) { + return; + } + let id: number; + let killed: boolean; + let name: string; + let entitySettings = this._engine.debug.entity; + let tx: TransformComponent; + let txSettings = this._engine.debug.transform; + let motion: MotionComponent; + let motionSettings = this._engine.debug.motion; + let collider: ColliderComponent; + let colliderSettings = this._engine.debug.collider; + let body: BodyComponent; + let bodySettings = this._engine.debug.body; + let offscreen: boolean; + for (let entity of entities) { + id = entity.id; + killed = entity.isKilled(); + name = entity.name + offscreen = entity.hasTag('offscreen'); + + tx = entity.get(TransformComponent); + if (tx) { + if (txSettings.showAll || txSettings.showPosition) { + this._graphicsContext.drawCircle(tx.pos, 5, txSettings.positionColor); + this._graphicsContext.debug.drawText(tx.pos.toString(), tx.pos); + } + if (txSettings.showAll || txSettings.showRotation) { + this._graphicsContext.drawLine(tx.pos, Vector.fromAngle(tx.rotation).scale(10).add(tx.pos), txSettings.rotationColor, 2); + } + if (txSettings.showAll || txSettings.showScale) { + this._graphicsContext.drawLine(tx.pos, tx.scale.add(tx.pos), txSettings.scaleColor, 2); + } + } + + motion = entity.get(MotionComponent); + if (motion) { + + } + + collider = entity.get(ColliderComponent); + if (collider) { + + } + + body = entity.get(BodyComponent); + if (body) { + + } + + } + } + +} \ No newline at end of file diff --git a/src/engine/EntityComponentSystem/Entity.ts b/src/engine/EntityComponentSystem/Entity.ts index a5162be0a7..4f7906668c 100644 --- a/src/engine/EntityComponentSystem/Entity.ts +++ b/src/engine/EntityComponentSystem/Entity.ts @@ -60,8 +60,9 @@ export function isRemovedComponent(x: Message): x is RemovedCom export class Entity extends Class implements OnInitialize, OnPreUpdate, OnPostUpdate { private static _ID = 0; - constructor(components?: Component[]) { + constructor(components?: Component[], name?: string) { super(); + this._name = name ?? this._name; if (components) { for (const component of components) { this.addComponent(component); @@ -74,6 +75,11 @@ export class Entity extends Class implements OnInitialize, OnPreUpdate, OnPostUp */ public id: number = Entity._ID++; + private _name: string = 'anonymous'; + public get name(): string { + return this._name; + } + public get events(): EventDispatcher { return this.eventDispatcher; } diff --git a/src/engine/EntityComponentSystem/EntityManager.ts b/src/engine/EntityComponentSystem/EntityManager.ts index 5b86f2ba5d..b6b6373847 100644 --- a/src/engine/EntityComponentSystem/EntityManager.ts +++ b/src/engine/EntityComponentSystem/EntityManager.ts @@ -126,6 +126,10 @@ export class EntityManager implements Observer e.name === name); + } + public clear(): void { for (const entity of this.entities) { this.removeEntity(entity); diff --git a/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts b/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts index 9e26627c89..74997fa0d3 100644 --- a/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts +++ b/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts @@ -50,6 +50,13 @@ export interface DebugDraw { * @param pointOptions */ drawPoint(point: Vector, pointOptions?: PointGraphicsOptions): void; + + /** + * Draw debug text + * @param text + * @param pos + */ + drawText(text: string, pos: Vector): void; } export interface ExcaliburGraphicsContext { @@ -117,6 +124,32 @@ export interface ExcaliburGraphicsContext { dheight?: number ): void; + /** + * Draw a solid line to the Excalibur Graphics context + * @param start + * @param end + * @param color + * @param thickness + */ + drawLine(start: Vector, end: Vector, color: Color, thickness: number): void; + + /** + * Draw a solid rectangle to the Excalibur Graphics context + * @param pos + * @param width + * @param height + * @param color + */ + drawRectangle(pos: Vector, width: number, height: number, color: Color): void; + + /** + * Draw a solid circle to the Excalibur Graphics context + * @param pos + * @param radius + * @param color + */ + drawCircle(pos: Vector, radius: number, color: Color): void; + /** * Save the current state of the canvas to the stack (transforms and opacity) */ diff --git a/src/engine/Graphics/Context/ExcaliburGraphicsContext2DCanvas.ts b/src/engine/Graphics/Context/ExcaliburGraphicsContext2DCanvas.ts index 03e7a7b866..7279b4f8e7 100644 --- a/src/engine/Graphics/Context/ExcaliburGraphicsContext2DCanvas.ts +++ b/src/engine/Graphics/Context/ExcaliburGraphicsContext2DCanvas.ts @@ -10,8 +10,10 @@ import { Vector } from '../../Algebra'; import { Color } from '../../Color'; import { StateStack } from './state-stack'; import { GraphicsDiagnostics } from '../GraphicsDiagnostics'; +import { DebugText } from './debug-text'; class ExcaliburGraphicsContext2DCanvasDebug implements DebugDraw { + private _debugText = new DebugText(); constructor(private _ex: ExcaliburGraphicsContext2DCanvas) {} /** * Draw a debug rectangle to the context @@ -59,6 +61,10 @@ class ExcaliburGraphicsContext2DCanvasDebug implements DebugDraw { this._ex.__ctx.closePath(); this._ex.__ctx.restore(); } + + drawText(text: string, pos: Vector) { + this._debugText.write(this._ex, text, pos); + } } export class ExcaliburGraphicsContext2DCanvas implements ExcaliburGraphicsContext { @@ -169,6 +175,46 @@ export class ExcaliburGraphicsContext2DCanvas implements ExcaliburGraphicsContex GraphicsDiagnostics.DrawnImagesCount = 1; } + public drawLine(start: Vector, end: Vector, color: Color, thickness = 1) { + this.__ctx.save(); + this.__ctx.beginPath(); + this.__ctx.strokeStyle = color.toString(); + this.__ctx.moveTo(this.snapToPixel ? ~~start.x : start.x, this.snapToPixel ? ~~start.y : start.y); + this.__ctx.lineTo(this.snapToPixel ? ~~end.x : end.x, this.snapToPixel ? ~~end.y : end.y); + this.__ctx.lineWidth = thickness; + this.__ctx.stroke(); + this.__ctx.closePath(); + this.__ctx.restore(); + } + + public drawRectangle(pos: Vector, width: number, height: number, color: Color) { + this.__ctx.save(); + this.__ctx.fillStyle = color.toString(); + this.__ctx.strokeRect( + this.snapToPixel ? ~~pos.x : pos.x, + this.snapToPixel ? ~~pos.y : pos.y, + this.snapToPixel ? ~~width : width, + this.snapToPixel ? ~~height : height + ); + this.__ctx.restore(); + } + + public drawCircle(pos: Vector, radius: number, color: Color) { + this.__ctx.save(); + this.__ctx.beginPath(); + this.__ctx.fillStyle = color.toString(); + this.__ctx.arc( + this.snapToPixel ? ~~pos.x : pos.x, + this.snapToPixel ? ~~pos.y : pos.y, + radius, + 0, + Math.PI * 2 + ); + this.__ctx.fill(); + this.__ctx.closePath(); + this.__ctx.restore(); + } + debug = new ExcaliburGraphicsContext2DCanvasDebug(this); /** diff --git a/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts b/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts index 19e7d9c620..9db65ca8f1 100644 --- a/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts +++ b/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts @@ -19,8 +19,10 @@ import { ImageRenderer } from './image-renderer'; import { PointRenderer } from './point-renderer'; import { Canvas } from '../Canvas'; import { GraphicsDiagnostics } from '../GraphicsDiagnostics'; +import { DebugText } from './debug-text'; class ExcaliburGraphicsContextWebGLDebug implements DebugDraw { + private _debugText = new DebugText(); constructor(private _webglCtx: ExcaliburGraphicsContextWebGL) {} /** @@ -55,6 +57,10 @@ class ExcaliburGraphicsContextWebGLDebug implements DebugDraw { drawPoint(point: Vector, pointOptions: PointGraphicsOptions = { color: Color.Black, size: 5 }): void { this._webglCtx.__pointRenderer.addPoint(point, pointOptions.color, pointOptions.size); } + + drawText(text: string, pos: Vector) { + this._debugText.write(this._webglCtx, text, pos); + } } export interface WebGLGraphicsContextInfo { diff --git a/src/engine/Graphics/Context/debug-font.png b/src/engine/Graphics/Context/debug-font.png new file mode 100644 index 0000000000000000000000000000000000000000..c4873941acec988b86e83217154912f88f608f69 GIT binary patch literal 1544 zcmV+j2KV`iP)Px)!bwCyRCt{2o$0RZC=7&QH1Ge)nLka2umx8+5S`Q~wRBJ4_QGaN0!au$2qAmMwl~N>)^{iM z+{))c;NOvw6i8x>;rI7(V|?0?8M!==8GVSw?2+qx+W-F*G$sP8?Q#-A?OP_GUj0wj zy+`|9bb$TO%|0Mc%a;CUQ1WF4@J~ai?D#P+NC=^uUX->5naHgi*m*B{a#?ingJlTG zcK!)-b;sJy>yN1ewBA{&tP$*gZuUc?P7Qea&o)RU-^&1Y6N7Ua%FbX0F4;$J-#QC4 z%0DZceuFH*Vm=7~b#cl&1rT_^ko~7phx&m+wEwjHTLS<+_%oRhg3ka1(ztUVPX7J^ z=|6S-r5x~?F7pyBm$%@#1VhvCTwPK{Ib|`3FCnMpt0h2;v753WgK*-z>Nn-ZpR6F$ z@+3<6Agwl7K&$_kZE67x#7$j*Ow2wNy$qmAi^}-b0>z%}r?;XeF2{+PqnPBzCeOXxCeq$>^jw3--u1lxtolIt z*VzYbSN(3o_?cq(JOC$$cW*1C{NFYBOZ&Uz!~PDt+devYTfg02mXev{z~$+@>B9({ z9w-q4xBfoaJ@~!Y)ecSvg5U|{fx}p?2XUSz9e*CR|IA$g^vOQEj&m|cH?NA;K8dDl zpQ-3IOCL!>;*8-l=*eIWZN4ne@9{nN*8 zoV*SMw4dkGj66WZWmJ#!3UifF`A(+tR6xvq0D}ly`PK~1yZ)>+ z``&N%3IV7;WBa|X{Gk0)&q!``8!&`+Wx3vmoYZ5n-IKM*KXo5{R{pQ-17HSAtL91)AwG-(fDmJ#+qZ<=QWOA$=!0%5^V*E| zco0zl5TXrpCdOkko!gGq5#7^(4WpK5#LBmRkJxX0R>Ro!klmSvmhFeg?hJbG4mPS{`!XH-|HD>0C^`eQq5k#K?e5ajEwzLEcs)U^B@w@ z{;a>O+^c8Li~m4b(r>MSaP&B?S7#8v^!~{H4k1)q^IiRa>MlU@K z3VQ?E*yo@r9xG-o2Rn1d&cCPa0~t*9dUxh|1S`48deCfW$ZY@sE;zIXUkG8@)zF@O zYiIVj*b+B%zRUVK1p%}jcLzZ92zTcr3Wfn7RX#cpSa}bK^Z4i*ywb6F5S`Zm(kK6W zp&uTgnG1{Cef$vM40Orl(K`-&-f|dy*MpV6(={v8!m7vWZ_gK24{A5rX6kXZ@1+mB u?C009j>hA(yyb)tFXQ;JN=^tNmhm58mWxZ|fY|K-0000 { + this._spriteSheet = SpriteSheet.fromGrid({ + image: this._imageSource, + grid: { + rows: 3, + columns: 16, + spriteWidth: 16, + spriteHeight: 16 + } + }) + this._spriteFont = new SpriteFont({ + alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ,!\'&."?-()+', + caseInsensitive: true, + spriteSheet: this._spriteSheet + }); + }); + } + + public write(ctx: ExcaliburGraphicsContext, text: string, pos: Vector) { + if (this._imageSource.isLoaded()) { + this._spriteFont.render(ctx, text, pos.x, pos.y); + } + } +} \ No newline at end of file diff --git a/src/engine/Scene.ts b/src/engine/Scene.ts index 3e683c7b74..db710823e9 100644 --- a/src/engine/Scene.ts +++ b/src/engine/Scene.ts @@ -32,6 +32,7 @@ import { Entity } from './EntityComponentSystem/Entity'; import { GraphicsSystem } from './Graphics/GraphicsSystem'; import { CanvasDrawingSystem } from './Drawing/CanvasDrawingSystem'; import { Flags, Legacy } from './Flags'; +import { DebugSystem } from './Debug/DebugSystem'; /** * [[Actor|Actors]] are composed together into groupings called Scenes in * Excalibur. The metaphor models the same idea behind real world @@ -114,6 +115,7 @@ export class Scene extends Class implements CanInitialize, CanActivate, CanDeact } else { this.world.add(new GraphicsSystem()); } + this.world.add(new DebugSystem()); } public on(eventName: Events.initialize, handler: (event: InitializeEvent) => void): void; From f2f675aae7a0bd24708074955fef5fad160e559d Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Fri, 3 Sep 2021 11:45:59 -0500 Subject: [PATCH 04/18] Fix vector --- src/engine/Math/vector.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/Math/vector.ts b/src/engine/Math/vector.ts index daf045d271..de41d1c23c 100644 --- a/src/engine/Math/vector.ts +++ b/src/engine/Math/vector.ts @@ -98,7 +98,7 @@ export class Vector implements Clonable { this._y = y; } - private _x = 0; + protected _x = 0; /** * Get the x component of the vector */ @@ -114,7 +114,7 @@ export class Vector implements Clonable { this._x = val; } - private _y = 0; + protected _y = 0; /** * Get the y component of the vector */ From 8a4183a8a1746518f2f9f0acfdf4dc8e9a8fa7e5 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Fri, 3 Sep 2021 20:57:06 -0500 Subject: [PATCH 05/18] WIP Debugger improvment --- src/engine/Actor.ts | 88 ++---- src/engine/Camera.ts | 28 +- src/engine/Collision/BodyComponent.ts | 23 +- src/engine/Collision/Shapes/CircleCollider.ts | 12 +- src/engine/Collision/Shapes/Collider.ts | 2 + .../Collision/Shapes/CompositeCollider.ts | 9 + src/engine/Collision/Shapes/ConvexPolygon.ts | 18 +- src/engine/Collision/Shapes/Edge.ts | 22 +- src/engine/Debug.ts | 57 ++-- src/engine/Debug/DebugSystem.ts | 250 ++++++++++++++++-- .../Components/TransformComponent.ts | 10 + src/engine/EntityComponentSystem/Entity.ts | 6 +- src/engine/Graphics/Context/debug-text.ts | 10 +- .../Graphics/Context/draw-image-command.ts | 22 +- src/engine/Graphics/Context/image-renderer.ts | 60 ++--- src/engine/Graphics/Context/line-renderer.ts | 4 +- src/engine/Graphics/GraphicsSystem.ts | 44 +-- src/engine/Math/vector.ts | 5 +- src/engine/Util/Pool.ts | 7 +- src/spec/CollisionShapeSpec.ts | 11 +- src/spec/ExcaliburGraphicsContextSpec.ts | 2 +- src/spec/PoolSpec.ts | 48 +++- src/spec/images/CollisionShapeSpec/circle.png | Bin 9171 -> 9690 bytes 23 files changed, 460 insertions(+), 278 deletions(-) diff --git a/src/engine/Actor.ts b/src/engine/Actor.ts index 597b872f92..203bf42180 100644 --- a/src/engine/Actor.ts +++ b/src/engine/Actor.ts @@ -44,7 +44,6 @@ import { Entity } from './EntityComponentSystem/Entity'; import { CanvasDrawComponent } from './Drawing/CanvasDrawComponent'; import { TransformComponent } from './EntityComponentSystem/Components/TransformComponent'; import { MotionComponent } from './EntityComponentSystem/Components/MotionComponent'; -import { Debug } from './Debug'; import { GraphicsComponent } from './Graphics/GraphicsComponent'; import { Rectangle } from './Graphics/Rectangle'; import { Flags, Legacy } from './Flags'; @@ -66,6 +65,10 @@ export function isActor(x: any): x is Actor { * Actor contructor options */ export interface ActorArgs { + /** + * Optionally set the name of the actor, default is 'anonymous' + */ + name?: string; /** * Optionally set the x position of the actor, default is 0 */ @@ -486,10 +489,29 @@ export class Actor extends Entity implements Actionable, Eventable, PointerEvent constructor(config?: ActorArgs) { super(); - const { x, y, pos, scale, width, height, collider, vel, acc, rotation, angularVelocity, z, color, visible, anchor, collisionType } = { + const { + name, + x, + y, + pos, + scale, + width, + height, + collider, + vel, + acc, + rotation, + angularVelocity, + z, + color, + visible, + anchor, + collisionType + } = { ...config }; + this._setName(name); this.anchor = anchor ?? Actor.defaults.anchor.clone(); this.addComponent(new TransformComponent()); @@ -1228,66 +1250,8 @@ export class Actor extends Entity implements Actionable, Eventable, PointerEvent * @param ctx The rendering context */ /* istanbul ignore next */ - public debugDraw(ctx: CanvasRenderingContext2D) { - this.emit('predebugdraw', new PreDebugDrawEvent(ctx, this)); - - // Draw actor Id - if (Debug.showActorId) { - ctx.fillText('id: ' + this.id, this.collider.bounds.left + 3, this.collider.bounds.top + 10); - } - - // Draw actor anchor Vector - if (Debug.showActorAnchor) { - ctx.fillStyle = Color.Yellow.toString(); - ctx.beginPath(); - ctx.arc(this.getGlobalPos().x, this.getGlobalPos().y, 3, 0, Math.PI * 2); - ctx.closePath(); - ctx.fill(); - } - - // Culling Box debug draw - for (let j = 0; j < this.traits.length; j++) { - if (this.traits[j] instanceof Traits.OffscreenCulling && Debug.showDrawingCullBox) { - (this.traits[j]).cullingBox.debugDraw(ctx); // eslint-disable-line - } - } - - // Unit Circle debug draw - if (Debug.showActorUnitCircle) { - ctx.strokeStyle = Color.Yellow.toString(); - ctx.beginPath(); - const radius = Math.min(this.width, this.height); - ctx.arc(this.getGlobalPos().x, this.getGlobalPos().y, radius, 0, Math.PI * 2); - ctx.closePath(); - ctx.stroke(); - const ticks: { [key: string]: number } = { - '0 Pi': 0, - 'Pi/2': Math.PI / 2, - Pi: Math.PI, - '3/2 Pi': (3 * Math.PI) / 2 - }; - - const oldFont = ctx.font; - for (const tick in ticks) { - ctx.fillStyle = Color.Yellow.toString(); - ctx.font = '14px'; - ctx.textAlign = 'center'; - ctx.fillText( - tick, - this.getGlobalPos().x + Math.cos(ticks[tick]) * (radius + 10), - this.getGlobalPos().y + Math.sin(ticks[tick]) * (radius + 10) - ); - } - - ctx.font = oldFont; - } - - // Draw child actors - // for (let i = 0; i < this.children.length; i++) { - // this.children[i].debugDraw(ctx); - // } - - this.emit('postdebugdraw', new PostDebugDrawEvent(ctx, this)); + public debugDraw(_ctx: CanvasRenderingContext2D) { + // pass } // #endregion } diff --git a/src/engine/Camera.ts b/src/engine/Camera.ts index 809925728c..6f5a1211e3 100644 --- a/src/engine/Camera.ts +++ b/src/engine/Camera.ts @@ -8,7 +8,6 @@ import { PreUpdateEvent, PostUpdateEvent, GameEvent, InitializeEvent } from './E import { Class } from './Class'; import { BoundingBox } from './Collision/BoundingBox'; import { Logger } from './Util/Log'; -import { Debug } from './Debug'; import { ExcaliburGraphicsContext } from './Graphics/Context/ExcaliburGraphicsContext'; import { watchAny } from './Util/Watch'; @@ -739,31 +738,8 @@ export class Camera extends Class implements CanUpdate, CanInitialize { } /* istanbul ignore next */ - public debugDraw(ctx: CanvasRenderingContext2D) { - if (Debug.showCameraFocus) { - const focus = this.getFocus(); - ctx.fillStyle = 'red'; - ctx.strokeStyle = 'white'; - ctx.lineWidth = 3; - ctx.beginPath(); - ctx.arc(focus.x, focus.y, 15, 0, Math.PI * 2); - ctx.closePath(); - ctx.stroke(); - - ctx.beginPath(); - ctx.arc(focus.x, focus.y, 5, 0, Math.PI * 2); - ctx.closePath(); - ctx.stroke(); - } - - if (Debug.showCameraViewport) { - ctx.beginPath(); - ctx.setLineDash([5, 15]); - ctx.lineWidth = 5; - ctx.strokeStyle = 'white'; - ctx.strokeRect(this.viewport.left, this.viewport.top, this.viewport.width, this.viewport.height); - ctx.closePath(); - } + public debugDraw(_ctx: CanvasRenderingContext2D) { + // pass } private _isDoneShaking(): boolean { diff --git a/src/engine/Collision/BodyComponent.ts b/src/engine/Collision/BodyComponent.ts index cf500384bd..7131bc27ad 100644 --- a/src/engine/Collision/BodyComponent.ts +++ b/src/engine/Collision/BodyComponent.ts @@ -9,8 +9,6 @@ import { CollisionGroup } from './Group/CollisionGroup'; import { EventDispatcher } from '../EventDispatcher'; import { createId, Id } from '../Id'; import { clamp } from '../Util/Util'; -import { DrawUtil } from '../Util/Index'; -import { Color } from '../Color'; import { ColliderComponent } from './ColliderComponent'; export interface BodyComponentOptions { @@ -402,24 +400,7 @@ export class BodyComponent extends Component<'ex.body'> implements Clonable { */ abstract draw(ctx: CanvasRenderingContext2D, color?: Color, pos?: Vector): void; + abstract debug(ex: ExcaliburGraphicsContext, color: Color): void; /** * Draw any debug information */ diff --git a/src/engine/Collision/Shapes/CompositeCollider.ts b/src/engine/Collision/Shapes/CompositeCollider.ts index 44e31b0a07..99bda5a2bf 100644 --- a/src/engine/Collision/Shapes/CompositeCollider.ts +++ b/src/engine/Collision/Shapes/CompositeCollider.ts @@ -1,5 +1,6 @@ import { Color } from '../../Color'; import { Transform } from '../../EntityComponentSystem'; +import { ExcaliburGraphicsContext } from '../../Graphics/Context/ExcaliburGraphicsContext'; import { Line } from '../../Math/line'; import { Projection } from '../../Math/projection'; import { Ray } from '../../Math/ray'; @@ -227,6 +228,14 @@ export class CompositeCollider extends Collider { collider.draw(ctx, color, pos); } } + + public debug(ex: ExcaliburGraphicsContext, color: Color) { + const colliders = this.getColliders(); + for (const collider of colliders) { + collider.debug(ex, color); + } + } + debugDraw(ctx: CanvasRenderingContext2D, color: Color): void { const colliders = this.getColliders(); for (const collider of colliders) { diff --git a/src/engine/Collision/Shapes/ConvexPolygon.ts b/src/engine/Collision/Shapes/ConvexPolygon.ts index e460d76513..3709db9252 100644 --- a/src/engine/Collision/Shapes/ConvexPolygon.ts +++ b/src/engine/Collision/Shapes/ConvexPolygon.ts @@ -11,6 +11,7 @@ import { Ray } from '../../Math/ray'; import { ClosestLineJumpTable } from './ClosestLineJumpTable'; import { Transform, TransformComponent } from '../../EntityComponentSystem'; import { Collider } from './Collider'; +import { ExcaliburGraphicsContext } from '../..'; export interface ConvexPolygonOptions { /** @@ -325,10 +326,10 @@ export class ConvexPolygon extends Collider { * Get the axis aligned bounding box for the polygon collider in world coordinates */ public get bounds(): BoundingBox { - // const points = this.getTransformedPoints(); - const scale = this._transform?.scale ?? Vector.One; - const rotation = this._transform?.rotation ?? 0; - const pos = (this._transform?.pos ?? Vector.Zero).add(this.offset); + const tx = this._transform as TransformComponent; + const scale = tx?.globalScale ?? Vector.One; + const rotation = tx?.globalRotation ?? 0; + const pos = (tx?.globalPos ?? Vector.Zero).add(this.offset); return this.localBounds.scale(scale).rotate(rotation).translate(pos); } @@ -419,6 +420,15 @@ export class ConvexPolygon extends Collider { ctx.closePath(); ctx.fill(); } + public debug(ex: ExcaliburGraphicsContext, color: Color) { + const firstPoint = this.getTransformedPoints()[0]; + const points = [firstPoint, ...this.getTransformedPoints(), firstPoint]; + for (let i = 0; i < points.length - 1; i++) { + ex.drawLine(points[i], points[i + 1], color, 2); + ex.drawCircle(points[i], 2, color); + ex.drawCircle(points[i + 1], 2, color); + } + } /* istanbul ignore next */ public debugDraw(ctx: CanvasRenderingContext2D, color: Color = Color.Red) { diff --git a/src/engine/Collision/Shapes/Edge.ts b/src/engine/Collision/Shapes/Edge.ts index 9de9a565f0..f26dfe6408 100644 --- a/src/engine/Collision/Shapes/Edge.ts +++ b/src/engine/Collision/Shapes/Edge.ts @@ -11,7 +11,8 @@ import { Ray } from '../../Math/ray'; import { Color } from '../../Color'; import { Collider } from './Collider'; import { ClosestLineJumpTable } from '../Shapes/ClosestLineJumpTable'; -import { Transform } from '../../EntityComponentSystem/Components/TransformComponent'; +import { Transform, TransformComponent } from '../../EntityComponentSystem/Components/TransformComponent'; +import { ExcaliburGraphicsContext } from '../../Graphics/Context/ExcaliburGraphicsContext'; export interface EdgeOptions { /** @@ -64,19 +65,20 @@ export class Edge extends Collider { } private _getBodyPos(): Vector { - const bodyPos = this._transform?.pos ?? Vector.Zero; + const tx = this._transform as TransformComponent; + const bodyPos = tx?.globalPos ?? Vector.Zero; return bodyPos; } private _getTransformedBegin(): Vector { - const transform = this._transform; - const angle = transform ? transform.rotation : 0; + const tx = this._transform as TransformComponent; + const angle = tx ? tx.globalRotation : 0; return this.begin.rotate(angle).add(this._getBodyPos()); } private _getTransformedEnd(): Vector { - const transform = this._transform; - const angle = transform ? transform.rotation : 0; + const tx = this._transform as TransformComponent; + const angle = tx ? tx.globalRotation : 0; return this.end.rotate(angle).add(this._getBodyPos()); } @@ -271,6 +273,14 @@ export class Edge extends Collider { ctx.stroke(); } + public debug(ex: ExcaliburGraphicsContext, color: Color) { + const begin = this._getTransformedBegin(); + const end = this._getTransformedEnd(); + ex.drawLine(begin, end, color, 2); + ex.drawCircle(begin, 2, color); + ex.drawCircle(end, 2, color); + } + /* istanbul ignore next */ public debugDraw(ctx: CanvasRenderingContext2D, color: Color = Color.Red) { const begin = this._getTransformedBegin(); diff --git a/src/engine/Debug.ts b/src/engine/Debug.ts index a25e62a0b2..509bfe83e2 100644 --- a/src/engine/Debug.ts +++ b/src/engine/Debug.ts @@ -167,13 +167,6 @@ export class Debug implements DebugFlags { this.colorBlindMode = new ColorBlindFlags(this._engine); } - public static showDrawingCullBox = false; - public static showCameraFocus = false; - public static showCameraViewport = false; - public static showActorAnchor = false; - public static showActorId = false; - public static showActorUnitCircle = false; - /** * Performance statistics */ @@ -197,40 +190,50 @@ export class Debug implements DebugFlags { */ public colorBlindMode: ColorBlindFlags; + public filter: { useFilter: boolean; nameQuery: string; ids: number[] } = { + useFilter: false, + nameQuery: '', + ids: [] + }; + public entity = { showAll: false, - showId: false, - showName: false, - } + showId: true, + showName: false + }; public transform = { showAll: false, - - showPosition: true, + + showPosition: false, positionColor: Color.Yellow, - + showScale: false, scaleColor: Color.Green, - + showRotation: false, rotationColor: Color.Blue }; + public graphics = { showAll: false, - - showBounds: false, + + showBounds: true, boundsColor: Color.Yellow }; + public collider = { showAll: false, - - showBounds: false, + + showBounds: true, boundsColor: Color.Blue, - - showGeometry: false, - geometryColor: Color.Green, + showOwner: false, + + showGeometry: true, + geometryColor: Color.Green }; + public motion = { showAll: false, @@ -245,8 +248,12 @@ export class Debug implements DebugFlags { showAll: false, showCollisionGroup: false, - showCollisionType: false - } + showCollisionType: false, + showSleeping: false, + showMotion: false, + showMass: false + }; + public camera = { showAll: false, @@ -255,7 +262,7 @@ export class Debug implements DebugFlags { showZoom: false, zoomColor: Color.Black - } + }; } /** @@ -290,7 +297,7 @@ export class FrameStats implements FrameStatistics { private _graphicsStats: GraphicsStatistics = { drawCalls: 0, drawnImages: 0 - } + }; /** * Zero out values or clone other IFrameStat stats. Allows instance reuse. diff --git a/src/engine/Debug/DebugSystem.ts b/src/engine/Debug/DebugSystem.ts index 37b57af4ce..6d61b65de6 100644 --- a/src/engine/Debug/DebugSystem.ts +++ b/src/engine/Debug/DebugSystem.ts @@ -1,16 +1,18 @@ -import { BodyComponent, ColliderComponent, Vector } from ".."; -import { Camera } from "../Camera"; -import { Engine } from "../Engine"; -import { Entity, TransformComponent } from "../EntityComponentSystem"; -import { MotionComponent } from "../EntityComponentSystem/Components/MotionComponent"; -import { System, SystemType } from "../EntityComponentSystem/System"; -import { ExcaliburGraphicsContext } from "../Graphics/Context/ExcaliburGraphicsContext"; -import { Scene } from "../Scene"; - +import { Engine } from '../Engine'; +import { Scene } from '../Scene'; +import { Camera } from '../Camera'; +import { MotionComponent } from '../EntityComponentSystem/Components/MotionComponent'; +import { ColliderComponent } from '../Collision/ColliderComponent'; +import { CoordPlane, Entity, TransformComponent } from '../EntityComponentSystem'; +import { System, SystemType } from '../EntityComponentSystem/System'; +import { ExcaliburGraphicsContext } from '../Graphics/Context/ExcaliburGraphicsContext'; +import { vec, Vector } from '../Math/vector'; +import { BodyComponent, CompositeCollider, GraphicsComponent, Particle, Util } from '..'; export class DebugSystem extends System { public readonly types = ['ex.transform'] as const; public readonly systemType = SystemType.Draw; + public priority = 999; // lowest priority private _graphicsContext: ExcaliburGraphicsContext; private _camera: Camera; private _engine: Engine; @@ -24,55 +26,245 @@ export class DebugSystem extends System { if (!this._engine.isDebug) { return; } + + const filterSettings = this._engine.debug.filter; + let id: number; - let killed: boolean; + // let killed: boolean; let name: string; - let entitySettings = this._engine.debug.entity; + const entitySettings = this._engine.debug.entity; + let tx: TransformComponent; - let txSettings = this._engine.debug.transform; + const txSettings = this._engine.debug.transform; + let motion: MotionComponent; - let motionSettings = this._engine.debug.motion; + const motionSettings = this._engine.debug.motion; + let collider: ColliderComponent; - let colliderSettings = this._engine.debug.collider; + const colliderSettings = this._engine.debug.collider; + + let graphics: GraphicsComponent; + const graphicsSettings = this._engine.debug.graphics; + let body: BodyComponent; - let bodySettings = this._engine.debug.body; - let offscreen: boolean; - for (let entity of entities) { + const bodySettings = this._engine.debug.body; + // let offscreen: boolean; + for (const entity of entities) { + if (entity.hasTag('offscreen')) { + // skip offscreen entities + continue; + } + if (entity instanceof Particle) { + // Particles crush the renderer :( + continue; + } + if (filterSettings.useFilter) { + const allIds = filterSettings.ids.length === 0; + const idMatch = allIds || filterSettings.ids.includes(entity.id); + if (!idMatch) { + continue; + } + const allNames = filterSettings.nameQuery === ''; + const nameMatch = allNames || entity.name.includes(filterSettings.nameQuery); + if (!nameMatch) { + continue; + } + } + + let cursor = Vector.Zero; + const lineHeight = vec(0, 16); id = entity.id; - killed = entity.isKilled(); - name = entity.name - offscreen = entity.hasTag('offscreen'); - + name = entity.name; tx = entity.get(TransformComponent); + + // This optionally sets our camera based on the entity coord plan (world vs. screen) + this._pushCameraTransform(tx); + + this._graphicsContext.save(); + + this._applyTransform(entity); if (tx) { if (txSettings.showAll || txSettings.showPosition) { - this._graphicsContext.drawCircle(tx.pos, 5, txSettings.positionColor); - this._graphicsContext.debug.drawText(tx.pos.toString(), tx.pos); + this._graphicsContext.debug.drawPoint(Vector.Zero, { size: 2, color: txSettings.positionColor }); + this._graphicsContext.debug.drawText(`pos${tx.pos.toString(2)}`, cursor); + cursor = cursor.add(lineHeight); + } + + if (entitySettings.showAll || entitySettings.showId) { + this._graphicsContext.debug.drawText(`id(${id}) ${tx.parent ? 'child of id(' + tx.parent?.owner?.id + ')' : ''}`, cursor); + cursor = cursor.add(lineHeight); } + + if (entitySettings.showAll || entitySettings.showName) { + this._graphicsContext.debug.drawText(`name(${name})`, cursor); + cursor = cursor.add(lineHeight); + } + if (txSettings.showAll || txSettings.showRotation) { - this._graphicsContext.drawLine(tx.pos, Vector.fromAngle(tx.rotation).scale(10).add(tx.pos), txSettings.rotationColor, 2); + this._graphicsContext.drawLine( + Vector.Zero, + Vector.fromAngle(tx.rotation).scale(50).add(Vector.Zero), + txSettings.rotationColor, + 2 + ); + this._graphicsContext.debug.drawText(`rot deg(${Util.toDegrees(tx.rotation).toFixed(2)})`, cursor); + cursor = cursor.add(lineHeight); } + if (txSettings.showAll || txSettings.showScale) { - this._graphicsContext.drawLine(tx.pos, tx.scale.add(tx.pos), txSettings.scaleColor, 2); + this._graphicsContext.drawLine(Vector.Zero, tx.scale.add(Vector.Zero), txSettings.scaleColor, 2); } } - + motion = entity.get(MotionComponent); if (motion) { + if (motionSettings.showAll || motionSettings.showVelocity) { + this._graphicsContext.debug.drawText(`vel${motion.vel.toString(2)}`, cursor); + this._graphicsContext.drawLine(Vector.Zero, motion.vel, motionSettings.velocityColor, 2); + cursor = cursor.add(lineHeight); + } + + if (motionSettings.showAll || motionSettings.showAcceleration) { + this._graphicsContext.drawLine(Vector.Zero, motion.acc, motionSettings.accelerationColor, 2); + } + } + + graphics = entity.get(GraphicsComponent); + if (graphics) { + if (graphicsSettings.showAll || graphicsSettings.showBounds) { + const bounds = graphics.localBounds; + bounds.draw(this._graphicsContext, graphicsSettings.boundsColor); + } + } + + body = entity.get(BodyComponent); + if (body) { + if (bodySettings.showAll || bodySettings.showCollisionGroup) { + this._graphicsContext.debug.drawText(`collision group(${body.group.name})`, cursor); + cursor = cursor.add(lineHeight); + } + + if (bodySettings.showAll || bodySettings.showCollisionType) { + this._graphicsContext.debug.drawText(`collision type(${body.collisionType})`, cursor); + cursor = cursor.add(lineHeight); + } + if (bodySettings.showAll || bodySettings.showMass) { + this._graphicsContext.debug.drawText(`mass(${body.mass})`, cursor); + cursor = cursor.add(lineHeight); + } + + if (bodySettings.showAll || bodySettings.showMotion) { + this._graphicsContext.debug.drawText(`motion(${body.motion})`, cursor); + cursor = cursor.add(lineHeight); + } + + if (bodySettings.showAll || bodySettings.showSleeping) { + this._graphicsContext.debug.drawText(`sleeping(${body.canSleep ? 'cant sleep' : body.sleeping})`, cursor); + cursor = cursor.add(lineHeight); + } } + // if (Debug.showCameraFocus) { + // const focus = this.getFocus(); + // ctx.fillStyle = 'red'; + // ctx.strokeStyle = 'white'; + // ctx.lineWidth = 3; + // ctx.beginPath(); + // ctx.arc(focus.x, focus.y, 15, 0, Math.PI * 2); + // ctx.closePath(); + // ctx.stroke(); + + // ctx.beginPath(); + // ctx.arc(focus.x, focus.y, 5, 0, Math.PI * 2); + // ctx.closePath(); + // ctx.stroke(); + // } + + // if (Debug.showCameraViewport) { + // ctx.beginPath(); + // ctx.setLineDash([5, 15]); + // ctx.lineWidth = 5; + // ctx.strokeStyle = 'white'; + // ctx.strokeRect(this.viewport.left, this.viewport.top, this.viewport.width, this.viewport.height); + // ctx.closePath(); + // } + + this._graphicsContext.restore(); + + // Colliders live in world space already so after the restore() collider = entity.get(ColliderComponent); if (collider) { + if (colliderSettings.showAll || colliderSettings.showBounds) { + if (collider.collider instanceof CompositeCollider) { + const colliders = collider.collider.getColliders(); + for (const collider of colliders) { + const bounds = collider.bounds; + const pos = vec(bounds.left, bounds.top); + if (colliderSettings.showOwner) { + this._graphicsContext.debug.drawText(`owner id(${collider.owner.id})`, pos); + } + this._graphicsContext.debug.drawRect(pos.x, pos.y, bounds.width, bounds.height, { color: colliderSettings.boundsColor }); + } + } else { + const bounds = collider.bounds; + const pos = vec(bounds.left, bounds.top); + if (colliderSettings.showOwner) { + this._graphicsContext.debug.drawText(`owner id(${collider.owner.id})`, pos); + } + this._graphicsContext.debug.drawRect(pos.x, pos.y, bounds.width, bounds.height, { color: colliderSettings.boundsColor }); + } + } + if (colliderSettings.showAll || colliderSettings.showGeometry) { + collider.collider.debug(this._graphicsContext, colliderSettings.geometryColor); + } } - body = entity.get(BodyComponent); - if (body) { + this._popCameraTransform(tx); + + this._graphicsContext.flush(); + } + } + /** + * This applies the current entity transform to the graphics context + * @param entity + */ + private _applyTransform(entity: Entity): void { + const ancestors = entity.getAncestors(); + for (const ancestor of ancestors) { + const transform = ancestor?.get(TransformComponent); + if (transform) { + this._graphicsContext.translate(transform.pos.x, transform.pos.y); + this._graphicsContext.scale(transform.scale.x, transform.scale.y); + this._graphicsContext.rotate(transform.rotation); } + } + } + /** + * Applies the current camera transform if in world coordinates + * @param transform + */ + private _pushCameraTransform(transform: TransformComponent) { + // Establish camera offset per entity + if (transform.coordPlane === CoordPlane.World) { + this._graphicsContext.save(); + if (this._camera) { + this._camera.draw(this._graphicsContext); + } } } -} \ No newline at end of file + /** + * Resets the current camera transform if in world coordinates + * @param transform + */ + private _popCameraTransform(transform: TransformComponent) { + if (transform.coordPlane === CoordPlane.World) { + // Apply camera world offset + this._graphicsContext.restore(); + } + } +} diff --git a/src/engine/EntityComponentSystem/Components/TransformComponent.ts b/src/engine/EntityComponentSystem/Components/TransformComponent.ts index f3f971ef7e..bac94bc86f 100644 --- a/src/engine/EntityComponentSystem/Components/TransformComponent.ts +++ b/src/engine/EntityComponentSystem/Components/TransformComponent.ts @@ -106,6 +106,16 @@ export class TransformComponent extends Component<'ex.transform'> implements Tra } } + public getGlobalTransform(): Transform { + return { + pos: this.globalPos, + scale: this.globalScale, + rotation: this.globalRotation, + z: this.z, + coordPlane: this.coordPlane + }; + } + public get parent(): TransformComponent | null { return this?.owner?.parent?.get(TransformComponent); } diff --git a/src/engine/EntityComponentSystem/Entity.ts b/src/engine/EntityComponentSystem/Entity.ts index 4f7906668c..b8e515fdef 100644 --- a/src/engine/EntityComponentSystem/Entity.ts +++ b/src/engine/EntityComponentSystem/Entity.ts @@ -76,6 +76,11 @@ export class Entity extends Class implements OnInitialize, OnPreUpdate, OnPostUp public id: number = Entity._ID++; private _name: string = 'anonymous'; + protected _setName(name: string) { + if (name) { + this._name = name; + } + } public get name(): string { return this._name; } @@ -279,7 +284,6 @@ export class Entity extends Class implements OnInitialize, OnPreUpdate, OnPostUp return result; } - /** * Creates a deep copy of the entity and a copy of all its components */ diff --git a/src/engine/Graphics/Context/debug-text.ts b/src/engine/Graphics/Context/debug-text.ts index 66a484f5b6..bee8b68819 100644 --- a/src/engine/Graphics/Context/debug-text.ts +++ b/src/engine/Graphics/Context/debug-text.ts @@ -3,7 +3,6 @@ import { Vector } from '../..'; import debugFont from './debug-font.png'; export class DebugText { - constructor() { this.load(); } @@ -27,11 +26,12 @@ export class DebugText { spriteWidth: 16, spriteHeight: 16 } - }) + }); this._spriteFont = new SpriteFont({ - alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ,!\'&."?-()+', + alphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ,!\'&."?-()+ ', caseInsensitive: true, - spriteSheet: this._spriteSheet + spriteSheet: this._spriteSheet, + spacing: -6 }); }); } @@ -41,4 +41,4 @@ export class DebugText { this._spriteFont.render(ctx, text, pos.x, pos.y); } } -} \ No newline at end of file +} diff --git a/src/engine/Graphics/Context/draw-image-command.ts b/src/engine/Graphics/Context/draw-image-command.ts index 75a9a51748..a8c09a0b83 100644 --- a/src/engine/Graphics/Context/draw-image-command.ts +++ b/src/engine/Graphics/Context/draw-image-command.ts @@ -3,7 +3,7 @@ import { BoundingBox } from '../../Collision/Index'; import { Color } from '../../Color'; import { Pool, Poolable } from '../../Util/Pool'; import { HTMLImageSource } from './ExcaliburGraphicsContext'; -import { vec, Vector } from '../../Algebra'; +import { vec, Vector } from '../../Math/vector'; export enum DrawCommandType { Image = 'image', @@ -18,7 +18,7 @@ export class DrawImageCommand implements Poolable { public snapToPixel: boolean = true; public image: HTMLImageSource; public color: Color; - public type = DrawCommandType.Image + public type = DrawCommandType.Image; public opacity: number = 1; public width: number = 0; public height: number = 0; @@ -100,8 +100,9 @@ export class DrawImageCommand implements Poolable { } return this; } + public initRect(color: Color, start: Vector, width: number, height: number) { - this.type = DrawCommandType.Rectangle + this.type = DrawCommandType.Rectangle; this.color = color; this.width = width; this.height = height; @@ -134,7 +135,6 @@ export class DrawImageCommand implements Poolable { const endBottom = normal.scale(-halfThick).add(end); /** - * * +---------------------^----------------------+ * | | (normal) | * (startx, starty)------------------>(endx, endy) @@ -148,8 +148,8 @@ export class DrawImageCommand implements Poolable { this._geom[index++] = [startBottom.x, startBottom.y]; this._geom[index++] = [startBottom.x, startBottom.y]; this._geom[index++] = [endTop.x, endTop.y]; - this._geom[index++] = [endBottom.x,endBottom.y]; - this._geom;//? + this._geom[index++] = [endBottom.x, endBottom.y]; + if (this.snapToPixel) { for (const point of this._geom) { point[0] = ~~point[0]; @@ -162,10 +162,10 @@ export class DrawImageCommand implements Poolable { public initCircle(pos: Vector, radius: number, color: Color) { this.type = DrawCommandType.Circle; this.color = color; - let topLeft = pos.add(vec(-radius, -radius)); - let topRight = pos.add(vec(radius, -radius)); - let bottomRight = pos.add(vec(radius, radius)); - let bottomLeft = pos.add(vec(-radius, radius)); + const topLeft = pos.add(vec(-radius, -radius)); + const topRight = pos.add(vec(radius, -radius)); + const bottomRight = pos.add(vec(radius, radius)); + const bottomLeft = pos.add(vec(-radius, radius)); let index = 0; this._geom[index++] = [topLeft.x, topLeft.y]; this._geom[index++] = [topRight.x, topRight.y]; @@ -173,7 +173,7 @@ export class DrawImageCommand implements Poolable { this._geom[index++] = [bottomLeft.x, bottomLeft.y]; this._geom[index++] = [topRight.x, topRight.y]; this._geom[index++] = [bottomRight.x, bottomRight.y]; - this._geom;//? + if (this.snapToPixel) { for (const point of this._geom) { point[0] = ~~point[0]; diff --git a/src/engine/Graphics/Context/image-renderer.ts b/src/engine/Graphics/Context/image-renderer.ts index 42e9af8c56..a748d70f44 100644 --- a/src/engine/Graphics/Context/image-renderer.ts +++ b/src/engine/Graphics/Context/image-renderer.ts @@ -65,7 +65,7 @@ export class BatchImage extends BatchCommand { } add(command: DrawImageCommand) { - if ((command.type === DrawCommandType.Image)) { + if (command.type === DrawCommandType.Image) { const texture = TextureLoader.load(command.image); if (this.textures.indexOf(texture) === -1) { this.textures.push(texture); @@ -141,7 +141,7 @@ export class ImageRenderer extends BatchRenderer { if (i === 0) { texturePickerBuilder += `if (v_textureIndex <= ${i}.5) {\n`; } else { - texturePickerBuilder += ` else if (v_textureIndex <= ${i}.5) {\n` + texturePickerBuilder += ` else if (v_textureIndex <= ${i}.5) {\n`; } texturePickerBuilder += ` color = texture2D(u_textures[${i}], v_texcoord);\n`; texturePickerBuilder += ` }\n`; @@ -207,25 +207,25 @@ export class ImageRenderer extends BatchRenderer { sw = command.view[2]; sh = command.view[3]; - potWidth = ensurePowerOfTwo(command.image?.width || command.width);//? - potHeight = ensurePowerOfTwo(command.image?.height || command.height);//? + potWidth = ensurePowerOfTwo(command.image?.width || command.width); + potHeight = ensurePowerOfTwo(command.image?.height || command.height); - textureId = batch.getBatchTextureId(command);//? + textureId = batch.getBatchTextureId(command); if (command.type === DrawCommandType.Line || command.type === DrawCommandType.Rectangle) { - textureId === -1 + textureId = -1; // sentinel for no image rect commandColor = command.color; } if (command.type === DrawCommandType.Circle) { - textureId = -2; + textureId = -2; // sentinel for circle commandColor = command.color; } - + // potential optimization when divding by 2 (bitshift) // Modifying the images to poweroftwo images warp the UV coordinates - let uvx0 = sx / potWidth;//? - let uvy0 = sy / potHeight;//? - let uvx1 = (sx + sw) / potWidth;//? - let uvy1 = (sy + sh) / potHeight;//? + let uvx0 = sx / potWidth; + let uvy0 = sy / potHeight; + let uvx1 = (sx + sw) / potWidth; + let uvy1 = (sy + sh) / potHeight; if (textureId === -2) { uvx0 = 0; uvy0 = 0; @@ -247,9 +247,9 @@ export class ImageRenderer extends BatchRenderer { // opacity vertexBuffer[vertIndex++] = command.opacity; // color - vertexBuffer[vertIndex++] = commandColor.r; - vertexBuffer[vertIndex++] = commandColor.g; - vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.r / 255; + vertexBuffer[vertIndex++] = commandColor.g / 255; + vertexBuffer[vertIndex++] = commandColor.b / 255; vertexBuffer[vertIndex++] = commandColor.a; // (0, 1) @@ -265,9 +265,9 @@ export class ImageRenderer extends BatchRenderer { // opacity vertexBuffer[vertIndex++] = command.opacity; // color - vertexBuffer[vertIndex++] = commandColor.r; - vertexBuffer[vertIndex++] = commandColor.g; - vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.r / 255; + vertexBuffer[vertIndex++] = commandColor.g / 255; + vertexBuffer[vertIndex++] = commandColor.b / 255; vertexBuffer[vertIndex++] = commandColor.a; // (1, 0) @@ -283,9 +283,9 @@ export class ImageRenderer extends BatchRenderer { // opacity vertexBuffer[vertIndex++] = command.opacity; // color - vertexBuffer[vertIndex++] = commandColor.r; - vertexBuffer[vertIndex++] = commandColor.g; - vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.r / 255; + vertexBuffer[vertIndex++] = commandColor.g / 255; + vertexBuffer[vertIndex++] = commandColor.b / 255; vertexBuffer[vertIndex++] = commandColor.a; // (1, 0) @@ -301,9 +301,9 @@ export class ImageRenderer extends BatchRenderer { // opacity vertexBuffer[vertIndex++] = command.opacity; // color - vertexBuffer[vertIndex++] = commandColor.r; - vertexBuffer[vertIndex++] = commandColor.g; - vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.r / 255; + vertexBuffer[vertIndex++] = commandColor.g / 255; + vertexBuffer[vertIndex++] = commandColor.b / 255; vertexBuffer[vertIndex++] = commandColor.a; // (0, 1) @@ -319,9 +319,9 @@ export class ImageRenderer extends BatchRenderer { // opacity vertexBuffer[vertIndex++] = command.opacity; // color - vertexBuffer[vertIndex++] = commandColor.r; - vertexBuffer[vertIndex++] = commandColor.g; - vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.r / 255; + vertexBuffer[vertIndex++] = commandColor.g / 255; + vertexBuffer[vertIndex++] = commandColor.b / 255; vertexBuffer[vertIndex++] = commandColor.a; // (1, 1) @@ -337,9 +337,9 @@ export class ImageRenderer extends BatchRenderer { // opacity vertexBuffer[vertIndex++] = command.opacity; // color - vertexBuffer[vertIndex++] = commandColor.r; - vertexBuffer[vertIndex++] = commandColor.g; - vertexBuffer[vertIndex++] = commandColor.b; + vertexBuffer[vertIndex++] = commandColor.r / 255; + vertexBuffer[vertIndex++] = commandColor.g / 255; + vertexBuffer[vertIndex++] = commandColor.b / 255; vertexBuffer[vertIndex++] = commandColor.a; } diff --git a/src/engine/Graphics/Context/line-renderer.ts b/src/engine/Graphics/Context/line-renderer.ts index 9ceae14d6e..3935199647 100644 --- a/src/engine/Graphics/Context/line-renderer.ts +++ b/src/engine/Graphics/Context/line-renderer.ts @@ -43,7 +43,9 @@ export class LineRenderer extends BatchRenderer { const cmd = this.commands.get(); cmd.start = this._contextInfo.transform.current.multv(start); cmd.end = this._contextInfo.transform.current.multv(end); - cmd.color = color; + cmd.color.r = color.r; + cmd.color.g = color.g; + cmd.color.b = color.b; cmd.color.a = cmd.color.a * this._contextInfo.state.current.opacity; this.addCommand(cmd); } diff --git a/src/engine/Graphics/GraphicsSystem.ts b/src/engine/Graphics/GraphicsSystem.ts index 1ca3773122..80c97667f8 100644 --- a/src/engine/Graphics/GraphicsSystem.ts +++ b/src/engine/Graphics/GraphicsSystem.ts @@ -1,9 +1,7 @@ -import { isActor } from '../Actor'; import { ExcaliburGraphicsContext } from './Context/ExcaliburGraphicsContext'; import { Scene } from '../Scene'; import { GraphicsComponent } from './GraphicsComponent'; -import { Vector, vec } from '../Math/vector'; -import { Color } from '../Color'; +import { vec } from '../Math/vector'; import { CoordPlane, TransformComponent } from '../EntityComponentSystem/Components/TransformComponent'; import { Entity } from '../EntityComponentSystem/Entity'; import { Camera } from '../Camera'; @@ -11,6 +9,7 @@ import { System, SystemType, TagComponent } from '../EntityComponentSystem'; import { Engine } from '../Engine'; import { GraphicsDiagnostics } from './GraphicsDiagnostics'; import { EnterViewPortEvent, ExitViewPortEvent } from '../Events'; +import { GraphicsGroup } from '.'; export class GraphicsSystem extends System { public readonly types = ['ex.transform', 'ex.graphics'] as const; @@ -73,8 +72,6 @@ export class GraphicsSystem extends System { /** * Returns a string representation of the vector. */ - public toString(): string { + public toString(fixed?: number): string { + if (fixed) { + return `(${this.x.toFixed(fixed)}, ${this.y.toFixed(fixed)})`; + } return `(${this.x}, ${this.y})`; } } diff --git a/src/engine/Util/Pool.ts b/src/engine/Util/Pool.ts index ba0e794225..8b44bc0ad9 100644 --- a/src/engine/Util/Pool.ts +++ b/src/engine/Util/Pool.ts @@ -1,3 +1,5 @@ +import { Logger } from '..'; + export interface Poolable { /** * Any type that is a member of a an object pool will have a reference to teh pool @@ -11,6 +13,7 @@ export class Pool { public totalAllocations = 0; public index = 0; public objects: Type[] = []; + private _logger = Logger.getInstance(); constructor( public builder: (...args: any[]) => Type, @@ -49,7 +52,9 @@ export class Pool { get(...args: any[]): Type { if (this.index === this.maxObjects) { // TODO implement hard or soft cap - throw new Error('Max pooled objects reached, possible memory leak?'); + this._logger.warn('Max pooled objects reached, possible memory leak? Doubling'); + this.maxObjects = this.maxObjects * 2; + // throw new Error('Max pooled objects reached, possible memory leak?'); } if (this.objects[this.index]) { diff --git a/src/spec/CollisionShapeSpec.ts b/src/spec/CollisionShapeSpec.ts index 0fdf273e59..d9b2e795b8 100644 --- a/src/spec/CollisionShapeSpec.ts +++ b/src/spec/CollisionShapeSpec.ts @@ -1,10 +1,11 @@ import * as ex from '@excalibur'; -import { ExcaliburMatchers, ensureImagesLoaded } from 'excalibur-jasmine'; +import { ExcaliburMatchers, ensureImagesLoaded, ExcaliburAsyncMatchers } from 'excalibur-jasmine'; import { TestUtils } from './util/TestUtils'; describe('Collision Shape', () => { beforeAll(() => { jasmine.addMatchers(ExcaliburMatchers); + jasmine.addAsyncMatchers(ExcaliburAsyncMatchers); }); describe('a Circle', () => { @@ -291,20 +292,16 @@ describe('Collision Shape', () => { }); }); - it('can be drawn with actor', (done) => { + it('can be drawn with actor', async () => { const circleActor = new ex.Actor({ pos: new ex.Vector(100, 100), color: ex.Color.Blue }); circleActor.collider.useCircleCollider(100); - scene.add(circleActor); scene.draw(engine.ctx, 100); - ensureImagesLoaded(engine.canvas, 'src/spec/images/CollisionShapeSpec/circle.png').then(([canvas, image]) => { - expect(canvas).toEqualImage(image); - done(); - }); + await expectAsync(engine.canvas).toEqualImage('src/spec/images/CollisionShapeSpec/circle.png'); }); it('can calculate the distance to another circle', () => { diff --git a/src/spec/ExcaliburGraphicsContextSpec.ts b/src/spec/ExcaliburGraphicsContextSpec.ts index 4f29541c39..1ca4070aae 100644 --- a/src/spec/ExcaliburGraphicsContextSpec.ts +++ b/src/spec/ExcaliburGraphicsContextSpec.ts @@ -217,7 +217,7 @@ describe('The ExcaliburGraphicsContext', () => { expect(context.height).toBe(canvas.height); }); - fit('can draw a graphic', async () => { + it('can draw a graphic', async () => { const canvasElement = document.createElement('canvas'); canvasElement.width = 100; canvasElement.height = 100; diff --git a/src/spec/PoolSpec.ts b/src/spec/PoolSpec.ts index 9b7daa4ec6..bbc22a26d7 100644 --- a/src/spec/PoolSpec.ts +++ b/src/spec/PoolSpec.ts @@ -10,25 +10,35 @@ class MockPoolable implements ex.Poolable { } describe('An object Pool', () => { - it('should exist', () => { expect(ex.Pool).toBeDefined(); }); it('can be constructed', () => { - const pool = new ex.Pool(() => new MockPoolable(), (m) => m.dispose(), 10); + const pool = new ex.Pool( + () => new MockPoolable(), + (m) => m.dispose(), + 10 + ); expect(pool).toBeDefined(); }); it('can get instances up to the maximum', () => { - const pool = new ex.Pool(() => new MockPoolable(), (m) => m.dispose(), 10); + const pool = new ex.Pool( + () => new MockPoolable(), + (m) => m.dispose(), + 10 + ); + const logger = ex.Logger.getInstance(); + spyOn(logger, 'warn'); for (let i = 0; i < 10; i++) { const instance = pool.get(); } + expect(pool.totalAllocations).toBe(10); - expect(() => { - pool.get(); - }).toThrow(); + expect(logger.warn).not.toHaveBeenCalled(); + pool.get(); + expect(logger.warn).toHaveBeenCalledOnceWith('Max pooled objects reached, possible memory leak? Doubling'); }); it('can get instances and return them to get recycled', () => { @@ -52,9 +62,13 @@ describe('An object Pool', () => { }); it('can borrow a single instance temporarily', () => { - const pool = new ex.Pool(() => new MockPoolable(), (m) => m.dispose(), 10); + const pool = new ex.Pool( + () => new MockPoolable(), + (m) => m.dispose(), + 10 + ); pool.get(); - pool.borrow(instance => { + pool.borrow((instance) => { expect(instance).toBeDefined(); expect(pool.index).toBe(2); }); @@ -62,9 +76,13 @@ describe('An object Pool', () => { }); it('can be used in a using which are then reclaimed', () => { - const pool = new ex.Pool(() => new MockPoolable(), (m) => m.dispose(), 10); + const pool = new ex.Pool( + () => new MockPoolable(), + (m) => m.dispose(), + 10 + ); for (let i = 0; i < 10; i++) { - pool.using(pool => { + pool.using((pool) => { for (let i = 0; i < 10; i++) { pool.get(); } @@ -75,8 +93,12 @@ describe('An object Pool', () => { }); it('can have instances unhooked from the pool', () => { - const pool = new ex.Pool(() => new MockPoolable(), (m) => m.dispose(), 10); - const is = pool.using(pool => { + const pool = new ex.Pool( + () => new MockPoolable(), + (m) => m.dispose(), + 10 + ); + const is = pool.using((pool) => { const instances = []; for (let i = 0; i < 10; i++) { const i = pool.get(); @@ -90,4 +112,4 @@ describe('An object Pool', () => { expect(is[i]._pool).toBe(undefined); } }); -}); \ No newline at end of file +}); diff --git a/src/spec/images/CollisionShapeSpec/circle.png b/src/spec/images/CollisionShapeSpec/circle.png index 2e3f689cdd532dc556a548ec568ebbc3f2cd58f2..547217cf7ceb4bf8381207e51fbbddc1be7035b7 100644 GIT binary patch literal 9690 zcmeHN`8$;B|L1Acn3-g(O=Zh4iV!E1H7!hHlnPmfQYuSRVn&uRb)>AFXG+R`I;9h3 z%aB>BvtXhoJ88By7ZF&N+b@%jD@-|sKEE|=?i?&o>m@8xyB?$`T$x#8;Kuoklc zBPAuZmh8Cqu#}Ya4DyR!4W6`o;wOSX(qV@k?4_Q*+4N0HN>hrwclQy7?`ZG3k#M?? z#>8B{(gwS87wmK{{j0GpFSFG-*BZF0jG1EzWfCxb$qZzV{gckz13 zV+Sufz}@VAirM-XPi5sb?fh0ljkr%1lRAd#S;8N)P78Y@b46{~%4Z_VnDWAnE4K4( z%a(+O>IXY4aMBP8J>qJYU!2TgoPnL#R$ds1KYT^7wY3c>>4k@zp;80Y1nQgA{ zA&~c1oQhGXf3^G^uhnn(iZxcN@*oiH(d;^(o*bl&(R0NxN#A)6_aRIuPdE z7VcKeX;Vr!%h6fhxS-L((I?pKir?qC3JJA`ns+GQNIgdw#a{G-o1?Tgkp*e&#l0}q z@mibO*>yR4k$pGf&$tdJO`h1s&lN7f(I59KEsV~zu@}4RZEaG=&0}xK%4hs(*Kbcv z9)HnH9~h{en(VC!S%L;?XL6mU+<3+fQB`~g7ls&Lpyj!uP{xk9RLBja`CM#S%6a-L(p zQG-5@qFY0A3Y80sVAaCjw{hlio9*JHSfl9Hx^`8{!KrC-PKBDGgXbYZ$J`tL^%7ds zP^1N7ph(a7y*;&0bC5d1bhr7q@qUp-V%a*4Vmo>Wtl@NRGWWmENV5nk;xCcprgEHq8bCWSK+kMl2IM%UkoLHx|#N^ z)GbIMgS_34qrW^zWX3qxjGnH7G&*bXsr;wo5x7*BI6EB{K}q~&o*cWK$8d7M;u|c^k7EK___w>iV-{QcI`kT?!?h0p^Q_m{gU{OUXq0mFdFH`6 zT)eL4M1FmDr#P}3b=qGRg>F6KcO||-G)6o>gI_pEJ>)>04P@6@5n{g$*G?P+_Ggqc zJo1fvtC%WL{2Pw3Atv4_ECfi$6f<1&jb7D8q|o=5;~9HQ7Ts`2iD(R#EO1wg+y@Jr z5~!69X7cN8FE1zXCLXNz+(!g;E|rP!9!^ToiKN3Vso5{(wqlZP&n`R-5Jfl9OaK!6 zvLQ=@nWlNN#0fs{6R5ecl|*xbTiE?$7kMa9LGA{bh$F*E4y5@CBJ)&==g8xy+;QT3 zYJlkdbG(i=cwrJ10U+5-BAjoZ6b@|bEzgu)jD-V9joGg+COLtu?2!$wX;&Q>^lFX@ zV7BBR8`_N;Gn(*Pcwi$r-Mdni0(5X`yXp`zk_rdjlVY7jCod;V?0gezdo8a(LDTiH zpHqCp`ZQAs?wgK|a{T8?E~7kWoP2FEqR0#+O}?`)rb)F!CsOAE6r@ybLx}ZDSt!>B zE3L=FG`<2qczFMQeu+zP=2R|B^`^822@woCo!B6tuw{4Vq> zq^L>Tz*@V4wZ6N;e}uBZu7V_TgDF8Wy*kam1MHVk$GwJ$ncP8+HRmzTx^HATW1|gu z=DLjzW-*!zB#@IMGG-trVeG|+PS0J!g{L~ggJ4cJZY~}0GAQSp%_TSN>HBN%^gKoR zX5t3O8Au$@6g|T7jdCiODYFqbA&R}AVJYcN@|^J|E!$Q&zZ= zm@!w_5|Pej$!2*9I^cdwDa5O*-0j%j2R@c{j;C`bb$GPKR#Oif5TKlr#eP6$rOX}G zNpe<+Yven#x!L5LbX;z4ufnFg0MePN7B2atICt$tj5{drB6raQe7#%tabqQNtZ;;b%= z#qQ9``{gvSzk)|;dp4Rb|A>?OEKX6VYptH7Z6~mR3RT2A$i87&t1cU|)UXH~#u#hm zC;$!>*dkD}X*k+|=l(QCIl|E|3es?%`YWJ1Fe>2^5!5Hx*YCDHy?*v}TO+l7(ch7B*(P#42P_GFjL{RQ;=*D>7U%A~lM_%n*(|fZkNuPE8j&>nLD%`%wh{ z1~5!?Lm$f@Sq%lsR~r!eC<$O8Ox5C1 zd-H-L=ggSalU%MRsxKWk&~X3(TIy%6Ri*j&4sQ+4&uY0=Ler)17}v ze)8c2I{28NirxP$)B5*%Jy2a2xod1<;|=Gx2DQi{I9>cULob()7jfzfdkUmmNwDX&W3Y4CYx0$z%qD|!|ti`x% z0XIssPGd1Rw0@mgc`^ zjYj%{S93e+iqfT698vxJmp_S2;oOj~A}SrRVZUNy0T5in)H;_ z^mdzqkUr_t{#?P`kKT%Ud`1QLHgDpqC#i%1p^nRD>0L4pIJ+alf1TFE=K#h}E%hNS zgxh}S-mqr|!b6l3`&4%OZGZr+iQHTe7@yVx=Cb>3k}h}(Rb;ZhHS7yc*lZ^dL3_#= zu5c~e>Wiqhd<}F8Ws@zu60p9FfhY~XDd=QAGX!2oiG85r=(hk-9IvGXFPEe`f)sv~ zd1cSB;yD-Eel$hjQbjOJwRsSSLf?{KkBzB2D*WN}7yb(OvW)K^xbxoQhNFn8%EYh7 z_EJ8XoeV0TR-JfXG2iA2K_RGD_H?&&vjLS$`=rg!dg5CfWf9Wx7#?s)Tn?Z+(E=d0 zvZyP7F(2R_h;X9CK0kw*jd0y{j5I{mou~|8Y*$6j#BT6-jyP`_=25gmp|gw|#r9uR zzK(3nRz;k;|DnqHe?tNCQ>WXuNLvAf8O|zgKTo+h`JM6;2Mx-2ODYzx%Me{1fVIg3 z9#l6HOhkRsr8$^boeGI6*GA~X6|B|5RuNa5OM;*FL~jRKrc&klOb@)PVv7q@_2bwvO9R+LqT&`Y)>;k=Ce*vX-|fk4x*R-vvsT636~> z+H@CJu4-&KzzaJ1ZAA7czb`6-nNG zEG~-T(~~3oa)11lsq8I9(P#kUa?KG4ujzVBus6T9`d}9x8?l$Qk=>uDZPW1{x&6Xf zit%FwL0$!P?hUMn_xa_TfJj8HgZI16P@4LA3Uw4BoYS3AFGl9A_k%mc? zE|8ae^>2jGt``?JA*3FJw>UJcsyp9#eD(|pbq?))6PNoKzYhbUcA-a9#p}Z-{~_`} zXgy0t$;s2M)!so%L*~W@!_-sQw8K#`psSFOUfQZ0>_q9s~^_`Anqo(Wv{VHq#l!V^4a$t z7?C78B_m>A9CsF_?lIipz|X1Ic6Q^=4ZY7wI*Qk%#c@|*d*_CIO?qXbnSD#jnzpRF z7=-)MY~JraK6@M$&1_KFZ>eSHrwXmpyTrdgegd7klOWB{rJPn$v5(_E21I?y{G8{} zw*!UfMMo2Fw-xO%qhR5apEeiQ@I>|MGeBt+=WE@bNRyZfX#bK%ZQF&LpQ3}MHL5f+ z+vR}Dd%(SmiK!bI{6ViVZq^0eNg?dN*!2)On&nCsZuz#lJRkC#iMjIDb8k^^h{)}u z)0nG;h*)o#H-|fu=17sRysvtivy@SNnYcT}6O45_U{Q#Ab8OhiY3d>GN#_-G5f!V$Ak7Zr>VC@)d_9mldQ?!c|~@A z4k57Q5^z8IE*BHu5Pc`s8SbDF!EzzH|1+VO{|MB5lBd#Nahp@7%03F|8@N-Tla+Vb}Ck4I7 zSMMa`?8qMUkOiND8|{NV$AXstC));=<1?OfMbpnF@6Ylx#r99qKr{}@h<@ZhpxzPQ zcCaoP!gv13MXv0TcL+h0LCsrcCQ&u(Nb^po!cm2qg1yxkw_7YJ)+(9QL&GifZ-%l_ znx$(-Jh(S#mg;ehikc;AtdlC@vJcssRoR|9|NRcR{O;A;Y6@8PZ|*Kkuk1xrwb;p1 zOf4QYe6#oDmL!KbE}6AMNj&8vZeiE4^jWg9)tR4vzvjPXML;?u!BL&3uDmwt!lO6o zfqQ4YQCt8JP<9(x;VR;JvQ36CsC!s;*FIJnG4irWk9AX%ry>a5OsC|PJ+;AUIi1M4 zE7Zz7Ub3WMk;JVhOHP|CQVxLJ4eOSCwWp>(UwBB5Qg$X4FJw^yRgQu4myUEJEr;h~ z#OZ6k(Q396o(hoG{3(>V^%B2;9@j)(>Pyx*$ocs#$X%BEF;}W7n7bk-qlWUd!fE+mVgOFx_FKl z*|N#F=t2Z!~6s2+aw+UpU!a_{Q&dCI(!&VNePU|^H2+__Tr z+&ckQtZy(c(WCR0Osqbyiqyy;?LbZ{njAWaUey`^>Ok-KohgR__d@bKvcd;r$Ds#;P2_>pHrN7~-`Y< zoP18S{SdQ$icb+d52#K=(2d^Y;aue<_mzF#L;hunh(97f!A8>1f9 zSQR3?TZcLgkVpTaszadMx_GK4vcBQXv*Q}29U1qPRl5d{}Ly`L{&dZ&61-8w^7EE}!6HQ6`F&%~M2_utG0*y>i&J3MT2jsG?2t3mVBq4{4uzGmDb+5Y z+2!OvnMb@cdS?O4wki`94!$SHhA;Puib;(zP1A`EprA0At`?p2@imnc>(tELvK<&a zJye%Ms$80Llk|Mu@FPsUpaSXpC}8Tlc)%k$K9;vd#y0kdt;(`el@YOKR3c-O93KkU zQi^o~*y>#6V`af(v%Tw>Cwdtddv7os1f8|EJ#w$Gb;*H$rjg3B#ktH39M!7F&?*t6 z2KCh-uiboCebdQVr&t5gWYze`4;(=d0@PEbwMtPwN zQc^P4k^cqwrAdBivtL^DmnQ$k1OHDvFtIGnkX}{{B-<27fgkcdm%UHz{jU5EGiq_6 literal 9171 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 G@xK6JK0oaM From 0a7d8b2518526b1e37e03fa9c6986ddfac71b334 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Fri, 3 Sep 2021 20:58:44 -0500 Subject: [PATCH 06/18] Rev game --- sandbox/src/game.ts | 71 ++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/sandbox/src/game.ts b/sandbox/src/game.ts index bcd721a1bd..289f8d22bd 100644 --- a/sandbox/src/game.ts +++ b/sandbox/src/game.ts @@ -51,23 +51,7 @@ declare module dat { } var gui = new dat.GUI({name: 'Excalibur'}); -var actorfolder = gui.addFolder('Flags'); -actorfolder.add(ex.Debug, 'showDrawingCullBox'); -actorfolder.add(ex.Debug, 'showCameraFocus'); -actorfolder.add(ex.Debug, 'showCameraViewport'); -actorfolder.add(ex.Debug, 'showActorAnchor'); -actorfolder.add(ex.Debug, 'showActorId'); -actorfolder.add(ex.Debug, 'showActorUnitCircle'); -var folder = gui.addFolder('Physics Flags'); -folder.add(ex.Physics, 'enabled') -folder.add(ex.Physics.debug, 'showColliderBounds') -folder.add(ex.Physics.debug, 'showColliderGeometry') -folder.add(ex.Physics.debug, 'showColliderNormals') -folder.add(ex.Physics.debug, 'showContacts') -folder.add(ex.Physics.debug, 'showMotionVectors') -folder.add(ex.Physics.debug, 'broadphaseDebug') -folder.add(ex.Physics, "positionIterations") -folder.add(ex.Physics, "velocityIterations") + var stats = new Stats(); stats.showPanel(0); @@ -75,7 +59,35 @@ document.body.appendChild(stats.dom); var bootstrap = (game: ex.Engine) => { gui.add({toggleDebug: false}, 'toggleDebug').onChange(() => game.toggleDebug()); - var entities = gui.addFolder('Entities'); + var supportedKeys = ['filter', 'entity', 'transform', 'motion', 'body', 'collider', 'graphics', 'camera']; + for (let key of supportedKeys) { + let folder = gui.addFolder(key); + if (game.debug[key]) { + for (let option in game.debug[key]) { + if (option) { + if (option.toLocaleLowerCase().includes('color')) { + folder.addColor(game.debug[key], option); + } else { + if (Array.isArray(game.debug[key][option])) { + continue; + } + folder.add(game.debug[key], option); + } + } + } + } + } + + var physics = gui.addFolder('Physics Flags'); + physics.add(ex.Physics, 'enabled') + physics.add(ex.Physics.debug, 'showColliderBounds') + physics.add(ex.Physics.debug, 'showColliderGeometry') + physics.add(ex.Physics.debug, 'showColliderNormals') + physics.add(ex.Physics.debug, 'showContacts') + physics.add(ex.Physics.debug, 'broadphaseDebug') + physics.add(ex.Physics, "positionIterations") + physics.add(ex.Physics, "velocityIterations") + game.on("preframe", () => { stats.begin(); }); @@ -83,14 +95,14 @@ var bootstrap = (game: ex.Engine) => { stats.end(); }); - game.currentScene.on('entityadded', (e: any) => { - var entity: ex.Entity = e.target; - var obj = {id: entity.id, name: entity.constructor.name, types: entity.types}; + // game.currentScene.on('entityadded', (e: any) => { + // var entity: ex.Entity = e.target; + // var obj = {id: entity.id, name: entity.constructor.name, types: entity.types}; - var pos = entities.addFolder(`${obj.id}:${obj.name}`) - pos.add({pos: entity.get(ex.TransformComponent).pos.toString()}, 'pos'); - pos.add({types: obj.types.join(', ')}, 'types'); - }); + // var pos = entities.addFolder(`${obj.id}:${obj.name}`) + // pos.add({pos: entity.get(ex.TransformComponent).pos.toString()}, 'pos'); + // pos.add({types: obj.types.join(', ')}, 'types'); + // }); return { stats, gui } } @@ -496,6 +508,7 @@ game.add(follower); // player.enableCapturePointer = true; // player.collisionType = ex.CollisionType.Active; var player = new ex.Actor({ + name: 'player', pos: new ex.Vector(100, -200), width: 32, height: 96, @@ -515,7 +528,13 @@ follower.actions player.rotation = 0; // Health bar example -var healthbar = new ex.Actor({x: 0, y: -70, width: 140, height: 5, color: new ex.Color(0, 255, 0)}); +var healthbar = new ex.Actor({ + name: 'player healthbar', + x: 0, + y: -70, + width: 140, + height: 5, + color: new ex.Color(0, 255, 0)}); player.addChild(healthbar); // player.onPostDraw = (ctx: CanvasRenderingContext2D) => { // ctx.fillStyle = 'red'; From d6d8dbb05d6e43ca9925a6025957e15bd5e7dddb Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Mon, 6 Sep 2021 13:22:25 -0500 Subject: [PATCH 07/18] Update tests --- src/engine/Collision/MotionSystem.ts | 31 +-------- src/engine/{ => Debug}/Debug.ts | 6 +- src/engine/{ => Debug}/DebugFlags.ts | 4 +- src/engine/Engine.ts | 2 +- src/engine/EntityComponentSystem/Entity.ts | 2 +- src/engine/Graphics/Context/debug-text.ts | 2 +- src/engine/Graphics/index.ts | 2 + src/engine/index.ts | 2 +- src/spec/AlgebraSpec.ts | 6 ++ src/spec/CollisionShapeSpec.ts | 20 ++++++ src/spec/CompositeColliderSpec.ts | 20 ++++++ src/spec/DebugTextSpec.ts | 62 ++++++++++++++++++ src/spec/EntityManagerSpec.ts | 23 +++++++ src/spec/EntitySpec.ts | 10 +++ src/spec/TransformComponentSpec.ts | 25 +++++-- .../CollisionShapeSpec/circle-debug.png | Bin 0 -> 1805 bytes .../CompositeColliderSpec/composite.png | Bin 0 -> 5383 bytes .../images/DebugTextSpec/draw-canvas2d.png | Bin 0 -> 832 bytes src/spec/images/DebugTextSpec/draw-webgl.png | Bin 0 -> 832 bytes src/spec/images/DebugTextSpec/draw.png | Bin 0 -> 832 bytes 20 files changed, 174 insertions(+), 43 deletions(-) rename src/engine/{ => Debug}/Debug.ts (98%) rename src/engine/{ => Debug}/DebugFlags.ts (81%) create mode 100644 src/spec/DebugTextSpec.ts create mode 100644 src/spec/images/CollisionShapeSpec/circle-debug.png create mode 100644 src/spec/images/CompositeColliderSpec/composite.png create mode 100644 src/spec/images/DebugTextSpec/draw-canvas2d.png create mode 100644 src/spec/images/DebugTextSpec/draw-webgl.png create mode 100644 src/spec/images/DebugTextSpec/draw.png diff --git a/src/engine/Collision/MotionSystem.ts b/src/engine/Collision/MotionSystem.ts index ca4114fa1b..464b92ff6a 100644 --- a/src/engine/Collision/MotionSystem.ts +++ b/src/engine/Collision/MotionSystem.ts @@ -1,12 +1,10 @@ import { Camera } from '../Camera'; -import { Color } from '../Color'; import { Entity } from '../EntityComponentSystem'; import { MotionComponent } from '../EntityComponentSystem/Components/MotionComponent'; import { TransformComponent } from '../EntityComponentSystem/Components/TransformComponent'; import { System, SystemType } from '../EntityComponentSystem/System'; import { Physics } from './Physics'; import { Scene } from '../Scene'; -import { DrawUtil } from '../Util/Index'; import { BodyComponent } from './BodyComponent'; import { CollisionType } from './CollisionType'; import { EulerIntegrator } from './Integrator'; @@ -16,16 +14,9 @@ export class MotionSystem extends System { public systemType = SystemType.Update; public priority = -1; - private _entities: Entity[] = []; - private _camera: Camera; - initialize(scene: Scene) { - this._camera = scene.camera; - } - update(_entities: Entity[], elapsedMs: number): void { let transform: TransformComponent; let motion: MotionComponent; - this._entities = _entities; for (const entity of _entities) { transform = entity.get(TransformComponent); motion = entity.get(MotionComponent); @@ -47,26 +38,6 @@ export class MotionSystem extends System { } debugDraw(ctx: CanvasRenderingContext2D) { - ctx.save(); - this._camera.draw(ctx); - for (const entity of this._entities) { - const transform = entity.get(TransformComponent); - const motion = entity.get(MotionComponent); - if (Physics.debug.showMotionVectors) { - DrawUtil.vector(ctx, Color.Yellow, transform.pos, motion.acc.add(Physics.acc)); - DrawUtil.vector(ctx, Color.Blue, transform.pos, motion.vel); - DrawUtil.point(ctx, Color.Red, transform.pos); - } - if (Physics.debug.showSleepMotion) { - const pos = transform.pos; - const body = entity.get(BodyComponent); - if (body) { - ctx.fillStyle = 'yellow'; - ctx.font = '18px'; - ctx.fillText(body.sleepMotion.toString(), pos.x, pos.y); - } - } - } - ctx.restore(); + // pass } } diff --git a/src/engine/Debug.ts b/src/engine/Debug/Debug.ts similarity index 98% rename from src/engine/Debug.ts rename to src/engine/Debug/Debug.ts index 509bfe83e2..8105aadf69 100644 --- a/src/engine/Debug.ts +++ b/src/engine/Debug/Debug.ts @@ -1,7 +1,7 @@ import { DebugFlags, ColorBlindFlags } from './DebugFlags'; -import { Pair } from './Collision/Detection/Pair'; -import { Engine } from './Engine'; -import { Color } from '.'; +import { Pair } from '../Collision/Detection/Pair'; +import { Engine } from '../Engine'; +import { Color } from '..'; /** * Debug stats containing current and previous frame statistics diff --git a/src/engine/DebugFlags.ts b/src/engine/Debug/DebugFlags.ts similarity index 81% rename from src/engine/DebugFlags.ts rename to src/engine/Debug/DebugFlags.ts index 3904803a6e..ad62833222 100644 --- a/src/engine/DebugFlags.ts +++ b/src/engine/Debug/DebugFlags.ts @@ -1,5 +1,5 @@ -import { Engine } from './Engine'; -import { ColorBlindCorrector, ColorBlindness } from './PostProcessing/Index'; +import { Engine } from '../Engine'; +import { ColorBlindCorrector, ColorBlindness } from '../PostProcessing/Index'; export interface DebugFlags { colorBlindMode: ColorBlindFlags; diff --git a/src/engine/Engine.ts b/src/engine/Engine.ts index 3348de3ff9..2eb82dba8d 100644 --- a/src/engine/Engine.ts +++ b/src/engine/Engine.ts @@ -34,7 +34,7 @@ import { Color } from './Color'; import { Scene } from './Scene'; import { Entity } from './EntityComponentSystem/Entity'; import { PostProcessor } from './PostProcessing/PostProcessor'; -import { Debug, DebugStats } from './Debug'; +import { Debug, DebugStats } from './Debug/Debug'; import { Class } from './Class'; import * as Input from './Input/Index'; import * as Events from './Events'; diff --git a/src/engine/EntityComponentSystem/Entity.ts b/src/engine/EntityComponentSystem/Entity.ts index b8e515fdef..a251b78367 100644 --- a/src/engine/EntityComponentSystem/Entity.ts +++ b/src/engine/EntityComponentSystem/Entity.ts @@ -62,7 +62,7 @@ export class Entity extends Class implements OnInitialize, OnPreUpdate, OnPostUp constructor(components?: Component[], name?: string) { super(); - this._name = name ?? this._name; + this._setName(name); if (components) { for (const component of components) { this.addComponent(component); diff --git a/src/engine/Graphics/Context/debug-text.ts b/src/engine/Graphics/Context/debug-text.ts index bee8b68819..802104af6e 100644 --- a/src/engine/Graphics/Context/debug-text.ts +++ b/src/engine/Graphics/Context/debug-text.ts @@ -17,7 +17,7 @@ export class DebugText { private _spriteFont: SpriteFont; public load() { this._imageSource = new ImageSource(this.fontSheet); - this._imageSource.load().then(() => { + return this._imageSource.load().then(() => { this._spriteSheet = SpriteSheet.fromGrid({ image: this._imageSource, grid: { diff --git a/src/engine/Graphics/index.ts b/src/engine/Graphics/index.ts index 21f25d63a9..b2c41cb225 100644 --- a/src/engine/Graphics/index.ts +++ b/src/engine/Graphics/index.ts @@ -24,3 +24,5 @@ export * from './Canvas'; export * from './Context/ExcaliburGraphicsContext'; export * from './Context/ExcaliburGraphicsContext2DCanvas'; export * from './Context/ExcaliburGraphicsContextWebGL'; + +export * from './Context/debug-text'; diff --git a/src/engine/index.ts b/src/engine/index.ts index 8e1905b51a..8328a476b4 100644 --- a/src/engine/index.ts +++ b/src/engine/index.ts @@ -17,7 +17,7 @@ export * from './Math/Index'; export * from './Camera'; export * from './Class'; export * from './Configurable'; -export * from './Debug'; +export * from './Debug/Debug'; export * from './EventDispatcher'; export * from './Events/MediaEvents'; export * from './Events'; diff --git a/src/spec/AlgebraSpec.ts b/src/spec/AlgebraSpec.ts index 8795e4f64c..69621949bd 100644 --- a/src/spec/AlgebraSpec.ts +++ b/src/spec/AlgebraSpec.ts @@ -244,6 +244,12 @@ describe('Vectors', () => { expect(c.x).not.toBe(v.x); expect(c.y).not.toBe(v.y); }); + + it('can be printed toString(fixed)', () => { + const v = new ex.Vector(1.2345, 2.345); + + expect(v.toString(2)).toBe('(1.23, 2.35)'); + }); }); describe('Rays', () => { diff --git a/src/spec/CollisionShapeSpec.ts b/src/spec/CollisionShapeSpec.ts index d9b2e795b8..f0c34ae8f6 100644 --- a/src/spec/CollisionShapeSpec.ts +++ b/src/spec/CollisionShapeSpec.ts @@ -292,6 +292,26 @@ describe('Collision Shape', () => { }); }); + it('can be debug drawn', async () => { + const canvasElement = document.createElement('canvas'); + canvasElement.width = 100; + canvasElement.height = 100; + const ctx = new ex.ExcaliburGraphicsContext2DCanvas({ canvasElement }); + + const circle = new ex.CircleCollider({ + offset: new ex.Vector(50, 50), + radius: 30 + }); + + ctx.clear(); + + circle.debug(ctx, ex.Color.Red); + + ctx.flush(); + + await expectAsync(canvasElement).toEqualImage('src/spec/images/CollisionShapeSpec/circle-debug.png'); + }); + it('can be drawn with actor', async () => { const circleActor = new ex.Actor({ pos: new ex.Vector(100, 100), diff --git a/src/spec/CompositeColliderSpec.ts b/src/spec/CompositeColliderSpec.ts index 2e556ec52f..1a994e62e6 100644 --- a/src/spec/CompositeColliderSpec.ts +++ b/src/spec/CompositeColliderSpec.ts @@ -184,4 +184,24 @@ describe('A CompositeCollider', () => { expect(compCollider.contains(vec(-99.9, 0))).toBe(true); expect(compCollider.contains(vec(-101, 0))).toBe(false); }); + + it('can be debug drawn', async () => { + const canvasElement = document.createElement('canvas'); + canvasElement.width = 300; + canvasElement.height = 300; + const ctx = new ex.ExcaliburGraphicsContext2DCanvas({ canvasElement }); + + const compCollider = new ex.CompositeCollider([ex.Shape.Circle(50), ex.Shape.Box(200, 10, Vector.Half)]); + const tx = new TransformComponent(); + tx.pos = ex.vec(150, 150); + compCollider.update(tx); + + ctx.clear(); + + compCollider.debug(ctx, ex.Color.Red); + + ctx.flush(); + + await expectAsync(canvasElement).toEqualImage('src/spec/images/CompositeColliderSpec/composite.png'); + }); }); diff --git a/src/spec/DebugTextSpec.ts b/src/spec/DebugTextSpec.ts new file mode 100644 index 0000000000..22c8a7cd7e --- /dev/null +++ b/src/spec/DebugTextSpec.ts @@ -0,0 +1,62 @@ +import * as ex from '@excalibur'; +import { ExcaliburAsyncMatchers } from 'excalibur-jasmine'; + +/** + * + */ +function flushWebGLCanvasTo2D(source: HTMLCanvasElement): HTMLCanvasElement { + const canvas = document.createElement('canvas'); + canvas.width = source.width; + canvas.height = source.height; + const ctx = canvas.getContext('2d'); + ctx.drawImage(source, 0, 0); + return canvas; +} + +describe('DebugText', () => { + beforeAll(() => { + jasmine.addAsyncMatchers(ExcaliburAsyncMatchers); + }); + + it('exists', () => { + expect(ex.DebugText); + }); + + it('can write text (2DCanvas)', async () => { + const canvasElement = document.createElement('canvas'); + canvasElement.width = 100; + canvasElement.height = 100; + const ctx = new ex.ExcaliburGraphicsContext2DCanvas({ canvasElement }); + + const debugText = new ex.DebugText(); + + await debugText.load(); + + ctx.clear(); + + debugText.write(ctx, 'some text', ex.vec(0, 50)); + + ctx.flush(); + + await expectAsync(canvasElement).toEqualImage('src/spec/images/DebugTextSpec/draw-canvas2d.png'); + }); + + it('can write text (WebGL)', async () => { + const canvasElement = document.createElement('canvas'); + canvasElement.width = 100; + canvasElement.height = 100; + const ctx = new ex.ExcaliburGraphicsContextWebGL({ canvasElement }); + + const debugText = new ex.DebugText(); + + await debugText.load(); + + ctx.clear(); + + debugText.write(ctx, 'some text', ex.vec(0, 50)); + + ctx.flush(); + + await expectAsync(flushWebGLCanvasTo2D(canvasElement)).toEqualImage('src/spec/images/DebugTextSpec/draw-webgl.png'); + }); +}); diff --git a/src/spec/EntityManagerSpec.ts b/src/spec/EntityManagerSpec.ts index 983a748783..b39c168dde 100644 --- a/src/spec/EntityManagerSpec.ts +++ b/src/spec/EntityManagerSpec.ts @@ -72,4 +72,27 @@ describe('An EntityManager', () => { entity.removeComponent(componentA); entity.processComponentRemoval(); }); + + it('can find entities by name', () => { + const entityManager = new ex.EntityManager(new ex.World(null)); + const entity = new ex.Entity([], 'some-e'); + const entity2 = new ex.Entity(); + entityManager.addEntity(entity); + entityManager.addEntity(entity2); + expect(entityManager.getByName('some-e')).toEqual([entity]); + expect(entityManager.getByName('anonymous')).toEqual([entity2]); + }); + + it('can clear entities', () => { + const entityManager = new ex.EntityManager(new ex.World(null)); + const entity = new ex.Entity([], 'some-e'); + const entity2 = new ex.Entity(); + entityManager.addEntity(entity); + entityManager.addEntity(entity2); + + expect(entityManager.entities.length).toBe(2); + entityManager.clear(); + entityManager.processEntityRemovals(); + expect(entityManager.entities.length).toBe(0); + }); }); diff --git a/src/spec/EntitySpec.ts b/src/spec/EntitySpec.ts index fe97ae6966..e6fcf8c85e 100644 --- a/src/spec/EntitySpec.ts +++ b/src/spec/EntitySpec.ts @@ -29,6 +29,16 @@ describe('An entity', () => { expect(entity2.id).not.toBe(entity3.id); }); + it('can have a name', () => { + const e = new ex.Entity([], 'my-name'); + expect(e.name).toBe('my-name'); + }); + + it('has a default name', () => { + const e = new ex.Entity(); + expect(e.name).toBe('anonymous'); + }); + it('can be killed', () => { const entity = new ex.Entity(); expect(entity.isKilled()).toBe(false); diff --git a/src/spec/TransformComponentSpec.ts b/src/spec/TransformComponentSpec.ts index 600a480fd2..7c9e155523 100644 --- a/src/spec/TransformComponentSpec.ts +++ b/src/spec/TransformComponentSpec.ts @@ -1,7 +1,6 @@ import * as ex from '@excalibur'; import { ExcaliburMatchers } from 'excalibur-jasmine'; - describe('A TransformComponent', () => { beforeAll(() => { jasmine.addMatchers(ExcaliburMatchers); @@ -100,13 +99,13 @@ describe('A TransformComponent', () => { // Changing a child global scale affects childs local and not parent scale childTx.globalScale = ex.vec(1, 1); expect(parentTx.scale).toBeVector(ex.vec(2, 3)); - expect(childTx.scale).toBeVector(ex.vec(1/2, 1/3)); + expect(childTx.scale).toBeVector(ex.vec(1 / 2, 1 / 3)); // can change scale by prop childTx.globalScale.x = 3; childTx.globalScale.y = 4; expect(parentTx.scale).toBeVector(ex.vec(2, 3)); - expect(childTx.scale).toBeVector(ex.vec(3/2, 4/3)); + expect(childTx.scale).toBeVector(ex.vec(3 / 2, 4 / 3)); }); it('can have parent/child relations with rotation', () => { @@ -128,4 +127,22 @@ describe('A TransformComponent', () => { expect(childTx.rotation).toBeCloseTo(Math.PI); // Math.PI + Math.PI = 2PI = 0 global }); -}); \ No newline at end of file + it('can retrieve the global transform', () => { + const parent = new ex.Entity([new ex.TransformComponent()]); + const child = new ex.Entity([new ex.TransformComponent()]); + parent.addChild(child); + + const parentTx = parent.get(ex.TransformComponent); + const childTx = child.get(ex.TransformComponent); + + // Changing a parent position influences the child global position + parentTx.pos = ex.vec(100, 200); + parentTx.rotation = Math.PI; + parentTx.scale = ex.vec(2, 3); + + expect(childTx.pos).toBeVector(ex.vec(0, 0)); + expect(childTx.getGlobalTransform().pos).toBeVector(ex.vec(100, 200)); + expect(childTx.getGlobalTransform().rotation).toBe(Math.PI); + expect(childTx.getGlobalTransform().scale).toBeVector(ex.vec(2, 3)); + }); +}); diff --git a/src/spec/images/CollisionShapeSpec/circle-debug.png b/src/spec/images/CollisionShapeSpec/circle-debug.png new file mode 100644 index 0000000000000000000000000000000000000000..6fa83235a8aed631e49a6bdf48e8264d6a6ad378 GIT binary patch literal 1805 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 DgY0E( literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugTextSpec/draw-canvas2d.png b/src/spec/images/DebugTextSpec/draw-canvas2d.png new file mode 100644 index 0000000000000000000000000000000000000000..f90355aad72671fc877d37ea456c8ff797e1044a GIT binary patch literal 832 zcmeAS@N?(olHy`uVBq!ia0vp^DImVEX0h;uum9 z_jbm`+&cy$ENivq91C)d@mu8~_I340>xti!CAmpa7hEKR@f&o)v=?sCE7D0~Hhg$|l>mPlme}De`Tli}G_WP+{ z3uB{A*4bYFV!!`akkzIrB1)%{!WgcyZ!#}JLTVHo~NJZocY$edd9YvIr4JuU%Bd7f4w@npXu|O z9y!0S0dvDobF8&`zhm31MIURf`fix@>aF(c&FdGfUY%_EY_)S-nx(U;TGkJWu()_%rLaFL|%{INtVZ z@Gr^x&Sl^Bsy>;iKJokumLKbd=Pop5R1BL_CE--kc8#SaqVQEB$3@8~VS%KhE87)a o(ldM=Iz*StwFwLsg)RGMn5^~Z|N40nFiS9ay85}Sb4q9e0N%iC<^TWy literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugTextSpec/draw-webgl.png b/src/spec/images/DebugTextSpec/draw-webgl.png new file mode 100644 index 0000000000000000000000000000000000000000..f90355aad72671fc877d37ea456c8ff797e1044a GIT binary patch literal 832 zcmeAS@N?(olHy`uVBq!ia0vp^DImVEX0h;uum9 z_jbm`+&cy$ENivq91C)d@mu8~_I340>xti!CAmpa7hEKR@f&o)v=?sCE7D0~Hhg$|l>mPlme}De`Tli}G_WP+{ z3uB{A*4bYFV!!`akkzIrB1)%{!WgcyZ!#}JLTVHo~NJZocY$edd9YvIr4JuU%Bd7f4w@npXu|O z9y!0S0dvDobF8&`zhm31MIURf`fix@>aF(c&FdGfUY%_EY_)S-nx(U;TGkJWu()_%rLaFL|%{INtVZ z@Gr^x&Sl^Bsy>;iKJokumLKbd=Pop5R1BL_CE--kc8#SaqVQEB$3@8~VS%KhE87)a o(ldM=Iz*StwFwLsg)RGMn5^~Z|N40nFiS9ay85}Sb4q9e0N%iC<^TWy literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugTextSpec/draw.png b/src/spec/images/DebugTextSpec/draw.png new file mode 100644 index 0000000000000000000000000000000000000000..f90355aad72671fc877d37ea456c8ff797e1044a GIT binary patch literal 832 zcmeAS@N?(olHy`uVBq!ia0vp^DImVEX0h;uum9 z_jbm`+&cy$ENivq91C)d@mu8~_I340>xti!CAmpa7hEKR@f&o)v=?sCE7D0~Hhg$|l>mPlme}De`Tli}G_WP+{ z3uB{A*4bYFV!!`akkzIrB1)%{!WgcyZ!#}JLTVHo~NJZocY$edd9YvIr4JuU%Bd7f4w@npXu|O z9y!0S0dvDobF8&`zhm31MIURf`fix@>aF(c&FdGfUY%_EY_)S-nx(U;TGkJWu()_%rLaFL|%{INtVZ z@Gr^x&Sl^Bsy>;iKJokumLKbd=Pop5R1BL_CE--kc8#SaqVQEB$3@8~VS%KhE87)a o(ldM=Iz*StwFwLsg)RGMn5^~Z|N40nFiS9ay85}Sb4q9e0N%iC<^TWy literal 0 HcmV?d00001 From beb505e4e175c73f71e557059b2d31d535645bda Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Mon, 6 Sep 2021 13:36:23 -0500 Subject: [PATCH 08/18] Fix debug --- src/engine/Debug/index.ts | 3 +++ src/engine/index.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 src/engine/Debug/index.ts diff --git a/src/engine/Debug/index.ts b/src/engine/Debug/index.ts new file mode 100644 index 0000000000..51da2d076e --- /dev/null +++ b/src/engine/Debug/index.ts @@ -0,0 +1,3 @@ +export * from './Debug'; +export * from './DebugFlags'; +export * from './DebugSystem'; diff --git a/src/engine/index.ts b/src/engine/index.ts index 8328a476b4..287d6427ac 100644 --- a/src/engine/index.ts +++ b/src/engine/index.ts @@ -17,7 +17,7 @@ export * from './Math/Index'; export * from './Camera'; export * from './Class'; export * from './Configurable'; -export * from './Debug/Debug'; +export * from './Debug/index'; export * from './EventDispatcher'; export * from './Events/MediaEvents'; export * from './Events'; From 05a3cbb9693b367354e2fc2c5f43d1e68f72a89d Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Mon, 6 Sep 2021 14:14:27 -0500 Subject: [PATCH 09/18] Update tests --- src/engine/Collision/MotionSystem.ts | 2 - src/engine/Debug/DebugSystem.ts | 2 +- src/spec/DebugSystemSpec.ts | 125 ++++++++++++++++++ src/spec/images/DebugSystemSpec/body.png | Bin 0 -> 9913 bytes src/spec/images/DebugSystemSpec/collider.png | Bin 0 -> 6475 bytes src/spec/images/DebugSystemSpec/graphics.png | Bin 0 -> 6538 bytes src/spec/images/DebugSystemSpec/motion.png | Bin 0 -> 7831 bytes src/spec/images/DebugSystemSpec/transform.png | Bin 0 -> 7933 bytes 8 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 src/spec/DebugSystemSpec.ts create mode 100644 src/spec/images/DebugSystemSpec/body.png create mode 100644 src/spec/images/DebugSystemSpec/collider.png create mode 100644 src/spec/images/DebugSystemSpec/graphics.png create mode 100644 src/spec/images/DebugSystemSpec/motion.png create mode 100644 src/spec/images/DebugSystemSpec/transform.png diff --git a/src/engine/Collision/MotionSystem.ts b/src/engine/Collision/MotionSystem.ts index 464b92ff6a..da9f49db47 100644 --- a/src/engine/Collision/MotionSystem.ts +++ b/src/engine/Collision/MotionSystem.ts @@ -1,10 +1,8 @@ -import { Camera } from '../Camera'; import { Entity } from '../EntityComponentSystem'; import { MotionComponent } from '../EntityComponentSystem/Components/MotionComponent'; import { TransformComponent } from '../EntityComponentSystem/Components/TransformComponent'; import { System, SystemType } from '../EntityComponentSystem/System'; import { Physics } from './Physics'; -import { Scene } from '../Scene'; import { BodyComponent } from './BodyComponent'; import { CollisionType } from './CollisionType'; import { EulerIntegrator } from './Integrator'; diff --git a/src/engine/Debug/DebugSystem.ts b/src/engine/Debug/DebugSystem.ts index 6d61b65de6..d5626e5bd7 100644 --- a/src/engine/Debug/DebugSystem.ts +++ b/src/engine/Debug/DebugSystem.ts @@ -155,7 +155,7 @@ export class DebugSystem extends System { } if (bodySettings.showAll || bodySettings.showMotion) { - this._graphicsContext.debug.drawText(`motion(${body.motion})`, cursor); + this._graphicsContext.debug.drawText(`motion(${body.sleepMotion})`, cursor); cursor = cursor.add(lineHeight); } diff --git a/src/spec/DebugSystemSpec.ts b/src/spec/DebugSystemSpec.ts new file mode 100644 index 0000000000..ac712e3616 --- /dev/null +++ b/src/spec/DebugSystemSpec.ts @@ -0,0 +1,125 @@ +import * as ex from '@excalibur'; +import { ExcaliburAsyncMatchers } from 'excalibur-jasmine'; +import { TestUtils } from './util/TestUtils'; + +describe('DebugSystem', () => { + beforeAll(() => { + jasmine.addAsyncMatchers(ExcaliburAsyncMatchers); + }); + let engine: ex.Engine; + + beforeEach(() => { + engine = TestUtils.engine(); + engine.toggleDebug(); + engine.currentScene.world.clearSystems(); + engine.currentScene._initialize(engine); + }); + + afterEach(() => { + engine.stop(); + engine = null; + }); + + it('exists', () => { + expect(ex.DebugSystem).toBeDefined(); + }); + + it('can show transform and entity info', async () => { + const debugSystem = new ex.DebugSystem(); + engine.currentScene.world.add(debugSystem); + debugSystem.initialize(engine.currentScene); + + engine.graphicsContext.clear(); + await (engine.graphicsContext.debug as any)._debugText.load(); + + const actor = new ex.Actor({ name: 'thingy', x: 0, y: 0, width: 50, height: 50, color: ex.Color.Yellow }); + actor.id = 0; + engine.debug.transform.showAll = true; + engine.debug.entity.showAll = true; + debugSystem.update([actor], 100); + + engine.graphicsContext.flush(); + + await expectAsync(engine.canvas).toEqualImage('src/spec/images/DebugSystemSpec/transform.png'); + }); + + it('can show motion info', async () => { + const debugSystem = new ex.DebugSystem(); + engine.currentScene.world.add(debugSystem); + debugSystem.initialize(engine.currentScene); + + engine.graphicsContext.clear(); + await (engine.graphicsContext.debug as any)._debugText.load(); + + const actor = new ex.Actor({ name: 'thingy', x: 0, y: 0, width: 50, height: 50, color: ex.Color.Yellow }); + actor.id = 0; + actor.vel = ex.vec(100, 0); + actor.acc = ex.vec(100, -100); + engine.debug.motion.showAll = true; + debugSystem.update([actor], 100); + + engine.graphicsContext.flush(); + + await expectAsync(engine.canvas).toEqualImage('src/spec/images/DebugSystemSpec/motion.png'); + }); + + it('can show body info', async () => { + const debugSystem = new ex.DebugSystem(); + engine.currentScene.world.add(debugSystem); + debugSystem.initialize(engine.currentScene); + + engine.graphicsContext.clear(); + await (engine.graphicsContext.debug as any)._debugText.load(); + + const actor = new ex.Actor({ name: 'thingy', x: -100, y: 0, width: 50, height: 50, color: ex.Color.Yellow }); + actor.id = 0; + actor.vel = ex.vec(100, 0); + actor.acc = ex.vec(100, -100); + engine.debug.body.showAll = true; + debugSystem.update([actor], 100); + + engine.graphicsContext.flush(); + + await expectAsync(engine.canvas).toEqualImage('src/spec/images/DebugSystemSpec/body.png'); + }); + + it('can show collider info', async () => { + const debugSystem = new ex.DebugSystem(); + engine.currentScene.world.add(debugSystem); + debugSystem.initialize(engine.currentScene); + + engine.graphicsContext.clear(); + await (engine.graphicsContext.debug as any)._debugText.load(); + + const actor = new ex.Actor({ name: 'thingy', x: -100, y: 0, width: 50, height: 50, color: ex.Color.Yellow }); + actor.id = 0; + engine.debug.collider.showAll = true; + debugSystem.update([actor], 100); + + engine.graphicsContext.flush(); + + await expectAsync(engine.canvas).toEqualImage('src/spec/images/DebugSystemSpec/collider.png'); + }); + + it('can show collider info', async () => { + const debugSystem = new ex.DebugSystem(); + engine.currentScene.world.add(debugSystem); + debugSystem.initialize(engine.currentScene); + + engine.graphicsContext.clear(); + await (engine.graphicsContext.debug as any)._debugText.load(); + + const actor = new ex.Actor({ name: 'thingy', x: -100, y: 0, width: 50, height: 50 }); + actor.graphics.use(new ex.Rectangle({ width: 200, height: 100, color: ex.Color.Red })); + actor.id = 0; + engine.debug.collider.showBounds = false; + engine.debug.collider.showGeometry = false; + engine.debug.collider.showOwner = false; + engine.debug.graphics.showAll = true; + debugSystem.update([actor], 100); + + engine.graphicsContext.flush(); + + await expectAsync(engine.canvas).toEqualImage('src/spec/images/DebugSystemSpec/graphics.png'); + }); +}); diff --git a/src/spec/images/DebugSystemSpec/body.png b/src/spec/images/DebugSystemSpec/body.png new file mode 100644 index 0000000000000000000000000000000000000000..f03d6178876d8def3ad4b396147b7bea948d030c GIT binary patch literal 9913 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 Y1i6U~@~ literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugSystemSpec/collider.png b/src/spec/images/DebugSystemSpec/collider.png new file mode 100644 index 0000000000000000000000000000000000000000..4849bc24e44ad8a152ef3ee85d422b9272c3c77f GIT binary patch literal 6475 zcmeHMX;4#F6n+UI3qlD&WmO`GpiTvJ1S@1AU?ZTixOS99nB~z)5QNbo4-pS2t?lX~;DyT-efAw|}lR2=qf^rvyYYl_`YsqNYSF(ulOmN3OPEITqhB z>mx@fjC3vy#A2{GA~{%-A%u`G9L_OU676CW7Di^<0Unh}rKYLu{Eq;2V2Wdd7orJ+ z#bEMh1}5SValr4eiIa&$cOA3P*R}#CUyHXXVlovHLhRH$+Z6YGVILJ?v3Od-MWSs5 zh2)-XA5=2fN*-m|#})7$PUuz#DP0|tbK-i#6cSWYH74r`odz$4mn)PwHG}}7yVCaq9G`W%{7R_P`6^n6> z#>KzY=xEmQR-cria7ITRZ@fQTYT(0CsR`eC2J}Y+mtYNH?l7ffrz+@zh6dWF-4$3o z9Lha#QaX>r?W&J&pZFUN{!_5Oippv{7yIqBcAU+0XBxP~R7T#YkD1qc`URT2@h5&lw_l$rWKF zV|!)^e;ARCXjmRJ8(2%jXup5L#W1Uu7Yb$8m7iamL3r(rN^e zOGP>sK;*4!u!a+mh^V?f=2{Nr1xyXr#2@AsWhNuEjg2nKXrUs+-q9Gs}tAef_5E`x{`Z22>`%$R7$(Vfp@rw)er){>c zsp%Afv&QrO#Q!U38^ygfa`S`MIdVgrSbeoE?!MBCR^SFY{Vo@9PA=(eeoU4s_2~QB z9mLT zoQjg&Q}X$S+vCYvlg}x&RWG8)%3HIH!-TO~{z8GCmuhHhOcYeO4RL|cTwvwgC;5%a z9|`b_+l_u)J?Ddn4_|m-5fEUDNv6GY0KmW*KaC@rMZ!p*@WZ>DjZ?HQod)1k?x@}w zwoF(TX|hKqxBMj6!YMSneEN9Mp}RO=;Dwf{9h$93$6(l|%p48tXK-aGMGxs}_SZ8cqxi!z=m>#3G6#qo zSC~OUiQt*C8H}yQ*y5-jZaNm1u}&dTf3>(=a7zyNu%*vy1<96UH50{3*}*|wXN`4V zQ^5P1B#@uHfUAV&=FE@ZtWrDFX0pJg^9BI;#;w%B3faU}W08+_TxYIGt?Yy!5k!`j z`^S5iuJ5`>p(acV*1Xu>?+Yqg9&jD{+PYiFWe_E!n4k`UMgy7((DaDb2DC7uUBUml dCCU@nl~!|l2;nDUz~2!lX4$IfYF65}e*xeMQse*t literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugSystemSpec/graphics.png b/src/spec/images/DebugSystemSpec/graphics.png new file mode 100644 index 0000000000000000000000000000000000000000..b60974499596f748fe2943562c73742b1cc2f1b1 GIT binary patch literal 6538 zcmeHM|4$QV7;ld&93W{6hz?YaI1x4w$mY5PDTg2(kb$U+%rV-9Y??@@Oh!8_Z4ZYA zGr|lT;BZnTf*Tt+m?>+)>m-v2V-zre3$$z?RLXY>N5AiSB>MyQ!{QJ3!{wgmee>p? zd+vFj&*zgXKc3F>@e1^!(P%y?ZzY|e(dZ`E1-TO|{iFJ8#EpLO1n(%V^^ZNzX|$J; zQj!u*iE|bw67qM>?qmo${KpXw#`RC%sLG68?)v6z^}tWlisRo-(jv-!3#(@N%=|2! z8^7u7_qXaq*rlpQJ#s6~X_av&wyx6X00^6tPTTnqVZ7UmopWsUtXs7x;()TDD3ncqFwr zOE8<)HuF+}9D?g;Itf0)zqbmB8wl1xZBg0ypK1d=fQn?+k=}wF+cd@uG@kinpDCo( zHUQZB6n$**ZK66e3S+pV&j4kJZQ*H0rGDkFo~zE-CvES{t!3!Usi>}Cv`mxh4Vij% zatG75vk2m=b(gQGhdJJ+R})@IoGK^w4n9!)S}DSF7u8R>&KUC?=C~7lN6(SO|6siz zJfK%Doz!1PWb;95W+ngr=$W}6+^X6uw z^adh3B^~g50)oXAXBuPCHpc86R%DIiTkNZ8m+>WIexmLC#v!vr|91exqU=0ee?wy@ zTF^cq>2+f)`5Bb)fzIJEzOMnp56es?GLw?gC|ZaL^V-bwNAwrN6=A6)|v<2ouQB|;$2bpp{*Jz-;pMZUK&>E`@D$f zwujeOX?lpGJvIp*$7nS-PYB=R2R^8AgyU(($%_j-`^Tu`uI+2TyPl9K@P4ug7_(e5 z)Q_rXoc=gZ#w>~XU04CCWH@v)1lA6YuiL&9eX!_!=D=D44C`}31PEq;szf;Y5UlOp zm3IWdJfXs7M39XrDx=25pi3ZKxFSd5a|fGVo+=|G(C5S_S!D!O%6Cy)FI+Y>e|pi{+0J(l*`%=xn@RJ9zb_djo8!#!pIjhF89c_=#f%`&N*4IzFPDrxY`nL%z z3m~xUVB|s%n^b9~awG~qpFnEoVE~AW{GLl{+b{xyfc5}V@4}I=cI4r_A9cVb@Tn|O tr9t%rH8rSFN#O$piWGJD&wR<|)mJdTs&)e|5I2T8|1!{{i|f(oO&X literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugSystemSpec/motion.png b/src/spec/images/DebugSystemSpec/motion.png new file mode 100644 index 0000000000000000000000000000000000000000..af4ab07d750ab6b3c95a50558c1312bdba2545dc GIT binary patch literal 7831 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 literal 0 HcmV?d00001 diff --git a/src/spec/images/DebugSystemSpec/transform.png b/src/spec/images/DebugSystemSpec/transform.png new file mode 100644 index 0000000000000000000000000000000000000000..e3f44aa90633112b05a612c77ed12ca5a6ef13c0 GIT binary patch literal 7933 zcmeHMdstHG*4OQ}P*X|EF6O0EYGp=e8pFGk)0moh%Uc-Iv6Gc17G7acvCOGXdNR!> zD>1V&Q#3R30$9z&RDxs%r6A7O2!crERtf^oR{#5+?~m`_?>YXlpZ%`=yz5!Nwf1_~ zTJKtC!}kW7o31uBFfcF=+WkYMfq@}3`ydvFSFWtDBWdHyugcdwg@r1!W0x> zERWo@nj;D@Xd`fT<%q%17JnaMGDl!&RffRq3hC&`H@bQkoFg3hTv!_X8G%H~3`6$; za|BX$coqbR01*hNz>EF^pLu%gXE$@?V6<|tJ8*U;}ogMk;+U!4D&DM)g&PZ$hnGznE?lX2- z`+hN2S7m~LxU%b4FwyR@W0w9i@*2-bxkrX|svd#`LF)|gA=GlmJ@rYi;?vWB5Mxji zxuevj8RO`Lh?2+ggDst!+Y2y~F5~U#hvznq^t9fc9x+2eBkFMD#WuT5o|b!A_yj-N zOS6)lDd;`}Y}|1rl5?hZxF58Ecl~zS#xcDe6O6pweh#9U z%}45rt==VIXoMla)}wV^#Ay6J?I&1?PvpCWMrL!&i|w7TF2kO1`T0LoO{hUFhjIWj za1MxZBNPr)!HBt7yixMJUOb=k$NX6Z82>!W=yd!%y6)OT3ecw*E}c_1&f!S)G~kC|=x_@3qoxQ2rMgYERmZ+Xu}F5?sCjm=4OUmyI`Wh8p5@&GfY1WcT-IJn{1!2Fr&Q0> z=uqsD%i00+hxSxE*s1K?pT+Q8(v=vt>+2B3msi`Wne5-fd{r@;15?Ld~O%>h7_ zNrKhss^eK7ekCf97Uc0ip&QWgfg`Whftt%29)(10-|^Tvj;MXud03)Uv62Qk?wO(( z70Yks{uEwCu2z&Hx{5kLhaS6=Bk66^xgo+TqUf5wQJKU%2w!@<5UCrQqiGEK!;ZhQ z3Hg?GgMLl*wInHBc-38-R@dA9QgMSk*7&t=D!3F&&Jg$!#z=J>cXm1gYV&6ENC3qy zumSmaH{OzpD?T(y*PiOIWd(5BjF|BRS#jP`8k!T@7;Bo?fg3wfCw22pQ%+uPiX|mP z1sQ7yTuYdum^vN2Z_ZWJtZ^-asFb+K=r zh41Ry#MC)a;s&`Sl?_tnTnHaCG6G8bsvI1s^Z!_!)-Fsb6+d<8D8iA3`UDlT($K^5 z8TN;-x)7ds40%=o6R0m}aN2{)YGk)oC&1u`Sbq(f-|lnx5`a3V=v3lP7RAEcc(+;0 zjdnS;mH+FDXi|_OC|#x5bu)w6Hv*D^Rxtd3q!A=W-X1O8IDjIPKhPSnk4B^K!Wa@) z%R=#O4vG1V`k6`=zlnaC1zS*YMspSQE`^{|zc&@w!%U4nuDI4PnFYn?AFD9i)0w-;imwNxy zcU5995i~UBvYUg`T#1cfp>eFPS z(E21o({up7qyST0`&S&`Iv-bD8N!p`#6Ap7vXOCVX%1uV+#mEaSrd<;Ikc0f^K#&T zF)nBk5F1k(iC=a^7*GL2Dz4xI5)fMLUIMk@%$Wi&q5OKL$#^J+rfOU_P25C972%%XC|AMz6+pb&phu^J=@peo>e-=swBU#ZS47~mAU5!#r2^| zotl&3z0!$y*sg8?b%^59vic|4v-kn7Y|*p`gQ6^M)m&@p8)2I-dt6q4Ni^yE4jeV@ z`t;7SA>U8)Mr+xretGHbDU`o^_SxtX2eY0UwL4*PerJii=-#~>c`BFtZRmmf@fuGl zK&jY{{Q$!00}PG%)6W=3)=WlcP73_c*<(%msOYzWXN<&m$OlXnC&1fqEPi-#9U@_u z<9GZSZ1me=V@>p;=QlxT5bRjGw)rB4(x6E!?TQn#R9yKG;fBYrZfebM>Eed*QugO5 zZKlshJ-UH`%SnMQce+9s&Tx;0!67Q__2FGsD{bO5Ye=mp$B5#9-dC2)?^qcJe4W#? z;QTmk5d`ihMDe_De{sz>CVEA!UK%SAFN(DX<@)t!B&c|E)!mg&J;$ zvKc>fCIYiAnVUkP@2-~{P$LcLS^0!3TByBm%u{#*&?V?%+^7L2h)I3c3D5qOlJ|#= zHP<7&mr^Mvkob{ZX9f0i_DE)t(m&ePJFxmkbxU3BO)GE?%tF@wpL@X<1vnXJZOY*Aq1 z?@*)A;@29ya`k5BPJ*hD!oa7|vhyQWYK?FEWsSFrJEdttpBOqu^4a^t6{(GkGhTJT zI&M8bI~rRYiM}6lsu_CO`Mu~i&_@5#=la3kiZ~6l*4J$$o5T#o3S&?>};?A049R4G3Jn z^#FCTB>{}z@1$|jkLoxTqQV;*T4kTB+`X`^pRQVQNt2S&^^SY~Pu@2>QDP?sZ4K_- zkp6-=LQMR-UuH=eRzlpF!o^f~O!-C+_Mew^#C|5yyQGh=rFVs4A7K2CE0*VFQN5PQ z^P}e%7Qqldz`p&KX+U$vR9Ux9cIyRH3 zEwmxsD7|@yeJsX2F7p4_i(3Si=o6-1s|j1BaFWi&l{jOD*8_q1?q6T4^UMB@ZQDa- zZMyRt0=XNx+vHifm(^kUmSad+cFp)2Flz%}RH964{LuPD!6~;#XN)fklITaT5qb6~ zT{%OuMoHPrtP=0z5(Tcl^l(%}ug8zHWGG@ec-RDDTJLY|u2Y@dp3JT1S~Ny?Fr3&M|^fY{+P_iV6)Op!5vuWEQ+ z)9ZwCyGzKAs(v=4kLw@xiE;?re^SJc&Ti$j^zKA8GVDg96kK0Arut1$5?o`Hiv2rX z(l2(`QHRtoj)}Xm>o6L@hOhf=ljPeqd^bgzu5$dJJf(Uzp{ID4_Y+?ubNLe>sF~RR zj{vX<6;2}m)L^jDS5=qU|1^N{C5z4?RP;(SZ|G3R;Go^%dp?A`LGO>ewSXnRS zqs*ac;?UGgn!ie{e)_n+xw>`e-(0@g=U^UXR8@3qz?c+?e!G(vj^|Yj@sBH07VRGM zraSS?4Hp`_3$4e6F8{(%VqxU7Frk7+feZ6LI3fO5j4Qim4FBcz X^HKL&@hy0sZ4k6;?+;Bo@rC~ZME+x` literal 0 HcmV?d00001 From 6e7e2affd168b63976a81675b8573273b628b14b Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Mon, 6 Sep 2021 14:17:59 -0500 Subject: [PATCH 10/18] Update motion --- src/engine/Collision/MotionSystem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/Collision/MotionSystem.ts b/src/engine/Collision/MotionSystem.ts index da9f49db47..f1ce23d707 100644 --- a/src/engine/Collision/MotionSystem.ts +++ b/src/engine/Collision/MotionSystem.ts @@ -35,7 +35,7 @@ export class MotionSystem extends System { } } - debugDraw(ctx: CanvasRenderingContext2D) { + debugDraw(_ctx: CanvasRenderingContext2D) { // pass } } From 67b54d339d86b7e84e0217be67541b4c6b1c2e8c Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Mon, 6 Sep 2021 16:37:51 -0500 Subject: [PATCH 11/18] More tests --- .../ExcaliburGraphicsContext2DCanvas.ts | 2 +- src/spec/ExcaliburGraphicsContextSpec.ts | 104 ++++++++++++++++++ .../2d-circle.png | Bin 0 -> 2134 bytes .../ExcaliburGraphicsContextSpec/2d-line.png | Bin 0 -> 726 bytes .../webgl-circle.png | Bin 0 -> 2452 bytes .../webgl-line.png | Bin 0 -> 625 bytes .../webgl-solid-rect.png | Bin 0 -> 584 bytes 7 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 src/spec/images/ExcaliburGraphicsContextSpec/2d-circle.png create mode 100644 src/spec/images/ExcaliburGraphicsContextSpec/2d-line.png create mode 100644 src/spec/images/ExcaliburGraphicsContextSpec/webgl-circle.png create mode 100644 src/spec/images/ExcaliburGraphicsContextSpec/webgl-line.png create mode 100644 src/spec/images/ExcaliburGraphicsContextSpec/webgl-solid-rect.png diff --git a/src/engine/Graphics/Context/ExcaliburGraphicsContext2DCanvas.ts b/src/engine/Graphics/Context/ExcaliburGraphicsContext2DCanvas.ts index fe9c6d53d9..b272322a89 100644 --- a/src/engine/Graphics/Context/ExcaliburGraphicsContext2DCanvas.ts +++ b/src/engine/Graphics/Context/ExcaliburGraphicsContext2DCanvas.ts @@ -190,7 +190,7 @@ export class ExcaliburGraphicsContext2DCanvas implements ExcaliburGraphicsContex public drawRectangle(pos: Vector, width: number, height: number, color: Color) { this.__ctx.save(); this.__ctx.fillStyle = color.toString(); - this.__ctx.strokeRect( + this.__ctx.fillRect( this.snapToPixel ? ~~pos.x : pos.x, this.snapToPixel ? ~~pos.y : pos.y, this.snapToPixel ? ~~width : width, diff --git a/src/spec/ExcaliburGraphicsContextSpec.ts b/src/spec/ExcaliburGraphicsContextSpec.ts index 1ca4070aae..a8e3d7d9c9 100644 --- a/src/spec/ExcaliburGraphicsContextSpec.ts +++ b/src/spec/ExcaliburGraphicsContextSpec.ts @@ -132,6 +132,57 @@ describe('The ExcaliburGraphicsContext', () => { await expectAsync(canvasElement).toEqualImage('src/spec/images/ExcaliburGraphicsContextSpec/2d-transform.png'); }); + it('can draw rectangle', async () => { + const canvasElement = document.createElement('canvas'); + canvasElement.width = 100; + canvasElement.height = 100; + const sut = new ex.ExcaliburGraphicsContext2DCanvas({ + canvasElement: canvasElement, + enableTransparency: false, + backgroundColor: ex.Color.White + }); + + sut.clear(); + sut.drawRectangle(ex.vec(10, 10), 80, 80, ex.Color.Blue); + sut.flush(); + + await expectAsync(canvasElement).toEqualImage('src/spec/images/ExcaliburGraphicsContextSpec/webgl-solid-rect.png'); + }); + + it('can draw circle', async () => { + const canvasElement = document.createElement('canvas'); + canvasElement.width = 100; + canvasElement.height = 100; + const sut = new ex.ExcaliburGraphicsContext2DCanvas({ + canvasElement: canvasElement, + enableTransparency: false, + backgroundColor: ex.Color.White + }); + + sut.clear(); + sut.drawCircle(ex.vec(50, 50), 50, ex.Color.Blue); + sut.flush(); + + await expectAsync(canvasElement).toEqualImage('src/spec/images/ExcaliburGraphicsContextSpec/2d-circle.png'); + }); + + it('can draw a line', async () => { + const canvasElement = document.createElement('canvas'); + canvasElement.width = 100; + canvasElement.height = 100; + const sut = new ex.ExcaliburGraphicsContext2DCanvas({ + canvasElement: canvasElement, + enableTransparency: false, + backgroundColor: ex.Color.White + }); + + sut.clear(); + sut.drawLine(ex.vec(0, 0), ex.vec(100, 100), ex.Color.Blue, 5); + sut.flush(); + + await expectAsync(canvasElement).toEqualImage('src/spec/images/ExcaliburGraphicsContextSpec/2d-line.png'); + }); + it('can snap drawings to pixel', async () => { const canvasElement = document.createElement('canvas'); canvasElement.width = 100; @@ -309,6 +360,59 @@ describe('The ExcaliburGraphicsContext', () => { await expectAsync(flushWebGLCanvasTo2D(canvasElement)).toEqualImage('src/spec/images/ExcaliburGraphicsContextSpec/webgl-rect.png'); }); + it('can draw rectangle', async () => { + const canvasElement = document.createElement('canvas'); + canvasElement.width = 100; + canvasElement.height = 100; + const sut = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvasElement, + enableTransparency: false, + backgroundColor: ex.Color.White + }); + + sut.clear(); + sut.drawRectangle(ex.vec(10, 10), 80, 80, ex.Color.Blue); + sut.flush(); + + await expectAsync(flushWebGLCanvasTo2D(canvasElement)).toEqualImage( + 'src/spec/images/ExcaliburGraphicsContextSpec/webgl-solid-rect.png' + ); + }); + + it('can draw circle', async () => { + const canvasElement = document.createElement('canvas'); + canvasElement.width = 100; + canvasElement.height = 100; + const sut = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvasElement, + enableTransparency: false, + backgroundColor: ex.Color.White + }); + + sut.clear(); + sut.drawCircle(ex.vec(50, 50), 50, ex.Color.Blue); + sut.flush(); + + await expectAsync(flushWebGLCanvasTo2D(canvasElement)).toEqualImage('src/spec/images/ExcaliburGraphicsContextSpec/webgl-circle.png'); + }); + + it('can draw a line', async () => { + const canvasElement = document.createElement('canvas'); + canvasElement.width = 100; + canvasElement.height = 100; + const sut = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvasElement, + enableTransparency: false, + backgroundColor: ex.Color.White + }); + + sut.clear(); + sut.drawLine(ex.vec(0, 0), ex.vec(100, 100), ex.Color.Blue, 5); + sut.flush(); + + await expectAsync(flushWebGLCanvasTo2D(canvasElement)).toEqualImage('src/spec/images/ExcaliburGraphicsContextSpec/webgl-line.png'); + }); + it('can transform the context', async () => { const canvasElement = document.createElement('canvas'); canvasElement.width = 100; diff --git a/src/spec/images/ExcaliburGraphicsContextSpec/2d-circle.png b/src/spec/images/ExcaliburGraphicsContextSpec/2d-circle.png new file mode 100644 index 0000000000000000000000000000000000000000..e81d32d644b0cd35974ac7ab330a2f1bdbcca840 GIT binary patch literal 2134 zcmV-c2&wmpP)Px-5J^NqRCr$PU3X{{PZa(xn%EnbsMu>1>?O88?1-olR5T!{AgK6<3S#Ui#Hb*s zh(tk%f?yW~QU6e5@4fequ?01;N3%b^o8P9G%iZ4H?9L`P50*e~_syI4?abSG@68*J zVHgHnOh0}AckcoZ9su|6i@zBez~|3EcDA@OGerk#)�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 literal 0 HcmV?d00001 diff --git a/src/spec/images/ExcaliburGraphicsContextSpec/webgl-solid-rect.png b/src/spec/images/ExcaliburGraphicsContextSpec/webgl-solid-rect.png new file mode 100644 index 0000000000000000000000000000000000000000..c4f1f053da497ab665571cbc2b917b81ad59b411 GIT binary patch literal 584 zcmeAS@N?(olHy`uVBq!ia0vp^DImU}ExgaSW-5 zdpp;V>#&2sk)r?gUnD;4hsvxZE+=2l-Ax{gG?yt{ znX*NDREVY_F`s?nyE Date: Mon, 6 Sep 2021 17:20:17 -0500 Subject: [PATCH 12/18] More tests --- src/engine/Collision/ColliderComponent.ts | 3 ++- src/engine/Debug/DebugSystem.ts | 16 ++++++------- src/spec/DebugSystemSpec.ts | 21 +++++++++++++++++- .../DebugSystemSpec/composite-collider.png | Bin 0 -> 12383 bytes 4 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 src/spec/images/DebugSystemSpec/composite-collider.png diff --git a/src/engine/Collision/ColliderComponent.ts b/src/engine/Collision/ColliderComponent.ts index 90f9b82808..463036db2c 100644 --- a/src/engine/Collision/ColliderComponent.ts +++ b/src/engine/Collision/ColliderComponent.ts @@ -48,6 +48,7 @@ export class ColliderComponent extends Component<'ex.collider'> { this._collider.owner = this.owner; this.events.wire(collider.events); this.$colliderAdded.notifyAll(collider); + this.update(); } } @@ -60,7 +61,7 @@ export class ColliderComponent extends Component<'ex.collider'> { } public update() { - const tx = this.owner.get(TransformComponent); + const tx = this.owner?.get(TransformComponent); if (this.collider) { this.collider.owner = this.owner; if (tx) { diff --git a/src/engine/Debug/DebugSystem.ts b/src/engine/Debug/DebugSystem.ts index d5626e5bd7..0fc0363b66 100644 --- a/src/engine/Debug/DebugSystem.ts +++ b/src/engine/Debug/DebugSystem.ts @@ -195,30 +195,30 @@ export class DebugSystem extends System { // Colliders live in world space already so after the restore() collider = entity.get(ColliderComponent); if (collider) { + if (colliderSettings.showAll || colliderSettings.showGeometry) { + collider.collider.debug(this._graphicsContext, colliderSettings.geometryColor); + } if (colliderSettings.showAll || colliderSettings.showBounds) { if (collider.collider instanceof CompositeCollider) { const colliders = collider.collider.getColliders(); for (const collider of colliders) { const bounds = collider.bounds; const pos = vec(bounds.left, bounds.top); - if (colliderSettings.showOwner) { + this._graphicsContext.debug.drawRect(pos.x, pos.y, bounds.width, bounds.height, { color: colliderSettings.boundsColor }); + if (colliderSettings.showAll || colliderSettings.showOwner) { this._graphicsContext.debug.drawText(`owner id(${collider.owner.id})`, pos); } - this._graphicsContext.debug.drawRect(pos.x, pos.y, bounds.width, bounds.height, { color: colliderSettings.boundsColor }); } + collider.bounds.draw(this._graphicsContext, colliderSettings.boundsColor); } else { const bounds = collider.bounds; const pos = vec(bounds.left, bounds.top); - if (colliderSettings.showOwner) { + this._graphicsContext.debug.drawRect(pos.x, pos.y, bounds.width, bounds.height, { color: colliderSettings.boundsColor }); + if (colliderSettings.showAll || colliderSettings.showOwner) { this._graphicsContext.debug.drawText(`owner id(${collider.owner.id})`, pos); } - this._graphicsContext.debug.drawRect(pos.x, pos.y, bounds.width, bounds.height, { color: colliderSettings.boundsColor }); } } - - if (colliderSettings.showAll || colliderSettings.showGeometry) { - collider.collider.debug(this._graphicsContext, colliderSettings.geometryColor); - } } this._popCameraTransform(tx); diff --git a/src/spec/DebugSystemSpec.ts b/src/spec/DebugSystemSpec.ts index ac712e3616..924426db3c 100644 --- a/src/spec/DebugSystemSpec.ts +++ b/src/spec/DebugSystemSpec.ts @@ -101,7 +101,26 @@ describe('DebugSystem', () => { await expectAsync(engine.canvas).toEqualImage('src/spec/images/DebugSystemSpec/collider.png'); }); - it('can show collider info', async () => { + it('can show composite collider info', async () => { + const debugSystem = new ex.DebugSystem(); + engine.currentScene.world.add(debugSystem); + debugSystem.initialize(engine.currentScene); + + engine.graphicsContext.clear(); + await (engine.graphicsContext.debug as any)._debugText.load(); + + const actor = new ex.Actor({ name: 'thingy', x: -100, y: 0, width: 50, height: 50, color: ex.Color.Yellow }); + actor.collider.useCompositeCollider([ex.Shape.Circle(50), ex.Shape.Box(150, 20), ex.Shape.Box(10, 150)]); + actor.id = 0; + engine.debug.collider.showAll = true; + debugSystem.update([actor], 100); + + engine.graphicsContext.flush(); + + await expectAsync(engine.canvas).toEqualImage('src/spec/images/DebugSystemSpec/composite-collider.png'); + }); + + it('can show graphics info', async () => { const debugSystem = new ex.DebugSystem(); engine.currentScene.world.add(debugSystem); debugSystem.initialize(engine.currentScene); diff --git a/src/spec/images/DebugSystemSpec/composite-collider.png b/src/spec/images/DebugSystemSpec/composite-collider.png new file mode 100644 index 0000000000000000000000000000000000000000..9a883485b7d6439148183c9db452eee3b0fe3253 GIT binary patch literal 12383 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 Date: Mon, 6 Sep 2021 18:38:21 -0500 Subject: [PATCH 13/18] Add camera + flush after processing --- src/engine/Debug/Debug.ts | 5 ++-- src/engine/Debug/DebugSystem.ts | 42 ++++++++++++--------------------- 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/src/engine/Debug/Debug.ts b/src/engine/Debug/Debug.ts index 8105aadf69..bcfbac9d85 100644 --- a/src/engine/Debug/Debug.ts +++ b/src/engine/Debug/Debug.ts @@ -258,10 +258,9 @@ export class Debug implements DebugFlags { showAll: false, showFocus: false, - focusColor: Color.Black, + focusColor: Color.Red, - showZoom: false, - zoomColor: Color.Black + showZoom: false }; } diff --git a/src/engine/Debug/DebugSystem.ts b/src/engine/Debug/DebugSystem.ts index 0fc0363b66..042339bd65 100644 --- a/src/engine/Debug/DebugSystem.ts +++ b/src/engine/Debug/DebugSystem.ts @@ -48,7 +48,8 @@ export class DebugSystem extends System { let body: BodyComponent; const bodySettings = this._engine.debug.body; - // let offscreen: boolean; + + const cameraSettings = this._engine.debug.camera; for (const entity of entities) { if (entity.hasTag('offscreen')) { // skip offscreen entities @@ -165,31 +166,6 @@ export class DebugSystem extends System { } } - // if (Debug.showCameraFocus) { - // const focus = this.getFocus(); - // ctx.fillStyle = 'red'; - // ctx.strokeStyle = 'white'; - // ctx.lineWidth = 3; - // ctx.beginPath(); - // ctx.arc(focus.x, focus.y, 15, 0, Math.PI * 2); - // ctx.closePath(); - // ctx.stroke(); - - // ctx.beginPath(); - // ctx.arc(focus.x, focus.y, 5, 0, Math.PI * 2); - // ctx.closePath(); - // ctx.stroke(); - // } - - // if (Debug.showCameraViewport) { - // ctx.beginPath(); - // ctx.setLineDash([5, 15]); - // ctx.lineWidth = 5; - // ctx.strokeStyle = 'white'; - // ctx.strokeRect(this.viewport.left, this.viewport.top, this.viewport.width, this.viewport.height); - // ctx.closePath(); - // } - this._graphicsContext.restore(); // Colliders live in world space already so after the restore() @@ -222,9 +198,21 @@ export class DebugSystem extends System { } this._popCameraTransform(tx); + } - this._graphicsContext.flush(); + if (cameraSettings) { + this._graphicsContext.save(); + this._camera.draw(this._graphicsContext); + if (cameraSettings.showAll || cameraSettings.showFocus) { + this._graphicsContext.drawCircle(this._camera.pos, 4, cameraSettings.focusColor); + } + if (cameraSettings.showAll || cameraSettings.showZoom) { + this._graphicsContext.debug.drawText(`zoom(${this._camera.zoom})`, this._camera.pos); + } + this._graphicsContext.restore(); } + + this._graphicsContext.flush(); } /** From 08a436628607f1c71a084a36db047d2399039a6d Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Fri, 10 Sep 2021 21:14:31 -0500 Subject: [PATCH 14/18] Add collision contacts --- sandbox/datgui/dat.gui.js | 13 ++++++ sandbox/src/game.ts | 11 ++--- sandbox/stats/stats.js | 5 +++ sandbox/tests/collisionvelocity/vel.ts | 8 ++-- sandbox/tests/physics/fastphysics.ts | 15 +++---- sandbox/tests/physics/physics.ts | 21 +++------ sandbox/tests/physics/physics2.ts | 4 +- sandbox/tests/trigger/trigger.ts | 4 +- src/engine/Collision/CollisionSystem.ts | 36 +++------------ .../Collision/Detection/CollisionProcessor.ts | 3 +- src/engine/Collision/Detection/DynamicTree.ts | 10 ++--- .../DynamicTreeCollisionProcessor.ts | 44 ++++--------------- src/engine/Collision/Physics.ts | 42 ------------------ src/engine/Debug/Debug.ts | 44 +++++++++++-------- src/engine/Debug/DebugSystem.ts | 28 +++++++++++- src/engine/EntityComponentSystem/System.ts | 5 --- .../EntityComponentSystem/SystemManager.ts | 8 ++++ src/engine/Scene.ts | 8 +--- 18 files changed, 124 insertions(+), 185 deletions(-) create mode 100644 sandbox/datgui/dat.gui.js create mode 100644 sandbox/stats/stats.js diff --git a/sandbox/datgui/dat.gui.js b/sandbox/datgui/dat.gui.js new file mode 100644 index 0000000000..0725382b53 --- /dev/null +++ b/sandbox/datgui/dat.gui.js @@ -0,0 +1,13 @@ +/** + * dat-gui JavaScript Controller Library + * http://code.google.com/p/dat-gui + * + * Copyright 2011 Data Arts Team, Google Creative Lab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.dat={})}(this,function(e){"use strict";function t(e,t){var n=e.__state.conversionName.toString(),o=Math.round(e.r),i=Math.round(e.g),r=Math.round(e.b),s=e.a,a=Math.round(e.h),l=e.s.toFixed(1),d=e.v.toFixed(1);if(t||"THREE_CHAR_HEX"===n||"SIX_CHAR_HEX"===n){for(var c=e.hex.toString(16);c.length<6;)c="0"+c;return"#"+c}return"CSS_RGB"===n?"rgb("+o+","+i+","+r+")":"CSS_RGBA"===n?"rgba("+o+","+i+","+r+","+s+")":"HEX"===n?"0x"+e.hex.toString(16):"RGB_ARRAY"===n?"["+o+","+i+","+r+"]":"RGBA_ARRAY"===n?"["+o+","+i+","+r+","+s+"]":"RGB_OBJ"===n?"{r:"+o+",g:"+i+",b:"+r+"}":"RGBA_OBJ"===n?"{r:"+o+",g:"+i+",b:"+r+",a:"+s+"}":"HSV_OBJ"===n?"{h:"+a+",s:"+l+",v:"+d+"}":"HSVA_OBJ"===n?"{h:"+a+",s:"+l+",v:"+d+",a:"+s+"}":"unknown format"}function n(e,t,n){Object.defineProperty(e,t,{get:function(){return"RGB"===this.__state.space?this.__state[t]:(I.recalculateRGB(this,t,n),this.__state[t])},set:function(e){"RGB"!==this.__state.space&&(I.recalculateRGB(this,t,n),this.__state.space="RGB"),this.__state[t]=e}})}function o(e,t){Object.defineProperty(e,t,{get:function(){return"HSV"===this.__state.space?this.__state[t]:(I.recalculateHSV(this),this.__state[t])},set:function(e){"HSV"!==this.__state.space&&(I.recalculateHSV(this),this.__state.space="HSV"),this.__state[t]=e}})}function i(e){if("0"===e||S.isUndefined(e))return 0;var t=e.match(U);return S.isNull(t)?0:parseFloat(t[1])}function r(e){var t=e.toString();return t.indexOf(".")>-1?t.length-t.indexOf(".")-1:0}function s(e,t){var n=Math.pow(10,t);return Math.round(e*n)/n}function a(e,t,n,o,i){return o+(e-t)/(n-t)*(i-o)}function l(e,t,n,o){e.style.background="",S.each(ee,function(i){e.style.cssText+="background: "+i+"linear-gradient("+t+", "+n+" 0%, "+o+" 100%); "})}function d(e){e.style.background="",e.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);",e.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",e.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",e.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",e.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}function c(e,t,n){var o=document.createElement("li");return t&&o.appendChild(t),n?e.__ul.insertBefore(o,n):e.__ul.appendChild(o),e.onResize(),o}function u(e){X.unbind(window,"resize",e.__resizeHandler),e.saveToLocalStorageIfPossible&&X.unbind(window,"unload",e.saveToLocalStorageIfPossible)}function _(e,t){var n=e.__preset_select[e.__preset_select.selectedIndex];n.innerHTML=t?n.value+"*":n.value}function h(e,t,n){if(n.__li=t,n.__gui=e,S.extend(n,{options:function(t){if(arguments.length>1){var o=n.__li.nextElementSibling;return n.remove(),f(e,n.object,n.property,{before:o,factoryArgs:[S.toArray(arguments)]})}if(S.isArray(t)||S.isObject(t)){var i=n.__li.nextElementSibling;return n.remove(),f(e,n.object,n.property,{before:i,factoryArgs:[t]})}},name:function(e){return n.__li.firstElementChild.firstElementChild.innerHTML=e,n},listen:function(){return n.__gui.listen(n),n},remove:function(){return n.__gui.remove(n),n}}),n instanceof q){var o=new Q(n.object,n.property,{min:n.__min,max:n.__max,step:n.__step});S.each(["updateDisplay","onChange","onFinishChange","step","min","max"],function(e){var t=n[e],i=o[e];n[e]=o[e]=function(){var e=Array.prototype.slice.call(arguments);return i.apply(o,e),t.apply(n,e)}}),X.addClass(t,"has-slider"),n.domElement.insertBefore(o.domElement,n.domElement.firstElementChild)}else if(n instanceof Q){var i=function(t){if(S.isNumber(n.__min)&&S.isNumber(n.__max)){var o=n.__li.firstElementChild.firstElementChild.innerHTML,i=n.__gui.__listening.indexOf(n)>-1;n.remove();var r=f(e,n.object,n.property,{before:n.__li.nextElementSibling,factoryArgs:[n.__min,n.__max,n.__step]});return r.name(o),i&&r.listen(),r}return t};n.min=S.compose(i,n.min),n.max=S.compose(i,n.max)}else n instanceof K?(X.bind(t,"click",function(){X.fakeEvent(n.__checkbox,"click")}),X.bind(n.__checkbox,"click",function(e){e.stopPropagation()})):n instanceof Z?(X.bind(t,"click",function(){X.fakeEvent(n.__button,"click")}),X.bind(t,"mouseover",function(){X.addClass(n.__button,"hover")}),X.bind(t,"mouseout",function(){X.removeClass(n.__button,"hover")})):n instanceof $&&(X.addClass(t,"color"),n.updateDisplay=S.compose(function(e){return t.style.borderLeftColor=n.__color.toString(),e},n.updateDisplay),n.updateDisplay());n.setValue=S.compose(function(t){return e.getRoot().__preset_select&&n.isModified()&&_(e.getRoot(),!0),t},n.setValue)}function p(e,t){var n=e.getRoot(),o=n.__rememberedObjects.indexOf(t.object);if(-1!==o){var i=n.__rememberedObjectIndecesToControllers[o];if(void 0===i&&(i={},n.__rememberedObjectIndecesToControllers[o]=i),i[t.property]=t,n.load&&n.load.remembered){var r=n.load.remembered,s=void 0;if(r[e.preset])s=r[e.preset];else{if(!r[se])return;s=r[se]}if(s[o]&&void 0!==s[o][t.property]){var a=s[o][t.property];t.initialValue=a,t.setValue(a)}}}}function f(e,t,n,o){if(void 0===t[n])throw new Error('Object "'+t+'" has no property "'+n+'"');var i=void 0;if(o.color)i=new $(t,n);else{var r=[t,n].concat(o.factoryArgs);i=ne.apply(e,r)}o.before instanceof z&&(o.before=o.before.__li),p(e,i),X.addClass(i.domElement,"c");var s=document.createElement("span");X.addClass(s,"property-name"),s.innerHTML=i.property;var a=document.createElement("div");a.appendChild(s),a.appendChild(i.domElement);var l=c(e,a,o.before);return X.addClass(l,he.CLASS_CONTROLLER_ROW),i instanceof $?X.addClass(l,"color"):X.addClass(l,H(i.getValue())),h(e,l,i),e.__controllers.push(i),i}function m(e,t){return document.location.href+"."+t}function g(e,t,n){var o=document.createElement("option");o.innerHTML=t,o.value=t,e.__preset_select.appendChild(o),n&&(e.__preset_select.selectedIndex=e.__preset_select.length-1)}function b(e,t){t.style.display=e.useLocalStorage?"block":"none"}function v(e){var t=e.__save_row=document.createElement("li");X.addClass(e.domElement,"has-save"),e.__ul.insertBefore(t,e.__ul.firstChild),X.addClass(t,"save-row");var n=document.createElement("span");n.innerHTML=" ",X.addClass(n,"button gears");var o=document.createElement("span");o.innerHTML="Save",X.addClass(o,"button"),X.addClass(o,"save");var i=document.createElement("span");i.innerHTML="New",X.addClass(i,"button"),X.addClass(i,"save-as");var r=document.createElement("span");r.innerHTML="Revert",X.addClass(r,"button"),X.addClass(r,"revert");var s=e.__preset_select=document.createElement("select");if(e.load&&e.load.remembered?S.each(e.load.remembered,function(t,n){g(e,n,n===e.preset)}):g(e,se,!1),X.bind(s,"change",function(){for(var t=0;t=0;n--)t=[e[n].apply(this,t)];return t[0]}},each:function(e,t,n){if(e)if(A&&e.forEach&&e.forEach===A)e.forEach(t,n);else if(e.length===e.length+0){var o=void 0,i=void 0;for(o=0,i=e.length;o1?S.toArray(arguments):arguments[0];return S.each(O,function(t){if(t.litmus(e))return S.each(t.conversions,function(t,n){if(T=t.read(e),!1===L&&!1!==T)return L=T,T.conversionName=n,T.conversion=t,S.BREAK}),S.BREAK}),L},B=void 0,N={hsv_to_rgb:function(e,t,n){var o=Math.floor(e/60)%6,i=e/60-Math.floor(e/60),r=n*(1-t),s=n*(1-i*t),a=n*(1-(1-i)*t),l=[[n,a,r],[s,n,r],[r,n,a],[r,s,n],[a,r,n],[n,r,s]][o];return{r:255*l[0],g:255*l[1],b:255*l[2]}},rgb_to_hsv:function(e,t,n){var o=Math.min(e,t,n),i=Math.max(e,t,n),r=i-o,s=void 0,a=void 0;return 0===i?{h:NaN,s:0,v:0}:(a=r/i,s=e===i?(t-n)/r:t===i?2+(n-e)/r:4+(e-t)/r,(s/=6)<0&&(s+=1),{h:360*s,s:a,v:i/255})},rgb_to_hex:function(e,t,n){var o=this.hex_with_component(0,2,e);return o=this.hex_with_component(o,1,t),o=this.hex_with_component(o,0,n)},component_from_hex:function(e,t){return e>>8*t&255},hex_with_component:function(e,t,n){return n<<(B=8*t)|e&~(255<this.__max&&(n=this.__max),void 0!==this.__step&&n%this.__step!=0&&(n=Math.round(n/this.__step)*this.__step),D(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"setValue",this).call(this,n)}},{key:"min",value:function(e){return this.__min=e,this}},{key:"max",value:function(e){return this.__max=e,this}},{key:"step",value:function(e){return this.__step=e,this.__impliedStep=e,this.__precision=r(e),this}}]),t}(),Q=function(e){function t(e,n,o){function i(){l.__onFinishChange&&l.__onFinishChange.call(l,l.getValue())}function r(e){var t=d-e.clientY;l.setValue(l.getValue()+t*l.__impliedStep),d=e.clientY}function s(){X.unbind(window,"mousemove",r),X.unbind(window,"mouseup",s),i()}F(this,t);var a=V(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,o));a.__truncationSuspended=!1;var l=a,d=void 0;return a.__input=document.createElement("input"),a.__input.setAttribute("type","text"),X.bind(a.__input,"change",function(){var e=parseFloat(l.__input.value);S.isNaN(e)||l.setValue(e)}),X.bind(a.__input,"blur",function(){i()}),X.bind(a.__input,"mousedown",function(e){X.bind(window,"mousemove",r),X.bind(window,"mouseup",s),d=e.clientY}),X.bind(a.__input,"keydown",function(e){13===e.keyCode&&(l.__truncationSuspended=!0,this.blur(),l.__truncationSuspended=!1,i())}),a.updateDisplay(),a.domElement.appendChild(a.__input),a}return j(t,W),P(t,[{key:"updateDisplay",value:function(){return this.__input.value=this.__truncationSuspended?this.getValue():s(this.getValue(),this.__precision),D(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"updateDisplay",this).call(this)}}]),t}(),q=function(e){function t(e,n,o,i,r){function s(e){e.preventDefault();var t=_.__background.getBoundingClientRect();return _.setValue(a(e.clientX,t.left,t.right,_.__min,_.__max)),!1}function l(){X.unbind(window,"mousemove",s),X.unbind(window,"mouseup",l),_.__onFinishChange&&_.__onFinishChange.call(_,_.getValue())}function d(e){var t=e.touches[0].clientX,n=_.__background.getBoundingClientRect();_.setValue(a(t,n.left,n.right,_.__min,_.__max))}function c(){X.unbind(window,"touchmove",d),X.unbind(window,"touchend",c),_.__onFinishChange&&_.__onFinishChange.call(_,_.getValue())}F(this,t);var u=V(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n,{min:o,max:i,step:r})),_=u;return u.__background=document.createElement("div"),u.__foreground=document.createElement("div"),X.bind(u.__background,"mousedown",function(e){document.activeElement.blur(),X.bind(window,"mousemove",s),X.bind(window,"mouseup",l),s(e)}),X.bind(u.__background,"touchstart",function(e){1===e.touches.length&&(X.bind(window,"touchmove",d),X.bind(window,"touchend",c),d(e))}),X.addClass(u.__background,"slider"),X.addClass(u.__foreground,"slider-fg"),u.updateDisplay(),u.__background.appendChild(u.__foreground),u.domElement.appendChild(u.__background),u}return j(t,W),P(t,[{key:"updateDisplay",value:function(){var e=(this.getValue()-this.__min)/(this.__max-this.__min);return this.__foreground.style.width=100*e+"%",D(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"updateDisplay",this).call(this)}}]),t}(),Z=function(e){function t(e,n,o){F(this,t);var i=V(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n)),r=i;return i.__button=document.createElement("div"),i.__button.innerHTML=void 0===o?"Fire":o,X.bind(i.__button,"click",function(e){return e.preventDefault(),r.fire(),!1}),X.addClass(i.__button,"button"),i.domElement.appendChild(i.__button),i}return j(t,z),P(t,[{key:"fire",value:function(){this.__onChange&&this.__onChange.call(this),this.getValue().call(this.object),this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue())}}]),t}(),$=function(e){function t(e,n){function o(e){u(e),X.bind(window,"mousemove",u),X.bind(window,"touchmove",u),X.bind(window,"mouseup",r),X.bind(window,"touchend",r)}function i(e){_(e),X.bind(window,"mousemove",_),X.bind(window,"touchmove",_),X.bind(window,"mouseup",s),X.bind(window,"touchend",s)}function r(){X.unbind(window,"mousemove",u),X.unbind(window,"touchmove",u),X.unbind(window,"mouseup",r),X.unbind(window,"touchend",r),c()}function s(){X.unbind(window,"mousemove",_),X.unbind(window,"touchmove",_),X.unbind(window,"mouseup",s),X.unbind(window,"touchend",s),c()}function a(){var e=R(this.value);!1!==e?(p.__color.__state=e,p.setValue(p.__color.toOriginal())):this.value=p.__color.toString()}function c(){p.__onFinishChange&&p.__onFinishChange.call(p,p.__color.toOriginal())}function u(e){-1===e.type.indexOf("touch")&&e.preventDefault();var t=p.__saturation_field.getBoundingClientRect(),n=e.touches&&e.touches[0]||e,o=n.clientX,i=n.clientY,r=(o-t.left)/(t.right-t.left),s=1-(i-t.top)/(t.bottom-t.top);return s>1?s=1:s<0&&(s=0),r>1?r=1:r<0&&(r=0),p.__color.v=s,p.__color.s=r,p.setValue(p.__color.toOriginal()),!1}function _(e){-1===e.type.indexOf("touch")&&e.preventDefault();var t=p.__hue_field.getBoundingClientRect(),n=1-((e.touches&&e.touches[0]||e).clientY-t.top)/(t.bottom-t.top);return n>1?n=1:n<0&&(n=0),p.__color.h=360*n,p.setValue(p.__color.toOriginal()),!1}F(this,t);var h=V(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n));h.__color=new I(h.getValue()),h.__temp=new I(0);var p=h;h.domElement=document.createElement("div"),X.makeSelectable(h.domElement,!1),h.__selector=document.createElement("div"),h.__selector.className="selector",h.__saturation_field=document.createElement("div"),h.__saturation_field.className="saturation-field",h.__field_knob=document.createElement("div"),h.__field_knob.className="field-knob",h.__field_knob_border="2px solid ",h.__hue_knob=document.createElement("div"),h.__hue_knob.className="hue-knob",h.__hue_field=document.createElement("div"),h.__hue_field.className="hue-field",h.__input=document.createElement("input"),h.__input.type="text",h.__input_textShadow="0 1px 1px ",X.bind(h.__input,"keydown",function(e){13===e.keyCode&&a.call(this)}),X.bind(h.__input,"blur",a),X.bind(h.__selector,"mousedown",function(){X.addClass(this,"drag").bind(window,"mouseup",function(){X.removeClass(p.__selector,"drag")})}),X.bind(h.__selector,"touchstart",function(){X.addClass(this,"drag").bind(window,"touchend",function(){X.removeClass(p.__selector,"drag")})});var f=document.createElement("div");return S.extend(h.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"}),S.extend(h.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:h.__field_knob_border+(h.__color.v<.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1}),S.extend(h.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1}),S.extend(h.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"}),S.extend(f.style,{width:"100%",height:"100%",background:"none"}),l(f,"top","rgba(0,0,0,0)","#000"),S.extend(h.__hue_field.style,{width:"15px",height:"100px",border:"1px solid #555",cursor:"ns-resize",position:"absolute",top:"3px",right:"3px"}),d(h.__hue_field),S.extend(h.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:h.__input_textShadow+"rgba(0,0,0,0.7)"}),X.bind(h.__saturation_field,"mousedown",o),X.bind(h.__saturation_field,"touchstart",o),X.bind(h.__field_knob,"mousedown",o),X.bind(h.__field_knob,"touchstart",o),X.bind(h.__hue_field,"mousedown",i),X.bind(h.__hue_field,"touchstart",i),h.__saturation_field.appendChild(f),h.__selector.appendChild(h.__field_knob),h.__selector.appendChild(h.__saturation_field),h.__selector.appendChild(h.__hue_field),h.__hue_field.appendChild(h.__hue_knob),h.domElement.appendChild(h.__input),h.domElement.appendChild(h.__selector),h.updateDisplay(),h}return j(t,z),P(t,[{key:"updateDisplay",value:function(){var e=R(this.getValue());if(!1!==e){var t=!1;S.each(I.COMPONENTS,function(n){if(!S.isUndefined(e[n])&&!S.isUndefined(this.__color.__state[n])&&e[n]!==this.__color.__state[n])return t=!0,{}},this),t&&S.extend(this.__color.__state,e)}S.extend(this.__temp.__state,this.__color.__state),this.__temp.a=1;var n=this.__color.v<.5||this.__color.s>.5?255:0,o=255-n;S.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toHexString(),border:this.__field_knob_border+"rgb("+n+","+n+","+n+")"}),this.__hue_knob.style.marginTop=100*(1-this.__color.h/360)+"px",this.__temp.s=1,this.__temp.v=1,l(this.__saturation_field,"left","#fff",this.__temp.toHexString()),this.__input.value=this.__color.toString(),S.extend(this.__input.style,{backgroundColor:this.__color.toHexString(),color:"rgb("+n+","+n+","+n+")",textShadow:this.__input_textShadow+"rgba("+o+","+o+","+o+",.7)"})}}]),t}(),ee=["-moz-","-o-","-webkit-","-ms-",""],te={load:function(e,t){var n=t||document,o=n.createElement("link");o.type="text/css",o.rel="stylesheet",o.href=e,n.getElementsByTagName("head")[0].appendChild(o)},inject:function(e,t){var n=t||document,o=document.createElement("style");o.type="text/css",o.innerHTML=e;var i=n.getElementsByTagName("head")[0];try{i.appendChild(o)}catch(e){}}},ne=function(e,t){var n=e[t];return S.isArray(arguments[2])||S.isObject(arguments[2])?new Y(e,t,arguments[2]):S.isNumber(n)?S.isNumber(arguments[2])&&S.isNumber(arguments[3])?S.isNumber(arguments[4])?new q(e,t,arguments[2],arguments[3],arguments[4]):new q(e,t,arguments[2],arguments[3]):S.isNumber(arguments[4])?new Q(e,t,{min:arguments[2],max:arguments[3],step:arguments[4]}):new Q(e,t,{min:arguments[2],max:arguments[3]}):S.isString(n)?new J(e,t):S.isFunction(n)?new Z(e,t,""):S.isBoolean(n)?new K(e,t):null},oe=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e){setTimeout(e,1e3/60)},ie=function(){function e(){F(this,e),this.backgroundElement=document.createElement("div"),S.extend(this.backgroundElement.style,{backgroundColor:"rgba(0,0,0,0.8)",top:0,left:0,display:"none",zIndex:"1000",opacity:0,WebkitTransition:"opacity 0.2s linear",transition:"opacity 0.2s linear"}),X.makeFullscreen(this.backgroundElement),this.backgroundElement.style.position="fixed",this.domElement=document.createElement("div"),S.extend(this.domElement.style,{position:"fixed",display:"none",zIndex:"1001",opacity:0,WebkitTransition:"-webkit-transform 0.2s ease-out, opacity 0.2s linear",transition:"transform 0.2s ease-out, opacity 0.2s linear"}),document.body.appendChild(this.backgroundElement),document.body.appendChild(this.domElement);var t=this;X.bind(this.backgroundElement,"click",function(){t.hide()})}return P(e,[{key:"show",value:function(){var e=this;this.backgroundElement.style.display="block",this.domElement.style.display="block",this.domElement.style.opacity=0,this.domElement.style.webkitTransform="scale(1.1)",this.layout(),S.defer(function(){e.backgroundElement.style.opacity=1,e.domElement.style.opacity=1,e.domElement.style.webkitTransform="scale(1)"})}},{key:"hide",value:function(){var e=this,t=function t(){e.domElement.style.display="none",e.backgroundElement.style.display="none",X.unbind(e.domElement,"webkitTransitionEnd",t),X.unbind(e.domElement,"transitionend",t),X.unbind(e.domElement,"oTransitionEnd",t)};X.bind(this.domElement,"webkitTransitionEnd",t),X.bind(this.domElement,"transitionend",t),X.bind(this.domElement,"oTransitionEnd",t),this.backgroundElement.style.opacity=0,this.domElement.style.opacity=0,this.domElement.style.webkitTransform="scale(1.1)"}},{key:"layout",value:function(){this.domElement.style.left=window.innerWidth/2-X.getWidth(this.domElement)/2+"px",this.domElement.style.top=window.innerHeight/2-X.getHeight(this.domElement)/2+"px"}}]),e}(),re=function(e){if(e&&"undefined"!=typeof window){var t=document.createElement("style");return t.setAttribute("type","text/css"),t.innerHTML=e,document.head.appendChild(t),e}}(".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear;border:0;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button.close-top{position:relative}.dg.main .close-button.close-bottom{position:absolute}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-y:visible}.dg.a.has-save>ul.close-top{margin-top:0}.dg.a.has-save>ul.close-bottom{margin-top:27px}.dg.a.has-save>ul.closed{margin-top:0}.dg.a .save-row{top:0;z-index:1002}.dg.a .save-row.close-top{position:relative}.dg.a .save-row.close-bottom{position:fixed}.dg li{-webkit-transition:height .1s ease-out;-o-transition:height .1s ease-out;-moz-transition:height .1s ease-out;transition:height .1s ease-out;-webkit-transition:overflow .1s linear;-o-transition:overflow .1s linear;-moz-transition:overflow .1s linear;transition:overflow .1s linear}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li>*{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px;overflow:hidden}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .c{float:left;width:60%;position:relative}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:7px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .cr.color{overflow:visible}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.color{border-left:3px solid}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2FA1D6}.dg .cr.number input[type=text]{color:#2FA1D6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2FA1D6;max-width:100%}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n");te.inject(re);var se="Default",ae=function(){try{return!!window.localStorage}catch(e){return!1}}(),le=void 0,de=!0,ce=void 0,ue=!1,_e=[],he=function e(t){var n=this,o=t||{};this.domElement=document.createElement("div"),this.__ul=document.createElement("ul"),this.domElement.appendChild(this.__ul),X.addClass(this.domElement,"dg"),this.__folders={},this.__controllers=[],this.__rememberedObjects=[],this.__rememberedObjectIndecesToControllers=[],this.__listening=[],o=S.defaults(o,{closeOnTop:!1,autoPlace:!0,width:e.DEFAULT_WIDTH}),o=S.defaults(o,{resizable:o.autoPlace,hideable:o.autoPlace}),S.isUndefined(o.load)?o.load={preset:se}:o.preset&&(o.load.preset=o.preset),S.isUndefined(o.parent)&&o.hideable&&_e.push(this),o.resizable=S.isUndefined(o.parent)&&o.resizable,o.autoPlace&&S.isUndefined(o.scrollable)&&(o.scrollable=!0);var i=ae&&"true"===localStorage.getItem(m(this,"isLocal")),r=void 0,s=void 0;if(Object.defineProperties(this,{parent:{get:function(){return o.parent}},scrollable:{get:function(){return o.scrollable}},autoPlace:{get:function(){return o.autoPlace}},closeOnTop:{get:function(){return o.closeOnTop}},preset:{get:function(){return n.parent?n.getRoot().preset:o.load.preset},set:function(e){n.parent?n.getRoot().preset=e:o.load.preset=e,E(this),n.revert()}},width:{get:function(){return o.width},set:function(e){o.width=e,w(n,e)}},name:{get:function(){return o.name},set:function(e){o.name=e,s&&(s.innerHTML=o.name)}},closed:{get:function(){return o.closed},set:function(t){o.closed=t,o.closed?X.addClass(n.__ul,e.CLASS_CLOSED):X.removeClass(n.__ul,e.CLASS_CLOSED),this.onResize(),n.__closeButton&&(n.__closeButton.innerHTML=t?e.TEXT_OPEN:e.TEXT_CLOSED)}},load:{get:function(){return o.load}},useLocalStorage:{get:function(){return i},set:function(e){ae&&(i=e,e?X.bind(window,"unload",r):X.unbind(window,"unload",r),localStorage.setItem(m(n,"isLocal"),e))}}}),S.isUndefined(o.parent)){if(this.closed=o.closed||!1,X.addClass(this.domElement,e.CLASS_MAIN),X.makeSelectable(this.domElement,!1),ae&&i){n.useLocalStorage=!0;var a=localStorage.getItem(m(this,"gui"));a&&(o.load=JSON.parse(a))}this.__closeButton=document.createElement("div"),this.__closeButton.innerHTML=e.TEXT_CLOSED,X.addClass(this.__closeButton,e.CLASS_CLOSE_BUTTON),o.closeOnTop?(X.addClass(this.__closeButton,e.CLASS_CLOSE_TOP),this.domElement.insertBefore(this.__closeButton,this.domElement.childNodes[0])):(X.addClass(this.__closeButton,e.CLASS_CLOSE_BOTTOM),this.domElement.appendChild(this.__closeButton)),X.bind(this.__closeButton,"click",function(){n.closed=!n.closed})}else{void 0===o.closed&&(o.closed=!0);var l=document.createTextNode(o.name);X.addClass(l,"controller-name"),s=c(n,l);X.addClass(this.__ul,e.CLASS_CLOSED),X.addClass(s,"title"),X.bind(s,"click",function(e){return e.preventDefault(),n.closed=!n.closed,!1}),o.closed||(this.closed=!1)}o.autoPlace&&(S.isUndefined(o.parent)&&(de&&(ce=document.createElement("div"),X.addClass(ce,"dg"),X.addClass(ce,e.CLASS_AUTO_PLACE_CONTAINER),document.body.appendChild(ce),de=!1),ce.appendChild(this.domElement),X.addClass(this.domElement,e.CLASS_AUTO_PLACE)),this.parent||w(n,o.width)),this.__resizeHandler=function(){n.onResizeDebounced()},X.bind(window,"resize",this.__resizeHandler),X.bind(this.__ul,"webkitTransitionEnd",this.__resizeHandler),X.bind(this.__ul,"transitionend",this.__resizeHandler),X.bind(this.__ul,"oTransitionEnd",this.__resizeHandler),this.onResize(),o.resizable&&y(this),r=function(){ae&&"true"===localStorage.getItem(m(n,"isLocal"))&&localStorage.setItem(m(n,"gui"),JSON.stringify(n.getSaveObject()))},this.saveToLocalStorageIfPossible=r,o.parent||function(){var e=n.getRoot();e.width+=1,S.defer(function(){e.width-=1})}()};he.toggleHide=function(){ue=!ue,S.each(_e,function(e){e.domElement.style.display=ue?"none":""})},he.CLASS_AUTO_PLACE="a",he.CLASS_AUTO_PLACE_CONTAINER="ac",he.CLASS_MAIN="main",he.CLASS_CONTROLLER_ROW="cr",he.CLASS_TOO_TALL="taller-than-window",he.CLASS_CLOSED="closed",he.CLASS_CLOSE_BUTTON="close-button",he.CLASS_CLOSE_TOP="close-top",he.CLASS_CLOSE_BOTTOM="close-bottom",he.CLASS_DRAG="drag",he.DEFAULT_WIDTH=245,he.TEXT_CLOSED="Close Controls",he.TEXT_OPEN="Open Controls",he._keydownHandler=function(e){"text"===document.activeElement.type||72!==e.which&&72!==e.keyCode||he.toggleHide()},X.bind(window,"keydown",he._keydownHandler,!1),S.extend(he.prototype,{add:function(e,t){return f(this,e,t,{factoryArgs:Array.prototype.slice.call(arguments,2)})},addColor:function(e,t){return f(this,e,t,{color:!0})},remove:function(e){this.__ul.removeChild(e.__li),this.__controllers.splice(this.__controllers.indexOf(e),1);var t=this;S.defer(function(){t.onResize()})},destroy:function(){if(this.parent)throw new Error("Only the root GUI should be removed with .destroy(). For subfolders, use gui.removeFolder(folder) instead.");this.autoPlace&&ce.removeChild(this.domElement);var e=this;S.each(this.__folders,function(t){e.removeFolder(t)}),X.unbind(window,"keydown",he._keydownHandler,!1),u(this)},addFolder:function(e){if(void 0!==this.__folders[e])throw new Error('You already have a folder in this GUI by the name "'+e+'"');var t={name:e,parent:this};t.autoPlace=this.autoPlace,this.load&&this.load.folders&&this.load.folders[e]&&(t.closed=this.load.folders[e].closed,t.load=this.load.folders[e]);var n=new he(t);this.__folders[e]=n;var o=c(this,n.domElement);return X.addClass(o,"folder"),n},removeFolder:function(e){this.__ul.removeChild(e.domElement.parentElement),delete this.__folders[e.name],this.load&&this.load.folders&&this.load.folders[e.name]&&delete this.load.folders[e.name],u(e);var t=this;S.each(e.__folders,function(t){e.removeFolder(t)}),S.defer(function(){t.onResize()})},open:function(){this.closed=!1},close:function(){this.closed=!0},hide:function(){this.domElement.style.display="none"},show:function(){this.domElement.style.display=""},onResize:function(){var e=this.getRoot();if(e.scrollable){var t=X.getOffset(e.__ul).top,n=0;S.each(e.__ul.childNodes,function(t){e.autoPlace&&t===e.__save_row||(n+=X.getHeight(t))}),window.innerHeight-t-20GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n\n
\n\n
\n\n'),this.parent)throw new Error("You can only call remember on a top level GUI.");var e=this;S.each(Array.prototype.slice.call(arguments),function(t){0===e.__rememberedObjects.length&&v(e),-1===e.__rememberedObjects.indexOf(t)&&e.__rememberedObjects.push(t)}),this.autoPlace&&w(this,this.width)},getRoot:function(){for(var e=this;e.parent;)e=e.parent;return e},getSaveObject:function(){var e=this.load;return e.closed=this.closed,this.__rememberedObjects.length>0&&(e.preset=this.preset,e.remembered||(e.remembered={}),e.remembered[this.preset]=x(this)),e.folders={},S.each(this.__folders,function(t,n){e.folders[n]=t.getSaveObject()}),e},save:function(){this.load.remembered||(this.load.remembered={}),this.load.remembered[this.preset]=x(this),_(this,!1),this.saveToLocalStorageIfPossible()},saveAs:function(e){this.load.remembered||(this.load.remembered={},this.load.remembered[se]=x(this,!0)),this.load.remembered[e]=x(this),this.preset=e,g(this,e,!0),this.saveToLocalStorageIfPossible()},revert:function(e){S.each(this.__controllers,function(t){this.getRoot().load.remembered?p(e||this.getRoot(),t):t.setValue(t.initialValue),t.__onFinishChange&&t.__onFinishChange.call(t,t.getValue())},this),S.each(this.__folders,function(e){e.revert(e)}),e||_(this.getRoot(),!1)},listen:function(e){var t=0===this.__listening.length;this.__listening.push(e),t&&C(this.__listening)},updateDisplay:function(){S.each(this.__controllers,function(e){e.updateDisplay()}),S.each(this.__folders,function(e){e.updateDisplay()})}});var pe={Color:I,math:N,interpret:R},fe={Controller:z,BooleanController:K,OptionController:Y,StringController:J,NumberController:W,NumberControllerBox:Q,NumberControllerSlider:q,FunctionController:Z,ColorController:$},me={dom:X},ge={GUI:he},be=he,ve={color:pe,controllers:fe,dom:me,gui:ge,GUI:be};e.color=pe,e.controllers=fe,e.dom=me,e.gui=ge,e.GUI=be,e.default=ve,Object.defineProperty(e,"__esModule",{value:!0})}); \ No newline at end of file diff --git a/sandbox/src/game.ts b/sandbox/src/game.ts index 289f8d22bd..6470d93f8e 100644 --- a/sandbox/src/game.ts +++ b/sandbox/src/game.ts @@ -59,7 +59,7 @@ document.body.appendChild(stats.dom); var bootstrap = (game: ex.Engine) => { gui.add({toggleDebug: false}, 'toggleDebug').onChange(() => game.toggleDebug()); - var supportedKeys = ['filter', 'entity', 'transform', 'motion', 'body', 'collider', 'graphics', 'camera']; + var supportedKeys = ['filter', 'entity', 'transform', 'motion', 'body', 'collider', 'physics', 'graphics', 'camera']; for (let key of supportedKeys) { let folder = gui.addFolder(key); if (game.debug[key]) { @@ -80,13 +80,8 @@ var bootstrap = (game: ex.Engine) => { var physics = gui.addFolder('Physics Flags'); physics.add(ex.Physics, 'enabled') - physics.add(ex.Physics.debug, 'showColliderBounds') - physics.add(ex.Physics.debug, 'showColliderGeometry') - physics.add(ex.Physics.debug, 'showColliderNormals') - physics.add(ex.Physics.debug, 'showContacts') - physics.add(ex.Physics.debug, 'broadphaseDebug') - physics.add(ex.Physics, "positionIterations") - physics.add(ex.Physics, "velocityIterations") + physics.add(ex.Physics, "positionIterations", 1, 15, 1); + physics.add(ex.Physics, "velocityIterations", 1, 15, 1); game.on("preframe", () => { stats.begin(); diff --git a/sandbox/stats/stats.js b/sandbox/stats/stats.js new file mode 100644 index 0000000000..2f1a0e1361 --- /dev/null +++ b/sandbox/stats/stats.js @@ -0,0 +1,5 @@ +// stats.js - http://github.com/mrdoob/stats.js +(function(f,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"===typeof define&&define.amd?define(e):f.Stats=e()})(this,function(){var f=function(){function e(a){c.appendChild(a.dom);return a}function u(a){for(var d=0;d=g+1E3&&(r.update(1E3*a/(c-g),100),g=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/ + 1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){k=this.end()},domElement:c,setMode:u}};f.Panel=function(e,f,l){var c=Infinity,k=0,g=Math.round,a=g(window.devicePixelRatio||1),r=80*a,h=48*a,t=3*a,v=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=h;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,h);b.fillStyle=f;b.fillText(e,t,v); + b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(h,w){c=Math.min(c,h);k=Math.max(k,h);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=f;b.fillText(g(h)+" "+e+" ("+g(c)+"-"+g(k)+")",t,v);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,g((1-h/w)*p))}}};return f}); \ No newline at end of file diff --git a/sandbox/tests/collisionvelocity/vel.ts b/sandbox/tests/collisionvelocity/vel.ts index ba6b82b17a..4b3b20fdc8 100644 --- a/sandbox/tests/collisionvelocity/vel.ts +++ b/sandbox/tests/collisionvelocity/vel.ts @@ -7,11 +7,11 @@ var engine = new ex.Engine({ pointerScope: ex.Input.PointerScope.Canvas }); engine.showDebug(true); +engine.debug.body.showAll = true; +engine.debug.collider.showGeometry = true; +engine.debug.physics.showCollisionContacts = true; +engine.debug.physics.showCollisionNormals = true; ex.Physics.acc.setTo(0, 200); -ex.Physics.debug.showMotionVectors = true; -ex.Physics.debug.showColliderGeometry = true; -ex.Physics.debug.showContacts = true; -ex.Physics.debug.showCollisionNormals = true; var floor = new ex.Actor({ x: engine.halfDrawWidth, diff --git a/sandbox/tests/physics/fastphysics.ts b/sandbox/tests/physics/fastphysics.ts index 8134911143..0762ac8827 100644 --- a/sandbox/tests/physics/fastphysics.ts +++ b/sandbox/tests/physics/fastphysics.ts @@ -1,17 +1,16 @@ /// +ex.Physics.useRealisticPhysics() var game = new ex.Engine({ width: 600, height: 400 }); -ex.Physics.collisionResolutionStrategy = ex.CollisionResolutionStrategy.Realistic; - -ex.Physics.debug.broadphaseDebug = false; -ex.Physics.debug.showColliderGeometry = true; -ex.Physics.debug.showMotionVectors = true; -ex.Physics.debug.showColliderBounds = true; -ex.Physics.debug.showContacts = true; -ex.Physics.debug.showNormals = true; + +game.debug.physics.showBroadphaseSpacePartitionDebug = false; +game.debug.physics.showCollisionContacts = true; +game.debug.collider.showGeometry = true; +game.debug.motion.showAll = true; +game.debug.collider.showBounds = true; ex.Physics.acc.setTo(0, 300); //ex.Physics.dynamicTreeVelocityMultiplyer = 1; game.currentScene.camera.zoom = 0.5; diff --git a/sandbox/tests/physics/physics.ts b/sandbox/tests/physics/physics.ts index 0da7adee19..275026e1f2 100644 --- a/sandbox/tests/physics/physics.ts +++ b/sandbox/tests/physics/physics.ts @@ -7,16 +7,15 @@ var game = new ex.Engine({ game.backgroundColor = ex.Color.Black; game.showDebug(true); +game.debug.physics.showBroadphaseSpacePartitionDebug = false; +game.debug.physics.showCollisionContacts = true; +game.debug.collider.showGeometry = true; +game.debug.collider.showBounds = true; +game.debug.motion.showAll = true; +game.debug.body.showMotion = true; ex.Physics.collisionResolutionStrategy = ex.CollisionResolutionStrategy.Realistic; ex.Physics.bodiesCanSleepByDefault = true; -ex.Physics.debug.broadphaseDebug = false; -ex.Physics.debug.showColliderGeometry = true; -ex.Physics.debug.showMotionVectors = true; -ex.Physics.debug.showSleepMotion = true; -ex.Physics.debug.showColliderBounds = true; -ex.Physics.debug.showContacts = true; -ex.Physics.debug.showNormals = true; ex.Physics.gravity = ex.vec(0, 100); @@ -25,14 +24,6 @@ var folder = gui.addFolder('Physics Flags'); folder.add(ex.Physics, 'enabled') folder.add(ex.Physics, 'bodiesCanSleepByDefault') folder.add(ex.Physics, 'warmStart') -folder.add(ex.Physics.debug, 'showColliderBounds') -folder.add(ex.Physics.debug, 'showColliderGeometry') -folder.add(ex.Physics.debug, 'showColliderNormals') -folder.add(ex.Physics.debug, 'showContacts') -folder.add(ex.Physics.debug, 'showNormals') -folder.add(ex.Physics.debug, 'showSleepMotion') -folder.add(ex.Physics.debug, 'showMotionVectors') -folder.add(ex.Physics.debug, 'broadphaseDebug') folder.add(ex.Physics, 'sleepEpsilon', 0.01, 2, .05); folder.add(ex.Physics, 'wakeThreshold', 0.01, 2, .05); folder.add(ex.Physics, 'positionIterations', 0, 30, 1); diff --git a/sandbox/tests/physics/physics2.ts b/sandbox/tests/physics/physics2.ts index 20ce1cca3e..4da7b6bc81 100644 --- a/sandbox/tests/physics/physics2.ts +++ b/sandbox/tests/physics/physics2.ts @@ -1,5 +1,6 @@ /// +ex.Physics.useRealisticPhysics(); var game = new ex.Engine({ width: 600, height: 400 @@ -7,8 +8,7 @@ var game = new ex.Engine({ game.showDebug(true); -ex.Physics.collisionResolutionStrategy = ex.CollisionResolutionStrategy.Realistic; -ex.Physics.debug.broadphaseDebug = true; +game.debug.physics.showBroadphaseSpacePartitionDebug = true; game.currentScene.camera.x = 0; game.currentScene.camera.y = 0; diff --git a/sandbox/tests/trigger/trigger.ts b/sandbox/tests/trigger/trigger.ts index d0b11f025d..dba1048872 100644 --- a/sandbox/tests/trigger/trigger.ts +++ b/sandbox/tests/trigger/trigger.ts @@ -5,8 +5,8 @@ var game = new ex.Engine({ height: 400 }); -ex.Physics.debug.showColliderGeometry = true; -ex.Physics.debug.showColliderBounds = true; +engine.debug.collider.showGeometry = true; +engine.debug.collider.showBounds = true; game.showDebug(true); diff --git a/src/engine/Collision/CollisionSystem.ts b/src/engine/Collision/CollisionSystem.ts index bf623f56d2..e8dbd09071 100644 --- a/src/engine/Collision/CollisionSystem.ts +++ b/src/engine/Collision/CollisionSystem.ts @@ -1,14 +1,9 @@ -import { Camera } from '../Camera'; -import { Color } from '../Color'; import { Entity } from '../EntityComponentSystem'; import { MotionComponent } from '../EntityComponentSystem/Components/MotionComponent'; import { TransformComponent } from '../EntityComponentSystem/Components/TransformComponent'; import { AddedEntity, isAddedSystemEntity, RemovedEntity, System, SystemType } from '../EntityComponentSystem/System'; import { CollisionEndEvent, CollisionStartEvent, ContactEndEvent, ContactStartEvent } from '../Events'; import { CollisionResolutionStrategy, Physics } from './Physics'; -import { Scene } from '../Scene'; -import { DrawUtil } from '../Util/Index'; -// import { BodyComponent } from './BodyComponent'; import { ArcadeSolver } from './Solver/ArcadeSolver'; import { Collider } from './Shapes/Collider'; import { CollisionContact } from './Detection/CollisionContact'; @@ -17,12 +12,14 @@ import { RealisticSolver } from './Solver/RealisticSolver'; import { CollisionSolver } from './Solver/Solver'; import { ColliderComponent } from './ColliderComponent'; import { CompositeCollider } from './Shapes/CompositeCollider'; +import { Engine, ExcaliburGraphicsContext, Scene } from '..'; export class CollisionSystem extends System { public readonly types = ['ex.transform', 'ex.motion', 'ex.collider'] as const; public systemType = SystemType.Update; public priority = -1; + private _engine: Engine; private _realisticSolver = new RealisticSolver(); private _arcadeSolver = new ArcadeSolver(); private _processor = new DynamicTreeCollisionProcessor(); @@ -32,9 +29,6 @@ export class CollisionSystem extends System this._processor.track(c); private _untrackCollider = (c: Collider) => this._processor.untrack(c); - // Ctx and camera are used for the debug draw - private _camera: Camera; - notify(message: AddedEntity | RemovedEntity) { if (isAddedSystemEntity(message)) { const colliderComponent = message.data.get(ColliderComponent); @@ -52,7 +46,7 @@ export class CollisionSystem extends System { - DrawUtil.point(ctx, Color.Red, p); - }); - } - if (Physics.debug.showCollisionNormals) { - contact.points.forEach((p) => { - DrawUtil.vector(ctx, Color.Cyan, p, contact.normal, 30); - }); - } - } - } - ctx.restore(); + debug(ex: ExcaliburGraphicsContext) { + this._processor.debug(ex); } public runContactStartEnd() { diff --git a/src/engine/Collision/Detection/CollisionProcessor.ts b/src/engine/Collision/Detection/CollisionProcessor.ts index 867465ef26..1186b9711f 100644 --- a/src/engine/Collision/Detection/CollisionProcessor.ts +++ b/src/engine/Collision/Detection/CollisionProcessor.ts @@ -2,6 +2,7 @@ import { Pair } from './Pair'; import { Collider } from '../Shapes/Collider'; import { CollisionContact } from './CollisionContact'; +import { ExcaliburGraphicsContext } from '../..'; /** * Definition for collision processor @@ -27,5 +28,5 @@ export interface CollisionProcessor { /** * Draw any debug information */ - debugDraw(ctx: CanvasRenderingContext2D, delta: number): void; + debug(ex: ExcaliburGraphicsContext, delta: number): void; } diff --git a/src/engine/Collision/Detection/DynamicTree.ts b/src/engine/Collision/Detection/DynamicTree.ts index bf5a9ff08d..660e6abca7 100644 --- a/src/engine/Collision/Detection/DynamicTree.ts +++ b/src/engine/Collision/Detection/DynamicTree.ts @@ -6,6 +6,7 @@ import { Logger } from '../../Util/Log'; import { Id } from '../../Id'; import { Entity } from '../../EntityComponentSystem/Entity'; import { BodyComponent } from '../BodyComponent'; +import { Color, ExcaliburGraphicsContext } from '../..'; /** * Dynamic Tree Node used for tracking bounds within the tree @@ -474,18 +475,15 @@ export class DynamicTree> { return helper(this.root); } - public debugDraw(ctx: CanvasRenderingContext2D) { + public debug(ex: ExcaliburGraphicsContext) { // draw all the nodes in the Dynamic Tree const helper = (currentNode: TreeNode) => { if (currentNode) { if (currentNode.isLeaf()) { - ctx.lineWidth = 1; - ctx.strokeStyle = 'green'; + currentNode.bounds.draw(ex, Color.Green); } else { - ctx.lineWidth = 1; - ctx.strokeStyle = 'white'; + currentNode.bounds.draw(ex, Color.White); } - currentNode.bounds.debugDraw(ctx); if (currentNode.left) { helper(currentNode.left); diff --git a/src/engine/Collision/Detection/DynamicTreeCollisionProcessor.ts b/src/engine/Collision/Detection/DynamicTreeCollisionProcessor.ts index 084eede1b7..d0e6c46f94 100644 --- a/src/engine/Collision/Detection/DynamicTreeCollisionProcessor.ts +++ b/src/engine/Collision/Detection/DynamicTreeCollisionProcessor.ts @@ -10,11 +10,9 @@ import { Logger } from '../../Util/Log'; import { CollisionType } from '../CollisionType'; import { Collider } from '../Shapes/Collider'; import { CollisionContact } from '../Detection/CollisionContact'; -import { Color } from '../../Color'; -import { ConvexPolygon } from '../Shapes/ConvexPolygon'; -import { DrawUtil } from '../../Util/Index'; import { BodyComponent } from '../BodyComponent'; import { CompositeCollider } from '../Shapes/CompositeCollider'; +import { ExcaliburGraphicsContext } from '../..'; /** * Responsible for performing the collision broadphase (locating potential colllisions) and @@ -216,9 +214,12 @@ export class DynamicTreeCollisionProcessor implements CollisionProcessor { public narrowphase(pairs: Pair[], stats?: FrameStats): CollisionContact[] { let contacts: CollisionContact[] = []; for (let i = 0; i < pairs.length; i++) { - contacts = contacts.concat(pairs[i].collide()); - if (stats && contacts.length > 0) { - stats.physics.collidersHash[pairs[i].id] = pairs[i]; + const newContacts = pairs[i].collide(); + contacts = contacts.concat(newContacts); + if (stats && newContacts.length > 0) { + for (const c of newContacts) { + stats.physics.contacts.set(c.id, c); + } } } if (stats) { @@ -242,34 +243,7 @@ export class DynamicTreeCollisionProcessor implements CollisionProcessor { return updated; } - /* istanbul ignore next */ - public debugDraw(ctx: CanvasRenderingContext2D) { - if (Physics.debug.broadphaseDebug) { - this._dynamicCollisionTree.debugDraw(ctx); - } - - if (Physics.debug.showColliderGeometry) { - for (const collider of this._colliders) { - const body = collider.owner.get(BodyComponent); - if (Physics.debug.showColliderBounds) { - collider.bounds.debugDraw(ctx, Color.Yellow); - } - - if (Physics.debug.showColliderGeometry) { - let color = Color.Green; - if (body.sleeping || body.collisionType === CollisionType.Fixed) { - color = Color.Gray; - } - collider.debugDraw(ctx, color); - } - - if (Physics.debug.showColliderNormals && collider instanceof ConvexPolygon) { - for (const side of collider.getSides()) { - DrawUtil.point(ctx, Color.Blue, side.midpoint); - DrawUtil.vector(ctx, Color.Yellow, side.midpoint, side.normal(), 30); - } - } - } - } + public debug(ex: ExcaliburGraphicsContext) { + this._dynamicCollisionTree.debug(ex); } } diff --git a/src/engine/Collision/Physics.ts b/src/engine/Collision/Physics.ts index 2f9321493e..fdf0270831 100644 --- a/src/engine/Collision/Physics.ts +++ b/src/engine/Collision/Physics.ts @@ -1,43 +1,6 @@ import { Vector } from '../Math/vector'; import { obsolete } from '../Util/Decorators'; -export class PhysicsDebug { - /** - * Globally switches the debug information for the broadphase strategy - */ - public static broadphaseDebug: boolean = false; - /** - * Show the normals as a result of collision on the screen. - */ - public static showCollisionNormals: boolean = false; - /** - * Show the position, velocity, and acceleration as graphical vectors. - */ - public static showMotionVectors: boolean = false; - - /** - * Show the amount of motion a body has accumulated - */ - public static showSleepMotion: boolean = false; - /** - * Show the axis-aligned bounding boxes of the collision bodies on the screen. - */ - public static showColliderBounds: boolean = false; - /** - * Show the bounding collision area shapes - */ - public static showColliderGeometry: boolean = false; - - public static showColliderNormals: boolean = false; - /** - * Show points of collision interpreted by excalibur as a result of collision. - */ - public static showContacts: boolean = false; - /** - * Show the surface normals of the collision areas. - */ - public static showNormals: boolean = false; -} /** * Possible collision resolution strategies @@ -95,11 +58,6 @@ export class Physics { */ public static enabled = true; - /** - * Physics debug toggles for debug mode - */ - public static debug = PhysicsDebug; - /** * Gets or sets the broadphase pair identification strategy. * diff --git a/src/engine/Debug/Debug.ts b/src/engine/Debug/Debug.ts index bcfbac9d85..7a714596b5 100644 --- a/src/engine/Debug/Debug.ts +++ b/src/engine/Debug/Debug.ts @@ -1,7 +1,7 @@ import { DebugFlags, ColorBlindFlags } from './DebugFlags'; -import { Pair } from '../Collision/Detection/Pair'; import { Engine } from '../Engine'; -import { Color } from '..'; +import { Color } from '../Color'; +import { CollisionContact } from '../Collision/Detection/CollisionContact'; /** * Debug stats containing current and previous frame statistics @@ -11,13 +11,6 @@ export interface DebugStats { prevFrame: FrameStats; } -/** - * Hash containing the [[Pair.id]]s of pairs that collided in a frame - */ -export interface CollidersHash { - [pairId: string]: Pair; -} - /** * Represents a frame's individual statistics */ @@ -123,9 +116,9 @@ export interface PhysicsStatistics { collisions: number; /** - * A Hash storing the [[Pair.id]]s of [[Pair]]s that collided in the frame + * Copy of the current frame contacts (only updated if debug is toggled on) */ - collidersHash: CollidersHash; + contacts: Map; /** * Gets the number of fast moving bodies using raycast continuous collisions in the scene @@ -231,7 +224,20 @@ export class Debug implements DebugFlags { showOwner: false, showGeometry: true, - geometryColor: Color.Green + geometryColor: Color.Green, + }; + + public physics = { + showAll: false, + + showBroadphaseSpacePartitionDebug: false, + + showCollisionNormals: false, + collisionNormalColor: Color.Cyan, + + showCollisionContacts: true, + collisionContactColor: Color.Red + }; public motion = { @@ -412,7 +418,7 @@ export class FrameStats implements FrameStatistics { export class PhysicsStats implements PhysicsStatistics { private _pairs: number = 0; private _collisions: number = 0; - private _collidersHash: CollidersHash = {}; + private _contacts: Map = new Map(); private _fastBodies: number = 0; private _fastBodyCollisions: number = 0; private _broadphase: number = 0; @@ -427,7 +433,7 @@ export class PhysicsStats implements PhysicsStatistics { if (otherStats) { this.pairs = otherStats.pairs; this.collisions = otherStats.collisions; - this.collidersHash = otherStats.collidersHash; + this.contacts = otherStats.contacts; this.fastBodies = otherStats.fastBodies; this.fastBodyCollisions = otherStats.fastBodyCollisions; this.broadphase = otherStats.broadphase; @@ -435,7 +441,7 @@ export class PhysicsStats implements PhysicsStatistics { } else { this.pairs = this.collisions = this.fastBodies = 0; this.fastBodyCollisions = this.broadphase = this.narrowphase = 0; - this.collidersHash = {}; + this.contacts.clear(); } } @@ -466,12 +472,12 @@ export class PhysicsStats implements PhysicsStatistics { this._collisions = value; } - public get collidersHash(): CollidersHash { - return this._collidersHash; + public get contacts(): Map { + return this._contacts; } - public set collidersHash(colliders: CollidersHash) { - this._collidersHash = colliders; + public set contacts(contacts: Map) { + this._contacts = contacts; } public get fastBodies(): number { diff --git a/src/engine/Debug/DebugSystem.ts b/src/engine/Debug/DebugSystem.ts index 042339bd65..23a1ff1db0 100644 --- a/src/engine/Debug/DebugSystem.ts +++ b/src/engine/Debug/DebugSystem.ts @@ -7,13 +7,14 @@ import { CoordPlane, Entity, TransformComponent } from '../EntityComponentSystem import { System, SystemType } from '../EntityComponentSystem/System'; import { ExcaliburGraphicsContext } from '../Graphics/Context/ExcaliburGraphicsContext'; import { vec, Vector } from '../Math/vector'; -import { BodyComponent, CompositeCollider, GraphicsComponent, Particle, Util } from '..'; +import { BodyComponent, CollisionSystem, Color, CompositeCollider, GraphicsComponent, Particle, Util } from '..'; export class DebugSystem extends System { public readonly types = ['ex.transform'] as const; public readonly systemType = SystemType.Draw; public priority = 999; // lowest priority private _graphicsContext: ExcaliburGraphicsContext; + private _collisionSystem: CollisionSystem; private _camera: Camera; private _engine: Engine; @@ -21,6 +22,7 @@ export class DebugSystem extends System { this._graphicsContext = scene.engine.graphicsContext; this._camera = scene.camera; this._engine = scene.engine; + this._collisionSystem = scene.world.systemManager.get(CollisionSystem); } update(entities: Entity[], _delta: number): void { if (!this._engine.isDebug) { @@ -30,7 +32,6 @@ export class DebugSystem extends System { const filterSettings = this._engine.debug.filter; let id: number; - // let killed: boolean; let name: string; const entitySettings = this._engine.debug.entity; @@ -43,6 +44,8 @@ export class DebugSystem extends System { let collider: ColliderComponent; const colliderSettings = this._engine.debug.collider; + const physicsSettings = this._engine.debug.physics; + let graphics: GraphicsComponent; const graphicsSettings = this._engine.debug.graphics; @@ -200,6 +203,27 @@ export class DebugSystem extends System { this._popCameraTransform(tx); } + this._graphicsContext.save(); + this._camera.draw(this._graphicsContext); + if (physicsSettings.showAll || physicsSettings.showBroadphaseSpacePartitionDebug) { + this._collisionSystem.debug(this._graphicsContext); + } + if (physicsSettings.showAll || physicsSettings.showCollisionContacts || physicsSettings.showCollisionNormals) { + for (const [_, contact] of this._engine.debug.stats.currFrame.physics.contacts) { + if (physicsSettings.showAll || physicsSettings.showCollisionContacts) { + for (const point of contact.points) { + this._graphicsContext.debug.drawPoint(point, { size: 5, color: physicsSettings.collisionContactColor}); + } + } + if (physicsSettings.showAll || physicsSettings.showCollisionNormals) { + for (const point of contact.points) { + this._graphicsContext.debug.drawLine(point, contact.normal.scale(30).add(point), { color: physicsSettings.collisionNormalColor }); + }; + } + } + } + this._graphicsContext.restore(); + if (cameraSettings) { this._graphicsContext.save(); this._camera.draw(this._graphicsContext); diff --git a/src/engine/EntityComponentSystem/System.ts b/src/engine/EntityComponentSystem/System.ts index 90820902c2..3fe69e3787 100644 --- a/src/engine/EntityComponentSystem/System.ts +++ b/src/engine/EntityComponentSystem/System.ts @@ -86,11 +86,6 @@ implements Observer { */ postupdate?(engine: ContextType, elapsedMs: number): void; - /** - * Optionally run a debug draw step to visualize the internals of the system - */ - debugDraw?(ctx: CanvasRenderingContext2D, elapsedMs: number): void; - /** * Systems observe when entities match their types or no longer match their types, override * @param _entityAddedOrRemoved diff --git a/src/engine/EntityComponentSystem/SystemManager.ts b/src/engine/EntityComponentSystem/SystemManager.ts index 3a71f28106..373e8a1140 100644 --- a/src/engine/EntityComponentSystem/SystemManager.ts +++ b/src/engine/EntityComponentSystem/SystemManager.ts @@ -2,6 +2,10 @@ import { System, SystemType } from './System'; import { Scene, Util } from '..'; import { World } from './World'; +export interface SystemCtor { + new (): T; +} + /** * The SystemManager is responsible for keeping track of all systems in a scene. * Systems are scene specific @@ -15,6 +19,10 @@ export class SystemManager { public initialized = false; constructor(private _world: World) {} + public get(systemType: SystemCtor): T | null { + return this.systems.find(s => s instanceof systemType) as unknown as T + } + /** * Adds a system to the manager, it will now be updated every frame * @param system diff --git a/src/engine/Scene.ts b/src/engine/Scene.ts index db710823e9..a139a66ae3 100644 --- a/src/engine/Scene.ts +++ b/src/engine/Scene.ts @@ -377,16 +377,12 @@ export class Scene extends Class implements CanInitialize, CanActivate, CanDeact /** * Draws all the actors' debug information in the Scene. Called by the [[Engine]]. * @param ctx The current rendering context + * @deprecated */ /* istanbul ignore next */ public debugDraw(ctx: CanvasRenderingContext2D) { this.emit('predebugdraw', new PreDebugDrawEvent(ctx, this)); - // this._collisionProcessor.debugDraw(ctx, 20); - for (const system of this.world.systemManager.systems) { - if (system.debugDraw) { - system.debugDraw(ctx, 1); - } - } + // pass this.emit('postdebugdraw', new PostDebugDrawEvent(ctx, this)); } From 3b874dbf7a5da5c7b81ddf422a9662da16d3cdad Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Fri, 10 Sep 2021 21:30:29 -0500 Subject: [PATCH 15/18] Fix lint --- src/engine/Collision/CollisionSystem.ts | 2 +- src/engine/Debug/Debug.ts | 5 ++--- src/engine/Debug/DebugSystem.ts | 10 +++++++--- src/engine/EntityComponentSystem/SystemManager.ts | 7 ++++++- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/engine/Collision/CollisionSystem.ts b/src/engine/Collision/CollisionSystem.ts index e8dbd09071..c91bf1c814 100644 --- a/src/engine/Collision/CollisionSystem.ts +++ b/src/engine/Collision/CollisionSystem.ts @@ -19,7 +19,7 @@ export class CollisionSystem extends System { this._engine = scene.engine; this._collisionSystem = scene.world.systemManager.get(CollisionSystem); } + update(entities: Entity[], _delta: number): void { if (!this._engine.isDebug) { return; @@ -212,13 +213,16 @@ export class DebugSystem extends System { for (const [_, contact] of this._engine.debug.stats.currFrame.physics.contacts) { if (physicsSettings.showAll || physicsSettings.showCollisionContacts) { for (const point of contact.points) { - this._graphicsContext.debug.drawPoint(point, { size: 5, color: physicsSettings.collisionContactColor}); + this._graphicsContext.debug.drawPoint(point, { size: 5, color: physicsSettings.collisionContactColor }); } } + if (physicsSettings.showAll || physicsSettings.showCollisionNormals) { for (const point of contact.points) { - this._graphicsContext.debug.drawLine(point, contact.normal.scale(30).add(point), { color: physicsSettings.collisionNormalColor }); - }; + this._graphicsContext.debug.drawLine(point, contact.normal.scale(30).add(point), { + color: physicsSettings.collisionNormalColor + }); + } } } } diff --git a/src/engine/EntityComponentSystem/SystemManager.ts b/src/engine/EntityComponentSystem/SystemManager.ts index 373e8a1140..27bf993bc3 100644 --- a/src/engine/EntityComponentSystem/SystemManager.ts +++ b/src/engine/EntityComponentSystem/SystemManager.ts @@ -19,8 +19,13 @@ export class SystemManager { public initialized = false; constructor(private _world: World) {} + /** + * Get a system registered in the manager by type + * @param systemType + * @returns + */ public get(systemType: SystemCtor): T | null { - return this.systems.find(s => s instanceof systemType) as unknown as T + return this.systems.find((s) => s instanceof systemType) as unknown as T; } /** From 94547e49e83fafe518a519f7d4cad810d84446c3 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Fri, 10 Sep 2021 21:35:12 -0500 Subject: [PATCH 16/18] Fix test --- src/spec/ActorSpec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/spec/ActorSpec.ts b/src/spec/ActorSpec.ts index d995af8c5e..be078ca105 100644 --- a/src/spec/ActorSpec.ts +++ b/src/spec/ActorSpec.ts @@ -38,6 +38,7 @@ describe('A game actor', () => { spyOn(actor, 'debugDraw'); engine.start(); + collisionSystem.initialize(scene); ex.Physics.useArcadePhysics(); ex.Physics.acc.setTo(0, 0); From c7ead3ab40310fe797938eedcb6928d088e7d978 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Sat, 11 Sep 2021 08:50:12 -0500 Subject: [PATCH 17/18] Remove unused import --- src/engine/Debug/DebugSystem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/Debug/DebugSystem.ts b/src/engine/Debug/DebugSystem.ts index 72fb391b13..c418febb1d 100644 --- a/src/engine/Debug/DebugSystem.ts +++ b/src/engine/Debug/DebugSystem.ts @@ -7,7 +7,7 @@ import { CoordPlane, Entity, TransformComponent } from '../EntityComponentSystem import { System, SystemType } from '../EntityComponentSystem/System'; import { ExcaliburGraphicsContext } from '../Graphics/Context/ExcaliburGraphicsContext'; import { vec, Vector } from '../Math/vector'; -import { BodyComponent, CollisionSystem, Color, CompositeCollider, GraphicsComponent, Particle, Util } from '..'; +import { BodyComponent, CollisionSystem, CompositeCollider, GraphicsComponent, Particle, Util } from '..'; export class DebugSystem extends System { public readonly types = ['ex.transform'] as const; From 9bd6073e9439b9acb04c6d80c2318eed7c19d242 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Sat, 11 Sep 2021 12:04:35 -0500 Subject: [PATCH 18/18] Update changelog + docs --- CHANGELOG.md | 8 ++++- src/engine/Debug/Debug.ts | 36 +++++++++++++++++++++++ src/engine/Graphics/Context/debug-text.ts | 9 ++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a69cfc1c6..eec8b6a30a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Breaking Changes +- `ex.Physics.debug` properties for Debug drawing are now moved to `engine.debug.physics`, `engine.debug.collider`, and `engine.debug.body`. + - Old `debugDraw(ctx: CanvasRenderingContext2D)` methods are removed. - Collision `Pair`'s are now between Collider's and not bodies - `PerlinNoise` has been removed from the core repo will now be offered as a [plugin](https://github.com/excaliburjs/excalibur-perlin) - Legacy drawing implementations are moved behind `ex.LegacyDrawing` new Graphics implemenations of `Sprite`, `SpriteSheet`, `Animation` are now the default import. @@ -36,7 +38,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Removes `Entity.components` as a way to access, add, and remove components - Camera.z has been renamed to property `zoom` which is the zoom factor - Camera.zoom(...) has been renamed to function `zoomOverTime()` -- TileMap no longer needs registered SpriteSheets, `Sprite`'s can be added directly to `Cell`'s with `addSprite` +- TileMap no longer needs registered SpriteSheets, `Sprite`'s can be added directly to `Cell`'s with `addGraphic` - The confusing `TileSprite` type is removed (Related to TileMap plugin updates https://github.com/excaliburjs/excalibur-tiled/issues/4, https://github.com/excaliburjs/excalibur-tiled/issues/23, https://github.com/excaliburjs/excalibur-tiled/issues/108) - Directly changing debug drawing by `engine.isDebug = value` has been replaced by `engine.showDebug(value)` and `engine.toggleDebug()` ([#1655](https://github.com/excaliburjs/Excalibur/issues/1655)) - `UIActor` Class instances need to be replaced to `ScreenElement` (This Class it's marked as Obsolete) ([#1656](https://github.com/excaliburjs/Excalibur/issues/1656)) @@ -55,6 +57,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +- The `ExcaliburGraphicsContext` now supports drawing debug text +- `Entity` may also now optionally have a `name`, this is useful for finding entities by name or when displaying in debug mode. +- New `DebugSystem` ECS system will show debug drawing output for things toggled on/off in the `engine.debug` section, this allows for a less cluttered debug experience. + - Each debug section now has a configurable color. - Turn on WebGL support with `ex.Flags.useWebGL()` - Added new helpers to `CollisionGroup` to define groups that collide with specified groups `CollisionGroup.collidesWith([groupA, groupB])` - Combine groups with `const groupAandB = CollisionGroup.combine([groupA, groupB])` diff --git a/src/engine/Debug/Debug.ts b/src/engine/Debug/Debug.ts index c74d11d826..a3d287c06e 100644 --- a/src/engine/Debug/Debug.ts +++ b/src/engine/Debug/Debug.ts @@ -183,18 +183,36 @@ export class Debug implements DebugFlags { */ public colorBlindMode: ColorBlindFlags; + /** + * Filter debug context to named entities or entity ids + */ public filter: { useFilter: boolean; nameQuery: string; ids: number[] } = { + /** + * Toggle filter on or off (default off) must be on for DebugDraw to use filters + */ useFilter: false, + /** + * Query for entities by name, if the entity name contains `nameQuery` it will be included + */ nameQuery: '', + /** + * Query for Entity ids, if the id matches it will be included + */ ids: [] }; + /** + * Entity debug settings + */ public entity = { showAll: false, showId: true, showName: false }; + /** + * Transform component debug settings + */ public transform = { showAll: false, @@ -208,6 +226,9 @@ export class Debug implements DebugFlags { rotationColor: Color.Blue }; + /** + * Graphics component debug settings + */ public graphics = { showAll: false, @@ -215,6 +236,9 @@ export class Debug implements DebugFlags { boundsColor: Color.Yellow }; + /** + * Collider component debug settings + */ public collider = { showAll: false, @@ -227,6 +251,9 @@ export class Debug implements DebugFlags { geometryColor: Color.Green }; + /** + * Physics simulation debug settings + */ public physics = { showAll: false, @@ -239,6 +266,9 @@ export class Debug implements DebugFlags { collisionContactColor: Color.Red }; + /** + * Motion component debug settings + */ public motion = { showAll: false, @@ -249,6 +279,9 @@ export class Debug implements DebugFlags { accelerationColor: Color.Red }; + /** + * Body component debug settings + */ public body = { showAll: false, @@ -259,6 +292,9 @@ export class Debug implements DebugFlags { showMass: false }; + /** + * Camera debug settings + */ public camera = { showAll: false, diff --git a/src/engine/Graphics/Context/debug-text.ts b/src/engine/Graphics/Context/debug-text.ts index 802104af6e..1ed05279d9 100644 --- a/src/engine/Graphics/Context/debug-text.ts +++ b/src/engine/Graphics/Context/debug-text.ts @@ -2,6 +2,9 @@ import { ExcaliburGraphicsContext, ImageSource, SpriteFont, SpriteSheet } from ' import { Vector } from '../..'; import debugFont from './debug-font.png'; +/** + * Internal debugtext helper + */ export class DebugText { constructor() { this.load(); @@ -36,6 +39,12 @@ export class DebugText { }); } + /** + * Writes debug text using the built in sprint font + * @param ctx + * @param text + * @param pos + */ public write(ctx: ExcaliburGraphicsContext, text: string, pos: Vector) { if (this._imageSource.isLoaded()) { this._spriteFont.render(ctx, text, pos.x, pos.y);