• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 15354777440

30 May 2025 08:03PM UTC coverage: 87.858% (-1.5%) from 89.344%
15354777440

Pull #3385

github

web-flow
Merge a00f57733 into e6ec66358
Pull Request #3385: updated Meet action to add tolerance

5002 of 6948 branches covered (71.99%)

3 of 5 new or added lines in 2 files covered. (60.0%)

872 existing lines in 83 files now uncovered.

13661 of 15549 relevant lines covered (87.86%)

25187.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.93
/src/engine/Debug/DebugSystem.ts
1
import type { Engine } from '../Engine';
2
import type { Scene } from '../Scene';
3
import type { Camera } from '../Camera';
4
import { MotionComponent } from '../EntityComponentSystem/Components/MotionComponent';
5
import { ColliderComponent } from '../Collision/ColliderComponent';
6
import type { Entity, Query, World } from '../EntityComponentSystem';
7
import { SystemPriority, TransformComponent } from '../EntityComponentSystem';
8
import { System, SystemType } from '../EntityComponentSystem/System';
9
import type { ExcaliburGraphicsContext } from '../Graphics/Context/ExcaliburGraphicsContext';
10
import { vec, Vector } from '../Math/vector';
11
import { toDegrees } from '../Math/util';
12
import { BodyComponent } from '../Collision/BodyComponent';
13
import { CollisionSystem } from '../Collision/CollisionSystem';
14
import { CompositeCollider } from '../Collision/Colliders/CompositeCollider';
15
import { GraphicsComponent } from '../Graphics/GraphicsComponent';
16
import { Particle } from '../Particles/Particles';
17
import { DebugGraphicsComponent } from '../Graphics/DebugGraphicsComponent';
18
import { CoordPlane } from '../Math/coord-plane';
19
import { Debug } from '../Graphics/Debug';
20

