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

excaliburjs / Excalibur / 19788875281

29 Nov 2025 08:23PM UTC coverage: 88.657% (+0.01%) from 88.647%
19788875281

Pull #3591

github

web-flow
Merge d11b5736d into d33ac1f05
Pull Request #3591: feat: Mobile play button

5297 of 7231 branches covered (73.25%)

17 of 17 new or added lines in 1 file covered. (100.0%)

8 existing lines in 2 files now uncovered.

14670 of 16547 relevant lines covered (88.66%)

24600.86 hits per line

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

98.68
/src/engine/EntityComponentSystem/EntityManager.ts
1
import { Entity } from './Entity';
2
import type { World } from './World';
3
import { removeItemFromArray } from '../Util/Util';
4
import type { Scene } from '../Scene';
5

6
// Add/Remove entities and components
7

8
export class EntityManager {
9
  public entities: Entity[] = [];
1,338✔
10
  public _entityIndex: { [entityId: string]: Entity } = {};
1,338✔
11
  private _childAddedHandlerMap = new Map<Entity, (entity: Entity) => void>();
1,338✔
12
  private _childRemovedHandlerMap = new Map<Entity, (entity: Entity) => void>();
1,338✔
13

14
  constructor(private _world: World) {}
1,338✔
15

16
  /**
17
   * Runs the entity lifecycle
18
   * @param scene
19
   * @param elapsed
20
   */
21
  public updateEntities(scene: Scene, elapsed: number) {
22
    for (let entityIndex = 0; entityIndex < this.entities.length; entityIndex++) {
1,799✔
23
      const entity = this.entities[entityIndex];
3,252✔
24
      entity.update(scene.engine, elapsed);
3,252✔
25
      if (!entity.isActive) {
3,252✔
26
        this.removeEntity(entity);
17✔
27
      }
28
    }
29
  }
30

31
  public findEntitiesForRemoval() {
32
    for (let entityIndex = 0; entityIndex < this.entities.length; entityIndex++) {
2,735✔
33
      const entity = this.entities[entityIndex];
5,621✔
34
      if (!entity.isActive) {
5,621✔
35
        this.removeEntity(entity);
23✔
36
      }
37
    }
38
  }
39

40
  private _createChildAddedHandler = () => (e: Entity) => {
1,962✔
41
    this.addEntity(e);
42✔
42
  };
43

44
  private _createChildRemovedHandler = () => (e: Entity) => {
1,962✔
45
    this.removeEntity(e, false);
2✔
46
  };
47

48
  /**
49
   * Adds an entity to be tracked by the EntityManager
50
   * @param entity
51
   */
52
  public addEntity(entity: Entity): void {
53
    entity.isActive = true;
1,972✔
54
    entity.scene = this._world.scene;
1,972✔
55
    if (entity && !this._entityIndex[entity.id]) {
1,972✔
56
      this._entityIndex[entity.id] = entity;
1,962✔
57
      this.entities.push(entity);
1,962✔
58
      this._world.queryManager.addEntity(entity);
1,962✔
59

60
      // if entity has children
61
      entity.children.forEach((c) => {
1,962✔
62
        c.scene = entity.scene;
1,307✔
63
        this.addEntity(c);
1,307✔
64
      });
65
      const childAdded = this._createChildAddedHandler();
1,962✔
66
      this._childAddedHandlerMap.set(entity, childAdded);
1,962✔
67
      const childRemoved = this._createChildRemovedHandler();
1,962✔
68
      this._childRemovedHandlerMap.set(entity, childRemoved);
1,962✔
69
      entity.childrenAdded$.subscribe(childAdded);
1,962✔
70
      entity.childrenRemoved$.subscribe(childRemoved);
1,962✔
71
    }
72
  }
73

74
  public removeEntity(entity: Entity, deferred?: boolean): void;
75
  public removeEntity(id: number, deferred?: boolean): void;
76
  public removeEntity(idOrEntity: number | Entity, deferred = true): void {
42✔
77
    let id = 0;
133✔
78
    if (idOrEntity instanceof Entity) {
133✔
79
      id = idOrEntity.id;
132✔
80
    } else {
81
      id = idOrEntity;
1✔
82
    }
83
    const entity = this._entityIndex[id];
133✔
84
    if (entity && entity.isActive) {
133✔
85
      entity.isActive = false;
33✔
86
    }
87

88
    if (entity && deferred) {
133✔
89
      this._entitiesToRemove.push(entity);
55✔
90
      return;
55✔
91
    }
92

93
    delete this._entityIndex[id];
78✔
94
    if (entity) {
78✔
95
      entity.scene = null;
51✔
96
      removeItemFromArray(entity, this.entities);
51✔
97
      this._world.queryManager.removeEntity(entity);
51✔
98

99
      // if entity has children
100
      entity.children.forEach((c) => {
51✔
101
        c.scene = null;
5✔
102
        this.removeEntity(c, deferred);
5✔
103
      });
104
      const childAddedHandler = this._childAddedHandlerMap.get(entity);
51✔
105
      if (childAddedHandler) {
51!
106
        entity.childrenAdded$.unsubscribe(childAddedHandler);
51✔
107
        this._childAddedHandlerMap.delete(entity);
51✔
108
      }
109
      const childRemovedHandler = this._childRemovedHandlerMap.get(entity);
51✔
110
      if (childRemovedHandler) {
51!
111
        entity.childrenRemoved$.unsubscribe(childRemovedHandler);
51✔
112
        this._childRemovedHandlerMap.delete(entity);
51✔
113
      }
114

115
      // on remove lifecycle
116
      entity._remove(this._world?.scene?.engine);
51!
117

118
      // stats
119
      if (this._world?.scene?.engine) {
51!
120
        this._world.scene.engine.stats.currFrame.actors.killed++;
34✔
121
      }
122
    }
123
  }
124

125
  private _entitiesToRemove: Entity[] = [];
1,338✔
126
  public processEntityRemovals(): void {
127
    for (let entityIndex = 0; entityIndex < this._entitiesToRemove.length; entityIndex++) {
2,737✔
128
      const entity = this._entitiesToRemove[entityIndex];
53✔
129
      if (entity.isActive) {
53!
UNCOV
130
        continue;
×
131
      }
132
      this.removeEntity(entity, false);
53✔
133
    }
134
    this._entitiesToRemove.length = 0;
2,737✔
135
  }
136

137
  public processComponentRemovals(): void {
138
    for (let entityIndex = 0; entityIndex < this.entities.length; entityIndex++) {
2,735✔
139
      const entity = this.entities[entityIndex];
5,621✔
140
      entity.processComponentRemoval();
5,621✔
141
    }
142
  }
143

144
  public getById(id: number): Entity | undefined {
145
    return this._entityIndex[id];
21✔
146
  }
147

148
  public getByName(name: string): Entity[] {
149
    return this.entities.filter((e) => e.name === name);
4✔
150
  }
151

152
  public clear(): void {
153
    for (let i = this.entities.length - 1; i >= 0; i--) {
2✔
154
      this.removeEntity(this.entities[i]);
2✔
155
    }
156
  }
157
}
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