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

excaliburjs / Excalibur / 20121940506

11 Dec 2025 04:26AM UTC coverage: 87.968% (-0.7%) from 88.636%
20121940506

Pull #3617

github

web-flow
Merge 7a8a256d7 into 548f5e4e7
Pull Request #3617: [feat] extend the components class to add json/serialize/deserialize to component properties

5431 of 7540 branches covered (72.03%)

211 of 366 new or added lines in 6 files covered. (57.65%)

149 existing lines in 5 files now uncovered.

14929 of 16971 relevant lines covered (87.97%)

24160.88 hits per line

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

88.89
/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: string;
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,285✔
23
  private _logger = Logger.getInstance();
9,285✔
24
  private _parentComponent: TransformComponent | null = null;
9,285✔
25
  private _transform = new Transform();
9,285✔
26
  public get() {
27
    return this._transform;
16,184✔
28
  }
29

30
  private _addChildTransform = (child: Entity) => {
9,285✔
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,406✔
UNCOV
39
      this._addChildTransform(child);
×
40
    }
41
    owner.childrenAdded$.subscribe((child) => this._addChildTransform(child));
7,406✔
42
    owner.childrenRemoved$.subscribe((child) => {
7,406✔
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;
5✔
52
    this._parentComponent = null;
5✔
53
  }
54

55
  /**
56
   * Observable that emits when the z index changes on this component
57
   */
58
  public zIndexChanged$ = new Observable<number>();
9,285✔
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;
566✔
66
  }
67

68
  public set z(val: number) {
69
    const oldz = this._transform.z;
5,518✔
70
    this._transform.z = val;
5,518✔
71
    if (oldz !== val) {
5,518✔
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,285✔
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) {
944✔
97
      this._coordPlane = value;
943✔
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,271✔
107
  }
108
  set pos(v: Vector) {
109
    this._transform.pos = v;
7,708✔
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,172✔
121
  }
122
  set rotation(rotation) {
123
    this._transform.rotation = rotation;
4,546✔
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,650✔
135
  }
136
  set scale(v: Vector) {
137
    this._transform.scale = v;
4,539✔
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 {
166
    let type = this.constructor.name;
3✔
167
    const data: TransformComponentData = {
3✔
168
      type,
169
      pos: { x: this.pos.x, y: this.pos.y },
170
      rotation: this.rotation,
171
      scale: { x: this.scale.x, y: this.scale.y },
172
      z: this.z
173
    };
174

175
    // Only serialize coordPlane if not inherited from parent
176
    if (!this._parentComponent) {
3!
177
      data.coordPlane = this._coordPlane;
3✔
178
    }
179

180
    return data;
3✔
181
  }
182

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

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