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

excaliburjs / Excalibur / 20009271139

07 Dec 2025 07:33PM UTC coverage: 87.821% (-0.8%) from 88.636%
20009271139

Pull #3617

github

web-flow
Merge 6656ac6c0 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%)

24483.22 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
// =============================================================
10
// Transform Component Serialization Data
11
// ============================================================
12

13
export interface TransformComponentData {
14
  type: 'TransformComponent';
15
  pos: { x: number; y: number };
16
  rotation: number;
17
  scale: { x: number; y: number };
18
  z: number;
19
  coordPlane?: CoordPlane;
20
}
21

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

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

55
  /**
56
   * Observable that emits when the z index changes on this component
57
   */
58
  public zIndexChanged$ = new Observable<number>();
9,267✔
59

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

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

76
  public get globalZ() {
77
    return this._transform.globalZ;
13,818✔
78
  }
79

80
  public set globalZ(z: number) {
81
    this._transform.globalZ = z;
1✔
82
  }
83

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

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

105
  get pos() {
106
    return this._transform.pos;
20,265✔
107
  }
108
  set pos(v: Vector) {
109
    this._transform.pos = v;
7,702✔
110
  }
111

112
  get globalPos() {
113
    return this._transform.globalPos;
5,647✔
114
  }
115
  set globalPos(v: Vector) {
116
    this._transform.globalPos = v;
134✔
117
  }
118

119
  get rotation() {
120
    return this._transform.rotation;
8,169✔
121
  }
122
  set rotation(rotation) {
123
    this._transform.rotation = rotation;
4,540✔
124
  }
125

126
  get globalRotation() {
127
    return this._transform.globalRotation;
456✔
128
  }
129
  set globalRotation(rotation) {
130
    this._transform.globalRotation = rotation;
129✔
131
  }
132

133
  get scale() {
134
    return this._transform.scale;
23,644✔
135
  }
136
  set scale(v: Vector) {
137
    this._transform.scale = v;
4,533✔
138
  }
139

140
  get globalScale() {
141
    return this._transform.globalScale;
1,226✔
142
  }
143
  set globalScale(v: Vector) {
144
    this._transform.globalScale = v;
2✔
145
  }
146

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

151
  apply(v: Vector) {
152
    return this._transform.apply(v);
360✔
153
  }
154

155
  clone(): TransformComponent {
156
    const component = new TransformComponent();
7✔
157
    component._transform = this._transform.clone();
7✔
158
    return component;
7✔
159
  }
160

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

174
    // Only serialize coordPlane if not inherited from parent
NEW
UNCOV
175
    if (!this._parentComponent) {
×
NEW
UNCOV
176
      data.coordPlane = this._coordPlane;
×
177
    }
178

NEW
UNCOV
179
    return data;
×
180
  }
181

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

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