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

excaliburjs / Excalibur / 11646099157

02 Nov 2024 09:47PM UTC coverage: 90.212% (-0.1%) from 90.352%
11646099157

push

github

eonarheim
test: update raycast test

5862 of 7457 branches covered (78.61%)

12839 of 14232 relevant lines covered (90.21%)

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

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

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

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

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

42
  update(): void {
43
    if (!this._engine.isDebug) {
850✔
44
      return;
840✔
45
    }
46

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

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

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

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

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

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

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

67
    let debugDraw: DebugGraphicsComponent;
68

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

340
  /**
341
   * Resets the current camera transform if in world coordinates
342
   * @param transform
343
   */
344
  private _popCameraTransform(transform: TransformComponent) {
345
    if (transform.coordPlane === CoordPlane.World) {
235!
346
      // Apply camera world offset
347
      this._graphicsContext.restore();
235✔
348
    }
349
  }
350
}
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