21
export class DebugSystem extends System {
117✔
22
  static priority = SystemPriority.Lowest;
23

24
  public readonly systemType = SystemType.Draw;
1,283✔
25
  private _graphicsContext: ExcaliburGraphicsContext;
26
  private _collisionSystem: CollisionSystem;
27
  private _camera: Camera;
28
  private _engine: Engine;
29
  query: Query<typeof TransformComponent>;
30

31
  constructor(public world: World) {
1,283✔
32
    super();
1,283✔
33
    this.query = this.world.query([TransformComponent]);
1,283✔
34
  }
35

36
  public initialize(world: World, scene: Scene): void {
37
    this._graphicsContext = scene.engine.graphicsContext;
681✔
38
    this._camera = scene.camera;
681✔
39
    this._engine = scene.engine;
681✔
40
    this._collisionSystem = world.systemManager.get(CollisionSystem);
681✔
41
  }
42

43
  update(): void {
44
    if (!this._engine.isDebug) {
942✔
45
      return;
932✔
46
    }
47

48
    const filterSettings = this._engine.debug.filter;
10✔
49

50
    let id: number;
51
    let name: string;
52
    const entitySettings = this._engine.debug.entity;
10✔
53

54
    let tx: TransformComponent;
55
    const txSettings = this._engine.debug.transform;
10✔
56

57
    let motion: MotionComponent;
58
    const motionSettings = this._engine.debug.motion;
10✔
59

60
    let colliderComp: ColliderComponent;
61
    const colliderSettings = this._engine.debug.collider;
10✔
62

63
    const physicsSettings = this._engine.debug.physics;
10✔
64

65
    let graphics: GraphicsComponent;
66
    const graphicsSettings = this._engine.debug.graphics;
10✔
67

68
    let debugDraw: DebugGraphicsComponent;
69

70
    let body: BodyComponent;
71
    const bodySettings = this._engine.debug.body;
10✔
72

73
    const cameraSettings = this._engine.debug.camera;
10✔
74
    for (let i = 0; i < this.query.entities.length; i++) {
10✔
75
      const entity = this.query.entities[i];
235✔
76
      if (entity.hasTag('offscreen')) {
235!
77
        // skip offscreen entities
UNCOV
78
        continue;
×
79
      }
80
      if (entity instanceof Particle) {
235!
81
        // Particles crush the renderer :(
UNCOV
82
        continue;
×
83
      }
84
      if (filterSettings.useFilter) {
235!
85
        const allIds = filterSettings.ids.length === 0;
×
86
        const idMatch = allIds || filterSettings.ids.includes(entity.id);
×
87
        if (!idMatch) {
×
UNCOV
88
          continue;
×
89
        }
90
        const allNames = filterSettings.nameQuery === '';
×
91
        const nameMatch = allNames || entity.name.includes(filterSettings.nameQuery);
×
92
        if (!nameMatch) {
×
UNCOV
93
          continue;
×
94
        }
95
      }
96

97
      let cursor = Vector.Zero;
235✔
98
      const lineHeight = vec(0, 16);
235✔
99
      id = entity.id;
235✔
100
      name = entity.name;
235✔
101
      tx = entity.get(TransformComponent);
235✔
102

103
      // This optionally sets our camera based on the entity coord plan (world vs. screen)
104
      this._pushCameraTransform(tx);
235✔
105

106
      this._graphicsContext.save();
235✔
107
      if (tx.coordPlane === CoordPlane.Screen) {
235!
UNCOV
108
        this._graphicsContext.translate(this._engine.screen.contentArea.left, this._engine.screen.contentArea.top);
×
109
      }
110
      this._graphicsContext.z = txSettings.debugZIndex;
235✔
111

112
      this._applyTransform(entity);
235✔
113
      if (tx) {
235!
114
        if (txSettings.showAll || txSettings.showPosition) {
235✔
115
          this._graphicsContext.debug.drawPoint(Vector.Zero, { size: 4, color: txSettings.positionColor });
227✔
116
        }
117
        if (txSettings.showAll || txSettings.showPositionLabel) {
235✔
118
          this._graphicsContext.debug.drawText(`pos${tx.pos.toString(2)}`, cursor);
1✔
119
          cursor = cursor.add(lineHeight);
1✔
120
        }
121
        if (txSettings.showAll || txSettings.showZIndex) {
235✔
122
          this._graphicsContext.debug.drawText(`z(${tx.z.toFixed(1)})`, cursor);
1✔
123
          cursor = cursor.add(lineHeight);
1✔
124
        }
125

126
        if (entitySettings.showAll || entitySettings.showId) {
235✔
127
          this._graphicsContext.debug.drawText(`id(${id}) ${entity.parent ? 'child of id(' + entity.parent?.id + ')' : ''}`, cursor);
2!
128
          cursor = cursor.add(lineHeight);
2✔
129
        }
130

131
        if (entitySettings.showAll || entitySettings.showName) {
235✔
132
          this._graphicsContext.debug.drawText(`name(${name})`, cursor);
1✔
133
          cursor = cursor.add(lineHeight);
1✔
134
        }
135

136
        if (txSettings.showAll || txSettings.showRotation) {
235✔
137
          this._graphicsContext.drawLine(
1✔
138
            Vector.Zero,
139
            Vector.fromAngle(tx.rotation).scale(50).add(Vector.Zero),
140
            txSettings.rotationColor,
141
            2
142
          );
143
          this._graphicsContext.debug.drawText(`rot deg(${toDegrees(tx.rotation).toFixed(2)})`, cursor);
1✔
144
          cursor = cursor.add(lineHeight);
1✔
145
        }
146

147
        if (txSettings.showAll || txSettings.showScale) {
235✔
148
          this._graphicsContext.drawLine(Vector.Zero, tx.scale.add(Vector.Zero), txSettings.scaleColor, 2);
1✔
149
        }
150
      }
151

152
      graphics = entity.get(GraphicsComponent);
235✔
153
      if (graphics) {
235✔
154
        if (graphicsSettings.showAll || graphicsSettings.showBounds) {
232✔
155
          const bounds = graphics.localBounds;
1✔
156
          bounds.draw(this._graphicsContext, graphicsSettings.boundsColor);
1✔
157
        }
158
      }
159

160
      debugDraw = entity.get(DebugGraphicsComponent);
235✔
161
      if (debugDraw) {
235✔
162
        if (!debugDraw.useTransform) {
3✔
163
          this._graphicsContext.restore();
2✔
164
        }
165
        debugDraw.draw(this._graphicsContext, this._engine.debug);
3✔
166
        if (!debugDraw.useTransform) {
3✔
167
          this._graphicsContext.save();
2✔
168
          this._applyTransform(entity);
2✔
169
        }
170
      }
171

172
      body = entity.get(BodyComponent);
235✔
173
      if (body) {
235✔
174
        if (bodySettings.showAll || bodySettings.showCollisionGroup) {
8✔
175
          this._graphicsContext.debug.drawText(`collision group(${body.group.name})`, cursor);
1✔
176
          cursor = cursor.add(lineHeight);
1✔
177
        }
178

179
        if (bodySettings.showAll || bodySettings.showCollisionType) {
8✔
180
          this._graphicsContext.debug.drawText(`collision type(${body.collisionType})`, cursor);
1✔
181
          cursor = cursor.add(lineHeight);
1✔
182
        }
183

184
        if (bodySettings.showAll || bodySettings.showMass) {
8✔
185
          this._graphicsContext.debug.drawText(`mass(${body.mass})`, cursor);
1✔
186
          cursor = cursor.add(lineHeight);
1✔
187
        }
188

189
        if (bodySettings.showAll || bodySettings.showMotion) {
8✔
190
          this._graphicsContext.debug.drawText(`motion(${body.sleepMotion})`, cursor);
1✔
191
          cursor = cursor.add(lineHeight);
1✔
192
        }
193

194
        if (bodySettings.showAll || bodySettings.showSleeping) {
8✔
195
          this._graphicsContext.debug.drawText(`sleeping(${body.canSleep ? body.isSleeping : 'cant sleep'})`, cursor);
1!
196
          cursor = cursor.add(lineHeight);
1✔
197
        }
198
      }
199

200
      this._graphicsContext.restore();
235✔
201

202
      // World space
203
      this._graphicsContext.save();
235✔
204
      if (tx.coordPlane === CoordPlane.Screen) {
235!
UNCOV
205
        this._graphicsContext.translate(this._engine.screen.contentArea.left, this._engine.screen.contentArea.top);
×
206
      }
207
      this._graphicsContext.z = txSettings.debugZIndex;
235✔
208
      motion = entity.get(MotionComponent);
235✔
209
      if (motion) {
235✔
210
        if (motionSettings.showAll || motionSettings.showVelocity) {
8✔
211
          this._graphicsContext.debug.drawText(`vel${motion.vel.toString(2)}`, cursor.add(tx.globalPos));
1✔
212
          this._graphicsContext.drawLine(tx.globalPos, tx.globalPos.add(motion.vel), motionSettings.velocityColor, 2);
1✔
213
          cursor = cursor.add(lineHeight);
1✔
214
        }
215

216
        if (motionSettings.showAll || motionSettings.showAcceleration) {
8✔
217
          this._graphicsContext.drawLine(tx.globalPos, tx.globalPos.add(motion.acc), motionSettings.accelerationColor, 2);
1✔
218
        }
219
      }
220

221
      // Colliders live in world space already so after the restore()
222
      colliderComp = entity.get(ColliderComponent);
235✔
223
      if (colliderComp) {
235✔
224
        const collider = colliderComp.get();
9✔
225
        if ((colliderSettings.showAll || colliderSettings.showGeometry) && collider) {
9✔
226
          collider.debug(this._graphicsContext, colliderSettings.geometryColor, {
7✔
227
            lineWidth: colliderSettings.geometryLineWidth,
228
            pointSize: colliderSettings.geometryPointSize
229
          });
230
        }
231
        if (colliderSettings.showAll || colliderSettings.showBounds) {
9✔
232
          if (collider instanceof CompositeCollider) {
3✔
233
            const colliders = collider.getColliders();
1✔
234
            for (const collider of colliders) {
1✔
235
              const bounds = collider.bounds;
3✔
236
              const pos = vec(bounds.left, bounds.top);
3✔
237
              this._graphicsContext.debug.drawRect(pos.x, pos.y, bounds.width, bounds.height, { color: colliderSettings.boundsColor });
3✔
238
              if (colliderSettings.showAll || colliderSettings.showOwner) {
3!
239
                this._graphicsContext.debug.drawText(`owner id(${collider.owner.id})`, pos);
3✔
240
              }
241
            }
242
            colliderComp.bounds.draw(this._graphicsContext, colliderSettings.boundsColor);
1✔
243
          } else if (collider) {
2✔
244
            const bounds = colliderComp.bounds;
1✔
245
            const pos = vec(bounds.left, bounds.top);
1✔
246
            this._graphicsContext.debug.drawRect(pos.x, pos.y, bounds.width, bounds.height, { color: colliderSettings.boundsColor });
1✔
247
            if (colliderSettings.showAll || colliderSettings.showOwner) {
1!
248
              this._graphicsContext.debug.drawText(`owner id(${colliderComp.owner.id})`, pos);
1✔
249
            }
250
          }
251
        }
252
      }
253

254
      this._graphicsContext.restore();
235✔
255
      this._popCameraTransform(tx);
235✔
256
    }
257

258
    this._graphicsContext.save();
10✔
259
    this._camera.draw(this._graphicsContext);
10✔
260
    if (physicsSettings.showAll || physicsSettings.showBroadphaseSpacePartitionDebug) {
10!
UNCOV
261
      this._collisionSystem.debug(this._graphicsContext);
×
262
    }
263
    if (physicsSettings.showAll || physicsSettings.showCollisionContacts || physicsSettings.showCollisionNormals) {
10!
264
      for (const [_, contact] of this._engine.debug.stats.currFrame.physics.contacts) {
10✔
265
        if (physicsSettings.showAll || physicsSettings.showCollisionContacts) {
×
266
          for (const point of contact.points) {
×
UNCOV
267
            this._graphicsContext.debug.drawPoint(point, {
×
268
              size: physicsSettings.contactSize,
269
              color: physicsSettings.collisionContactColor
270
            });
271
          }
272
        }
273

274
        if (physicsSettings.showAll || physicsSettings.showCollisionNormals) {
×
275
          for (const point of contact.points) {
×
UNCOV
276
            this._graphicsContext.debug.drawLine(point, contact.normal.scale(30).add(point), {
×
277
              color: physicsSettings.collisionNormalColor
278
            });
279
          }
280
        }
281
      }
282
    }
283
    this._graphicsContext.restore();
10✔
284

285
    if (cameraSettings) {
10!
286
      this._graphicsContext.save();
10✔
287
      this._camera.draw(this._graphicsContext);
10✔
288
      if (cameraSettings.showAll || cameraSettings.showFocus) {
10!
UNCOV
289
        this._graphicsContext.drawCircle(this._camera.pos, 4, cameraSettings.focusColor);
×
290
      }
291
      if (cameraSettings.showAll || cameraSettings.showZoom) {
10!
UNCOV
292
        this._graphicsContext.debug.drawText(`zoom(${this._camera.zoom})`, this._camera.pos);
×
293
      }
294
      this._graphicsContext.restore();
10✔
295
    }
296

297
    this._graphicsContext.flush();
10✔
298
  }
299

300
  postupdate(engine: Scene<unknown>, elapsed: number): void {
301
    if (this._engine.isDebug) {
933✔
302
      this._graphicsContext.save();
1✔
303
      if (this._camera) {
1!
304
        this._camera.draw(this._graphicsContext);
1✔
305
      }
306
      Debug.flush(this._graphicsContext);
1✔
307
      this._graphicsContext.restore();
1✔
308
    }
309
  }
310

311
  /**
312
   * This applies the current entity transform to the graphics context
313
   * @param entity
314
   */
315
  private _applyTransform(entity: Entity): void {
316
    const ancestors = entity.getAncestors();
237✔
317
    for (const ancestor of ancestors) {
237✔
318
      const transform = ancestor?.get(TransformComponent);
462!
319
      if (transform) {
462!
320
        this._graphicsContext.translate(transform.pos.x, transform.pos.y);
462✔
321
        this._graphicsContext.scale(transform.scale.x, transform.scale.y);
462✔
322
        this._graphicsContext.rotate(transform.rotation);
462✔
323
      }
324
    }
325
  }
326

327
  /**
328
   * Applies the current camera transform if in world coordinates
329
   * @param transform
330
   */
331
  private _pushCameraTransform(transform: TransformComponent) {
332
    // Establish camera offset per entity
333
    if (transform.coordPlane === CoordPlane.World) {
235!
334
      this._graphicsContext.save();
235✔
335
      if (this._camera) {
235!
336
        this._camera.draw(this._graphicsContext);
235✔
337
      }
338
    }
339
  }
340

341
  /**
342
   * Resets the current camera transform if in world coordinates
343
   * @param transform
344
   */
345
  private _popCameraTransform(transform: TransformComponent) {
346
    if (transform.coordPlane === CoordPlane.World) {
235!
347
      // Apply camera world offset
348
      this._graphicsContext.restore();
235✔
349
    }
350
  }
351
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc