Skip to content

Commit

Permalink
feat: Debug Draw Improvements (#2015)
Browse files Browse the repository at this point in the history
* fix: Tweak vector-view to make console output clearer

* Add drawLine, drawRectangle, drawCircle to context

* WIP

* Fix vector

* WIP Debugger improvment

* Rev game

* Update tests

* Fix debug

* Update tests

* Update motion

* More tests

* More tests

* Add camera + flush after processing

* Add collision contacts

* Fix lint

* Fix test

* Remove unused import

* Update changelog + docs
  • Loading branch information
eonarheim committed Sep 11, 2021
1 parent 0c51f1e commit 587a5e3
Show file tree
Hide file tree
Showing 81 changed files with 1,509 additions and 459 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Expand Up @@ -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.
Expand Down Expand Up @@ -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))
Expand All @@ -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])`
Expand Down
13 changes: 13 additions & 0 deletions sandbox/datgui/dat.gui.js

Large diffs are not rendered by default.

66 changes: 40 additions & 26 deletions sandbox/src/game.ts
Expand Up @@ -51,46 +51,53 @@ 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);
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', 'physics', '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, "positionIterations", 1, 15, 1);
physics.add(ex.Physics, "velocityIterations", 1, 15, 1);

game.on("preframe", () => {
stats.begin();
});
game.on('postframe', () =>{
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 }
}
Expand Down Expand Up @@ -496,6 +503,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,
Expand All @@ -515,7 +523,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';
Expand Down
5 changes: 5 additions & 0 deletions sandbox/stats/stats.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions sandbox/tests/collisionvelocity/vel.ts
Expand Up @@ -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,
Expand Down
15 changes: 7 additions & 8 deletions sandbox/tests/physics/fastphysics.ts
@@ -1,17 +1,16 @@
/// <reference path='../../lib/excalibur.d.ts' />

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;
Expand Down
21 changes: 6 additions & 15 deletions sandbox/tests/physics/physics.ts
Expand Up @@ -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);


Expand All @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions sandbox/tests/physics/physics2.ts
@@ -1,14 +1,14 @@
/// <reference path='../../lib/excalibur.d.ts' />

ex.Physics.useRealisticPhysics();
var game = new ex.Engine({
width: 600,
height: 400
});

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;
Expand Down
4 changes: 2 additions & 2 deletions sandbox/tests/trigger/trigger.ts
Expand Up @@ -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);

Expand Down
88 changes: 26 additions & 62 deletions src/engine/Actor.ts
Expand Up @@ -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';
Expand All @@ -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
*/
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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) {
(<Traits.OffscreenCulling>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
}

0 comments on commit 587a5e3

Please sign in to comment.