Skip to content

Commit

Permalink
feat: Debug Draw Static Helpers (excaliburjs#2929)
Browse files Browse the repository at this point in the history
This PR adds 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)`
  • Loading branch information
eonarheim committed Feb 16, 2024
1 parent 7baf96b commit c995d42
Show file tree
Hide file tree
Showing 26 changed files with 378 additions and 39 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 29 additions & 4 deletions sandbox/src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion sandbox/tests/polygon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,5 @@ var triangulated = shape.triangulate();
actor.collider.set(triangulated);
game.add(actor);


game.currentScene.camera.zoom = .75;
game.start();
33 changes: 27 additions & 6 deletions src/engine/Collision/BoundingBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
13 changes: 4 additions & 9 deletions src/engine/Collision/Colliders/PolygonCollider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -46,6 +46,7 @@ export class PolygonCollider extends Collider {
public flagDirty() {
this._localBoundsDirty = true;
this._localSidesDirty = true;
this._transformedPointsDirty = true;
this._sidesDirty = true;
}

Expand Down Expand Up @@ -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 });
}
}
5 changes: 3 additions & 2 deletions src/engine/Debug/Debug.ts → src/engine/Debug/DebugConfig.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -309,6 +309,7 @@ export class Debug implements DebugFlags {
collisionNormalColor: Color.Cyan,

showCollisionContacts: true,
contactSize: 2,
collisionContactColor: Color.Red
};

Expand Down
4 changes: 0 additions & 4 deletions src/engine/Debug/DebugFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
15 changes: 14 additions & 1 deletion src/engine/Debug/DebugSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
});
}
}

Expand Down Expand Up @@ -290,6 +294,15 @@ export class DebugSystem extends System {
this._graphicsContext.flush();
}

postupdate(engine: Scene<unknown>, 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
Expand Down
2 changes: 1 addition & 1 deletion src/engine/Debug/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './Debug';
export * from './DebugConfig';
export * from './DebugFlags';
export * from './DebugSystem';
6 changes: 3 additions & 3 deletions src/engine/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -505,7 +505,7 @@ export class Engine<TKnownScenes extends string = any> implements CanInitialize,
* * Graphics
* * Colliders
*/
public debug: Debug;
public debug: DebugConfig;

/**
* Access [[stats]] that holds frame statistics.
Expand Down Expand Up @@ -932,7 +932,7 @@ O|===|* >________________>\n\
};
}

this.debug = new Debug(this);
this.debug = new DebugConfig(this);

this.director = new Director(this, options.scenes);

Expand Down
4 changes: 2 additions & 2 deletions src/engine/Graphics/Context/ExcaliburGraphicsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 5 additions & 0 deletions src/engine/Graphics/Context/line-renderer/line-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
107 changes: 107 additions & 0 deletions src/engine/Graphics/Debug.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit c995d42

Please sign in to comment.