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

excaliburjs / Excalibur / 20008642092

07 Dec 2025 06:40PM UTC coverage: 87.821% (-0.8%) from 88.636%
20008642092

Pull #3617

github

web-flow
Merge c8a4c0a6d into 0f899e40c
Pull Request #3617: [feat] extend the components class to add json/serialize/deserialize to component properties

5316 of 7441 branches covered (71.44%)

0 of 154 new or added lines in 5 files covered. (0.0%)

193 existing lines in 5 files now uncovered.

14718 of 16759 relevant lines covered (87.82%)

24468.94 hits per line

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

82.26
/src/engine/EntityComponentSystem/Components/TransformComponent.ts
1
import type { Vector } from '../../Math/vector';
2
import { CoordPlane } from '../../Math/coord-plane';
3
import { Transform } from '../../Math/transform';
4
import { Component } from '../Component';
5
import type { Entity } from '../Entity';
6
import { Observable } from '../../Util/Observable';
7
import { Logger } from '../../Util/Log';
8

9
export interface TransformComponentData {
10
  type: 'TransformComponent';
11
  pos: { x: number; y: number };
12
  rotation: number;
13
  scale: { x: number; y: number };
14
  z: number;
15
  coordPlane?: CoordPlane;
16
}
17

18
export class TransformComponent extends Component {
9,267✔
19
  private _logger = Logger.getInstance();
9,267✔
20
  private _parentComponent: TransformComponent | null = null;
9,267✔
21
  private _transform = new Transform();
9,267✔
22
  public get() {
23
    return this._transform;
16,184✔
24
  }
25

26
  private _addChildTransform = (child: Entity) => {
9,267✔
27
    const childTxComponent = child.get(TransformComponent);
2,925✔
28
    if (childTxComponent) {
2,925!
29
      childTxComponent._transform.parent = this._transform;
2,925✔
30
      childTxComponent._parentComponent = this;
2,925✔
31
    }
32
  };
33
  onAdd(owner: Entity): void {
34
    for (const child of owner.children) {
7,400✔
UNCOV
35
      this._addChildTransform(child);
×
36
    }
37
    owner.childrenAdded$.subscribe((child) => this._addChildTransform(child));
7,400✔
38
    owner.childrenRemoved$.subscribe((child) => {
7,400✔
39
      const childTxComponent = child.get(TransformComponent);
1✔
40
      if (childTxComponent) {
1!
41
        childTxComponent._transform.parent = null;
1✔
42
        childTxComponent._parentComponent = null;
1✔
43
      }
44
    });
45
  }
46
  onRemove(_previousOwner: Entity): void {
47
    this._transform.parent = null;
2✔
48
    this._parentComponent = null;
2✔
49
  }
50

51
  /**
52
   * Observable that emits when the z index changes on this component
53
   */
54
  public zIndexChanged$ = new Observable<number>();
9,267✔
55

56
  /**
57
   * The z-index ordering of the entity, a higher values are drawn on top of lower values.
58
   * For example z=99 would be drawn on top of z=0.
59
   */
60
  public get z(): number {
61
    return this._transform.z;
563✔
62
  }
63

64
  public set z(val: number) {
65
    const oldz = this._transform.z;
5,512✔
66
    this._transform.z = val;
5,512✔
67
    if (oldz !== val) {
5,512✔
68
      this.zIndexChanged$.notifyAll(val);
1,015✔
69
    }
70
  }
71

72
  public get globalZ() {
73
    return this._transform.globalZ;
13,818✔
74
  }
75

76
  public set globalZ(z: number) {
77
    this._transform.globalZ = z;
1✔
78
  }
79

80
  private _coordPlane = CoordPlane.World;
9,267✔
81
  /**
82
   * The {@apilink CoordPlane | `coordinate plane`} for this transform for the entity.
83
   */
84
  public get coordPlane() {
85
    if (this._parentComponent) {
14,773✔
86
      return this._parentComponent.coordPlane;
4,532✔
87
    }
88
    return this._coordPlane;
10,241✔
89
  }
90

91
  public set coordPlane(value: CoordPlane) {
92
    if (!this._parentComponent) {
938✔
93
      this._coordPlane = value;
937✔
94
    } else {
95
      this._logger.warn(
1✔
96
        `Cannot set coordinate plane on child entity ${this.owner?.name}, children inherit their coordinate plane from their parents.`
1!
97
      );
98
    }
99
  }
100

101
  get pos() {
102
    return this._transform.pos;
20,265✔
103
  }
104
  set pos(v: Vector) {
105
    this._transform.pos = v;
7,702✔
106
  }
107

108
  get globalPos() {
109
    return this._transform.globalPos;
5,647✔
110
  }
111
  set globalPos(v: Vector) {
112
    this._transform.globalPos = v;
134✔
113
  }
114

115
  get rotation() {
116
    return this._transform.rotation;
8,169✔
117
  }
118
  set rotation(rotation) {
119
    this._transform.rotation = rotation;
4,540✔
120
  }
121

122
  get globalRotation() {
123
    return this._transform.globalRotation;
456✔
124
  }
125
  set globalRotation(rotation) {
126
    this._transform.globalRotation = rotation;
129✔
127
  }
128

129
  get scale() {
130
    return this._transform.scale;
23,644✔
131
  }
132
  set scale(v: Vector) {
133
    this._transform.scale = v;
4,533✔
134
  }
135

136
  get globalScale() {
137
    return this._transform.globalScale;
1,226✔
138
  }
139
  set globalScale(v: Vector) {
140
    this._transform.globalScale = v;
2✔
141
  }
142

143
  applyInverse(v: Vector) {
144
    return this._transform.applyInverse(v);
72✔
145
  }
146

147
  apply(v: Vector) {
148
    return this._transform.apply(v);
360✔
149
  }
150

151
  clone(): TransformComponent {
152
    const component = new TransformComponent();
7✔
153
    component._transform = this._transform.clone();
7✔
154
    return component;
7✔
155
  }
156

157
  /**
158
   * Custom serialization - only store local transform values
159
   * Private fields (_transform, _parentComponent) are automatically excluded
160
   */
161
  public serialize(): TransformComponentData {
NEW
UNCOV
162
    const data: TransformComponentData = {
×
163
      type: 'TransformComponent',
164
      pos: { x: this.pos.x, y: this.pos.y },
165
      rotation: this.rotation,
166
      scale: { x: this.scale.x, y: this.scale.y },
167
      z: this.z
168
    };
169

170
    // Only serialize coordPlane if not inherited from parent
NEW
UNCOV
171
    if (!this._parentComponent) {
×
NEW
UNCOV
172
      data.coordPlane = this._coordPlane;
×
173
    }
174

NEW
UNCOV
175
    return data;
×
176
  }
177

178
  /**
179
   * Restore state from serialized data
180
   */
181
  public deserialize(data: TransformComponentData): void {
182
    // Set local transform values
NEW
UNCOV
183
    this.pos = { x: data.pos.x, y: data.pos.y } as Vector;
×
NEW
UNCOV
184
    this.rotation = data.rotation;
×
NEW
UNCOV
185
    this.scale = { x: data.scale.x, y: data.scale.y } as Vector;
×
NEW
UNCOV
186
    this.z = data.z;
×
187

188
    // Only set coordPlane if provided (root entities only)
NEW
UNCOV
189
    if (data.coordPlane !== undefined && !this._parentComponent) {
×
NEW
UNCOV
190
      this._coordPlane = data.coordPlane;
×
191
    }
192
  }
193
}
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