diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cd125801..f9beb597f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,15 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Added new `ex.Debug` static for more convenient debug drawing where you might not have a graphics context accessible to you. This works by batching up all the debug draw requests and flushing them during the debug draw step. + * `ex.Debug.drawRay(ray: Ray, options?: { distance?: number, color?: Color })` + * `ex.Debug.drawBounds(boundingBox: BoundingBox, options?: { color?: Color })` + * `ex.Debug.drawCircle(center: Vector, radius: number, options?: ...)` + * `ex.Debug.drawPolygon(points: Vector[], options?: { color?: Color })` + * `ex.Debug.drawText(text: string, pos: Vector)` + * `ex.Debug.drawLine(start: Vector, end: Vector, options?: LineGraphicsOptions)` + * `ex.Debug.drawLines(points: Vector[], options?: LineGraphicsOptions)` + * `drawPoint(point: Vector, options?: PointGraphicsOptions)` - Experimental `ex.coroutine` for running code that changes over time, useful for modeling complex animation code. Coroutines return a promise when they are complete. You can think of each `yield` as a frame. * The result of a yield is the current elapsed time * You can yield a number in milliseconds and it will wait that long before resuming diff --git a/sandbox/src/game.ts b/sandbox/src/game.ts index e38165173..74e02a454 100644 --- a/sandbox/src/game.ts +++ b/sandbox/src/game.ts @@ -53,9 +53,17 @@ var game = new ex.Engine({ pointerScope: ex.PointerScope.Canvas, displayMode: ex.DisplayMode.FitScreenAndZoom, snapToPixel: false, + // fixedUpdateFps: 30, + pixelRatio: 2, fixedUpdateFps: 60, maxFps: 60, - antialiasing: false, + antialiasing: { + pixelArtSampler: true, + canvasImageRendering: 'auto', + nativeContextAntialiasing: false, + filtering: ex.ImageFiltering.Pixel, + multiSampleAntialiasing: true, + }, uvPadding: 0, physics: { colliders: { @@ -555,9 +563,9 @@ player.onPostUpdate = (engine) => { }); // console.log(hits); } -player.graphics.onPostDraw = (ctx) => { - ctx.drawLine(ex.Vector.Zero, ex.Vector.Down.scale(100), ex.Color.Red, 2); -} +// player.graphics.onPostDraw = (ctx) => { +// ctx.drawLine(ex.Vector.Zero, ex.Vector.Down.scale(100), ex.Color.Red, 2); +// } player.body.canSleep = false; player.graphics.copyGraphics = false; follower.actions @@ -594,6 +602,23 @@ var healthbar = new ex.Actor({ height: 5, color: new ex.Color(0, 255, 0)}); player.addChild(healthbar); +player.onPostUpdate = () => { + ex.Debug.drawLine( + player.pos, + player.pos.add(ex.Vector.Down.scale(100)), { + color: ex.Color.Red + }); + ex.Debug.drawPoint(player.pos, { + size: 1, + color: ex.Color.Violet + }); + ex.Debug.drawCircle(player.pos, 100, { + color: ex.Color.Transparent, + strokeColor: ex.Color.Black, + width: 1 + }); + ex.Debug.drawBounds(player.collider.bounds, { color: ex.Color.Yellow }); +} // player.onPostDraw = (ctx: CanvasRenderingContext2D) => { // ctx.fillStyle = 'red'; // ctx.fillRect(0, 0, 100, 100); diff --git a/sandbox/tests/polygon/index.ts b/sandbox/tests/polygon/index.ts index 854e3ccdf..496ed701b 100644 --- a/sandbox/tests/polygon/index.ts +++ b/sandbox/tests/polygon/index.ts @@ -91,5 +91,5 @@ var triangulated = shape.triangulate(); actor.collider.set(triangulated); game.add(actor); - +game.currentScene.camera.zoom = .75; game.start(); \ No newline at end of file diff --git a/src/engine/Collision/BoundingBox.ts b/src/engine/Collision/BoundingBox.ts index a673b1787..6e711442a 100644 --- a/src/engine/Collision/BoundingBox.ts +++ b/src/engine/Collision/BoundingBox.ts @@ -213,13 +213,34 @@ export class BoundingBox { return 2 * (wx + wy); } + + // Cache bounding box point returns + private _points: Vector[] = []; + private _left?: number; + private _right?: number; + private _top?: number; + private _bottom?: number; + + /** + * Returns the world space points that make up the corners of the bounding box as a polygon + */ public getPoints(): Vector[] { - const results = []; - results.push(new Vector(this.left, this.top)); - results.push(new Vector(this.right, this.top)); - results.push(new Vector(this.right, this.bottom)); - results.push(new Vector(this.left, this.bottom)); - return results; + if (this._left !== this.left || + this._right !== this.right || + this._top !== this.top || + this._bottom !== this.bottom + ) { + this._points.length = 0; + this._points.push(new Vector(this.left, this.top)); + this._points.push(new Vector(this.right, this.top)); + this._points.push(new Vector(this.right, this.bottom)); + this._points.push(new Vector(this.left, this.bottom)); + this._left = this.left; + this._right = this.right; + this._top = this.top; + this._bottom = this.bottom; + } + return this._points; } /** diff --git a/src/engine/Collision/Colliders/PolygonCollider.ts b/src/engine/Collision/Colliders/PolygonCollider.ts index 09bb4d838..d235f9302 100644 --- a/src/engine/Collision/Colliders/PolygonCollider.ts +++ b/src/engine/Collision/Colliders/PolygonCollider.ts @@ -11,7 +11,7 @@ import { AffineMatrix } from '../../Math/affine-matrix'; import { Ray } from '../../Math/ray'; import { ClosestLineJumpTable } from './ClosestLineJumpTable'; import { Collider } from './Collider'; -import { BodyComponent, ExcaliburGraphicsContext, Logger } from '../..'; +import { BodyComponent, Debug, ExcaliburGraphicsContext, Logger } from '../..'; import { CompositeCollider } from './CompositeCollider'; import { Shape } from './Shape'; import { Transform } from '../../Math/transform'; @@ -46,6 +46,7 @@ export class PolygonCollider extends Collider { public flagDirty() { this._localBoundsDirty = true; this._localSidesDirty = true; + this._transformedPointsDirty = true; this._sidesDirty = true; } @@ -701,13 +702,7 @@ export class PolygonCollider extends Collider { } public debug(ex: ExcaliburGraphicsContext, color: Color, options?: { lineWidth: number, pointSize: number }) { - const firstPoint = this.getTransformedPoints()[0]; - const points = [firstPoint, ...this.getTransformedPoints(), firstPoint]; - const { lineWidth, pointSize } = { ...{ lineWidth: 1, pointSize: 1 }, ...options }; - for (let i = 0; i < points.length - 1; i++) { - ex.drawLine(points[i], points[i + 1], color, lineWidth); - ex.drawCircle(points[i], pointSize, color); - ex.drawCircle(points[i + 1], pointSize, color); - } + const points = this.getTransformedPoints(); + Debug.drawPolygon(points, { color }); } } diff --git a/src/engine/Debug/Debug.ts b/src/engine/Debug/DebugConfig.ts similarity index 99% rename from src/engine/Debug/Debug.ts rename to src/engine/Debug/DebugConfig.ts index eddf3742d..b2bf7ec31 100644 --- a/src/engine/Debug/Debug.ts +++ b/src/engine/Debug/DebugConfig.ts @@ -1,4 +1,4 @@ -import { DebugFlags, ColorBlindFlags } from './DebugFlags'; +import { ColorBlindFlags } from './DebugFlags'; import { Engine } from '../Engine'; import { Color } from '../Color'; import { CollisionContact } from '../Collision/Detection/CollisionContact'; @@ -152,7 +152,7 @@ export interface GraphicsStatistics { * best to do so on the `postupdate` event for [[Engine]], after all values have been * updated during a frame. */ -export class Debug implements DebugFlags { +export class DebugConfig { private _engine: Engine; constructor(engine: Engine) { @@ -309,6 +309,7 @@ export class Debug implements DebugFlags { collisionNormalColor: Color.Cyan, showCollisionContacts: true, + contactSize: 2, collisionContactColor: Color.Red }; diff --git a/src/engine/Debug/DebugFlags.ts b/src/engine/Debug/DebugFlags.ts index c83031bf6..bc4d32952 100644 --- a/src/engine/Debug/DebugFlags.ts +++ b/src/engine/Debug/DebugFlags.ts @@ -3,10 +3,6 @@ import { ColorBlindnessPostProcessor } from '../Graphics/PostProcessor/ColorBlin import { Engine } from '../Engine'; import { ExcaliburGraphicsContextWebGL } from '../Graphics/Context/ExcaliburGraphicsContextWebGL'; -export interface DebugFlags { - colorBlindMode: ColorBlindFlags; -} - export class ColorBlindFlags { private _engine: Engine; private _colorBlindPostProcessor: ColorBlindnessPostProcessor; diff --git a/src/engine/Debug/DebugSystem.ts b/src/engine/Debug/DebugSystem.ts index c87a809f1..2462f595a 100644 --- a/src/engine/Debug/DebugSystem.ts +++ b/src/engine/Debug/DebugSystem.ts @@ -15,6 +15,7 @@ import { GraphicsComponent } from '../Graphics/GraphicsComponent'; import { Particle } from '../Particles'; import { DebugGraphicsComponent } from '../Graphics/DebugGraphicsComponent'; import { CoordPlane } from '../Math/coord-plane'; +import { Debug } from '../Graphics/Debug'; export class DebugSystem extends System { public readonly systemType = SystemType.Draw; @@ -260,7 +261,10 @@ 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: physicsSettings.contactSize, + color: physicsSettings.collisionContactColor + }); } } @@ -290,6 +294,15 @@ export class DebugSystem extends System { this._graphicsContext.flush(); } + postupdate(engine: Scene, elapsedMs: number): void { + this._graphicsContext.save(); + if (this._camera) { + this._camera.draw(this._graphicsContext); + } + Debug.flush(this._graphicsContext); + this._graphicsContext.restore(); + } + /** * This applies the current entity transform to the graphics context * @param entity diff --git a/src/engine/Debug/index.ts b/src/engine/Debug/index.ts index 51da2d076..fa4a1ec20 100644 --- a/src/engine/Debug/index.ts +++ b/src/engine/Debug/index.ts @@ -1,3 +1,3 @@ -export * from './Debug'; +export * from './DebugConfig'; export * from './DebugFlags'; export * from './DebugSystem'; diff --git a/src/engine/Engine.ts b/src/engine/Engine.ts index fe6ac79c5..c6b6776ae 100644 --- a/src/engine/Engine.ts +++ b/src/engine/Engine.ts @@ -32,7 +32,7 @@ import { Logger, LogLevel } from './Util/Log'; import { Color } from './Color'; import { Scene, SceneConstructor, isSceneConstructor } from './Scene'; import { Entity } from './EntityComponentSystem/Entity'; -import { Debug, DebugStats } from './Debug/Debug'; +import { DebugConfig, DebugStats } from './Debug/DebugConfig'; import { BrowserEvents } from './Util/Browser'; import { AntialiasOptions, @@ -505,7 +505,7 @@ export class Engine implements CanInitialize, * * Graphics * * Colliders */ - public debug: Debug; + public debug: DebugConfig; /** * Access [[stats]] that holds frame statistics. @@ -932,7 +932,7 @@ O|===|* >________________>\n\ }; } - this.debug = new Debug(this); + this.debug = new DebugConfig(this); this.director = new Director(this, options.scenes); diff --git a/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts b/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts index a564e24f3..d49af0620 100644 --- a/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts +++ b/src/engine/Graphics/Context/ExcaliburGraphicsContext.ts @@ -130,11 +130,11 @@ export interface ExcaliburGraphicsContextState { material: Material; } export interface LineGraphicsOptions { - color: Color; + color?: Color; } export interface RectGraphicsOptions { - color: Color; + color?: Color; } export interface PointGraphicsOptions { diff --git a/src/engine/Graphics/Context/line-renderer/line-renderer.ts b/src/engine/Graphics/Context/line-renderer/line-renderer.ts index 1b8df8bc2..5efd878c9 100644 --- a/src/engine/Graphics/Context/line-renderer/line-renderer.ts +++ b/src/engine/Graphics/Context/line-renderer/line-renderer.ts @@ -7,6 +7,11 @@ import { RendererPlugin } from '../renderer'; import { Shader, VertexBuffer, VertexLayout } from '../..'; import { GraphicsDiagnostics } from '../../GraphicsDiagnostics'; +export interface LineOptions { + color?: Color; + width?: number; +} + export class LineRenderer implements RendererPlugin { public readonly type = 'ex.line'; public priority: number = 0; diff --git a/src/engine/Graphics/Debug.ts b/src/engine/Graphics/Debug.ts new file mode 100644 index 000000000..a76400e35 --- /dev/null +++ b/src/engine/Graphics/Debug.ts @@ -0,0 +1,107 @@ +import { Vector } from '../Math/vector'; +import { ExcaliburGraphicsContext, LineGraphicsOptions, PointGraphicsOptions } from './Context/ExcaliburGraphicsContext'; +import { Color } from '../Color'; +import { Ray } from '../Math/ray'; +import { BoundingBox } from '../excalibur'; + +export class Debug { + static _drawCalls: ((ctx: ExcaliburGraphicsContext) => void)[] = []; + static _ctx: ExcaliburGraphicsContext; + static z: number = Infinity; + static registerGraphicsContext(ctx: ExcaliburGraphicsContext) { + Debug._ctx = ctx; + } + static draw(debugDrawCall: (ctx: ExcaliburGraphicsContext) => void) { + this._drawCalls.push(debugDrawCall); + } + + static drawPoint(point: Vector, options?: PointGraphicsOptions) { + Debug.draw(ctx => { + ctx.debug.drawPoint(point, options); + }); + } + + static drawLine(start: Vector, end: Vector, options?: LineGraphicsOptions) { + Debug.draw(ctx => { + ctx.debug.drawLine(start, end, options); + }); + } + + static drawLines(points: Vector[], options?: LineGraphicsOptions) { + if (points.length > 1) { + Debug.draw(ctx => { + for (let i = 0; i < points.length - 1; i++) { + ctx.debug.drawLine(points[i], points[i + 1], options); + } + }); + } + } + + static drawText(text: string, pos: Vector) { + Debug.draw(ctx => { + ctx.debug.drawText(text, pos); + }); + } + + static drawPolygon(points: Vector[], options?: { color?: Color }) { + if (points.length > 1) { + Debug.draw(ctx => { + const firstPoint = points[0]; + const polygon = [...points, firstPoint]; + for (let i = 0; i < polygon.length - 1; i++) { + ctx.debug.drawLine(polygon[i], polygon[i + 1], options); + } + }); + } + } + + static drawCircle(center: Vector, radius: number, options?: { + color?: Color, + strokeColor?: Color, + width?: number + }) { + const { color, strokeColor, width} = { + color: Color.Black, + strokeColor: undefined, + width: undefined, + ...options + }; + Debug.draw(ctx => { + ctx.drawCircle(center, radius, color, strokeColor, width); + }); + } + + static drawBounds(boundingBox: BoundingBox, options?: { color?: Color }): void { + Debug.draw(ctx => { + ctx.debug.drawRect(boundingBox.left, boundingBox.top, boundingBox.width, boundingBox.height, options); + }); + } + + static drawRay(ray: Ray, options?: { distance?: number, color?: Color }) { + const { distance, color } = { + color: Color.Blue, + distance: 10, + ...options + }; + Debug.draw((ctx) => { + const start = ray.pos; + const end = ray.pos.add(ray.dir.scale(distance)); + + ctx.debug.drawLine(start, end, { color }); + }); + } + + static flush(ctx: ExcaliburGraphicsContext) { + ctx.save(); + ctx.z = Debug.z; + for (const drawCall of Debug._drawCalls) { + drawCall(ctx); + } + ctx.restore(); + Debug.clear(); + } + + static clear() { + Debug._drawCalls.length = 0; + } +} \ No newline at end of file diff --git a/src/engine/Graphics/DebugGraphicsComponent.ts b/src/engine/Graphics/DebugGraphicsComponent.ts index 247081651..871d6b933 100644 --- a/src/engine/Graphics/DebugGraphicsComponent.ts +++ b/src/engine/Graphics/DebugGraphicsComponent.ts @@ -1,5 +1,5 @@ import { ExcaliburGraphicsContext } from './Context/ExcaliburGraphicsContext'; -import { Debug } from '../Debug'; +import { DebugConfig } from '../Debug'; import { Component } from '../EntityComponentSystem/Component'; @@ -11,7 +11,7 @@ import { Component } from '../EntityComponentSystem/Component'; */ export class DebugGraphicsComponent extends Component { constructor( - public draw: (ctx: ExcaliburGraphicsContext, debugFlags: Debug) => void, + public draw: (ctx: ExcaliburGraphicsContext, debugFlags: DebugConfig) => void, public useTransform = true) { super(); } diff --git a/src/engine/Graphics/index.ts b/src/engine/Graphics/index.ts index 3f36fb480..2a23e1fa0 100644 --- a/src/engine/Graphics/index.ts +++ b/src/engine/Graphics/index.ts @@ -49,6 +49,9 @@ export * from './Context/vertex-layout'; export * from './Context/quad-index-buffer'; export * from './Context/material'; +// Debug +export * from './Debug'; + // Util import * as webgl from './Context/webgl-util'; diff --git a/src/engine/TileMap/IsometricMap.ts b/src/engine/TileMap/IsometricMap.ts index 1a1982346..29ae28b66 100644 --- a/src/engine/TileMap/IsometricMap.ts +++ b/src/engine/TileMap/IsometricMap.ts @@ -9,7 +9,7 @@ import { TransformComponent } from '../EntityComponentSystem/Components/Transfor import { Entity } from '../EntityComponentSystem/Entity'; import { DebugGraphicsComponent, ExcaliburGraphicsContext, Graphic, GraphicsComponent } from '../Graphics'; import { IsometricEntityComponent } from './IsometricEntityComponent'; -import { Debug } from '../Debug'; +import { DebugConfig } from '../Debug'; export class IsometricTile extends Entity { /** * Indicates whether this tile is solid @@ -445,7 +445,7 @@ export class IsometricMap extends Entity { * Debug draw for IsometricMap, called internally by excalibur when debug mode is toggled on * @param gfx */ - public debug(gfx: ExcaliburGraphicsContext, debugFlags: Debug) { + public debug(gfx: ExcaliburGraphicsContext, debugFlags: DebugConfig) { const { showAll, showPosition, diff --git a/src/engine/TileMap/TileMap.ts b/src/engine/TileMap/TileMap.ts index fad270e6a..3852b5760 100644 --- a/src/engine/TileMap/TileMap.ts +++ b/src/engine/TileMap/TileMap.ts @@ -17,7 +17,7 @@ import { PostDrawEvent, PostUpdateEvent, PreDrawEvent, PreUpdateEvent } from '.. import { EventEmitter, EventKey, Handler, Subscription } from '../EventEmitter'; import { CoordPlane } from '../Math/coord-plane'; import { QuadTree } from '../Collision/Detection/QuadTree'; -import { Debug } from '../Debug'; +import { DebugConfig } from '../Debug'; export interface TileMapOptions { /** @@ -539,7 +539,7 @@ export class TileMap extends Entity { this.emit('postdraw', new PostDrawEvent(ctx as any, delta, this)); } - public debug(gfx: ExcaliburGraphicsContext, debugFlags: Debug) { + public debug(gfx: ExcaliburGraphicsContext, debugFlags: DebugConfig) { const { showAll, showGrid, diff --git a/src/spec/DebugSpec.ts b/src/spec/DebugSpec.ts new file mode 100644 index 000000000..1d2ca5fa1 --- /dev/null +++ b/src/spec/DebugSpec.ts @@ -0,0 +1,164 @@ +import * as ex from '@excalibur'; +import { ExcaliburAsyncMatchers } from 'excalibur-jasmine'; + +describe('Debug draw static', () => { + beforeAll(() => { + jasmine.addAsyncMatchers(ExcaliburAsyncMatchers); + }); + it('exists', () => { + expect(ex.Debug).toBeDefined(); + }); + + it('can draw a point', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawPoint(ex.vec(5, 5), {size: 5, color: ex.Color.ExcaliburBlue}); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/point.png'); + }); + + it('can draw a line', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawLine(ex.vec(0, 0), ex.vec(10, 10), {color: ex.Color.ExcaliburBlue}); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/line.png'); + }); + + it('can draw lines', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawLines([ex.vec(0, 0), ex.vec(10, 10), ex.vec(10, 0), ex.vec(0, 10)], {color: ex.Color.ExcaliburBlue}); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/lines.png'); + }); + + xit('can draw text', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 100; + canvas.height = 100; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawText('some text', ex.vec(0, 50)); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/text.png'); + }); + + it('can draw a polygon', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawPolygon([ex.vec(2, 2), ex.vec(8, 2), ex.vec(8, 8), ex.vec(2, 8)], {color: ex.Color.ExcaliburBlue}); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/polygon.png'); + }); + + it('can draw a circle', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + ex.Debug.drawCircle(ex.vec(5, 5), 5, {color: ex.Color.Transparent, strokeColor: ex.Color.ExcaliburBlue, width: 1}); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/circle.png'); + }); + + it('can draw bounds', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + const bounds = new ex.BoundingBox({ + left: 3, + right: 7, + top: 3, + bottom: 7 + }); + + ex.Debug.drawBounds(bounds, { color: ex.Color.ExcaliburBlue }); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/bounds.png'); + }); + + it('can draw a ray', async () => { + const canvas = document.createElement('canvas'); + canvas.width = 10; + canvas.height = 10; + const context = new ex.ExcaliburGraphicsContextWebGL({ + canvasElement: canvas, + backgroundColor: ex.Color.Black + }); + context.clear(); + + const ray = new ex.Ray(ex.vec(0, 0), ex.vec(1, 1)); + + ex.Debug.drawRay(ray, { distance: 4, color: ex.Color.ExcaliburBlue }); + + ex.Debug.flush(context); + context.flush(); + + await expectAsync(canvas).toEqualImage('src/spec/images/DebugSpec/ray.png'); + }); +}); \ No newline at end of file diff --git a/src/spec/images/DebugSpec/bounds.png b/src/spec/images/DebugSpec/bounds.png new file mode 100644 index 000000000..fef363472 Binary files /dev/null and b/src/spec/images/DebugSpec/bounds.png differ diff --git a/src/spec/images/DebugSpec/circle.png b/src/spec/images/DebugSpec/circle.png new file mode 100644 index 000000000..898e9c96b Binary files /dev/null and b/src/spec/images/DebugSpec/circle.png differ diff --git a/src/spec/images/DebugSpec/line.png b/src/spec/images/DebugSpec/line.png new file mode 100644 index 000000000..8e10f2b21 Binary files /dev/null and b/src/spec/images/DebugSpec/line.png differ diff --git a/src/spec/images/DebugSpec/lines.png b/src/spec/images/DebugSpec/lines.png new file mode 100644 index 000000000..addc4cad0 Binary files /dev/null and b/src/spec/images/DebugSpec/lines.png differ diff --git a/src/spec/images/DebugSpec/point.png b/src/spec/images/DebugSpec/point.png new file mode 100644 index 000000000..758ca7cfb Binary files /dev/null and b/src/spec/images/DebugSpec/point.png differ diff --git a/src/spec/images/DebugSpec/polygon.png b/src/spec/images/DebugSpec/polygon.png new file mode 100644 index 000000000..8ab60da96 Binary files /dev/null and b/src/spec/images/DebugSpec/polygon.png differ diff --git a/src/spec/images/DebugSpec/ray.png b/src/spec/images/DebugSpec/ray.png new file mode 100644 index 000000000..4a523f304 Binary files /dev/null and b/src/spec/images/DebugSpec/ray.png differ diff --git a/src/spec/images/DebugSpec/text.png b/src/spec/images/DebugSpec/text.png new file mode 100644 index 000000000..9d22c1f2d Binary files /dev/null and b/src/spec/images/DebugSpec/text.png differ