diff --git a/CHANGELOG.md b/CHANGELOG.md index 44d419287..c5fef7156 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,12 +42,14 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Breaking Changes - `ex.Action` now requires a unique `id` property +- Z-indexes are now relative to the parent's Z-index. You can get the global Z-index with the `globalZ` property on the Actor or TransformComponent. ### Deprecated ### Added - Built in actions now have a unique `id` property +- `globalZ` property to Actor and TransformComponent ### Fixed diff --git a/src/engine/Actor.ts b/src/engine/Actor.ts index cd77d4fb6..c71564b02 100644 --- a/src/engine/Actor.ts +++ b/src/engine/Actor.ts @@ -884,6 +884,13 @@ export class Actor extends Entity implements Eventable, PointerEvents, CanInitia return this.get(TransformComponent).globalScale; } + /** + * The global z-index of the actor + */ + public get globalZ(): number { + return this.get(TransformComponent).globalZ; + } + // #region Collision /** diff --git a/src/engine/EntityComponentSystem/Components/TransformComponent.ts b/src/engine/EntityComponentSystem/Components/TransformComponent.ts index a3441c487..314b8b173 100644 --- a/src/engine/EntityComponentSystem/Components/TransformComponent.ts +++ b/src/engine/EntityComponentSystem/Components/TransformComponent.ts @@ -43,24 +43,31 @@ export class TransformComponent extends Component { * Observable that emits when the z index changes on this component */ public zIndexChanged$ = new Observable(); - private _z = 0; /** * The z-index ordering of the entity, a higher values are drawn on top of lower values. * For example z=99 would be drawn on top of z=0. */ public get z(): number { - return this._z; + return this._transform.z; } public set z(val: number) { - const oldz = this._z; - this._z = val; + const oldz = this._transform.z; + this._transform.z = val; if (oldz !== val) { this.zIndexChanged$.notifyAll(val); } } + public get globalZ() { + return this._transform.globalZ; + } + + public set globalZ(z: number) { + this._transform.globalZ = z; + } + private _coordPlane = CoordPlane.World; /** * The [[CoordPlane|coordinate plane|]] for this transform for the entity. @@ -135,7 +142,6 @@ export class TransformComponent extends Component { clone(): TransformComponent { const component = new TransformComponent(); component._transform = this._transform.clone(); - component._z = this._z; return component; } } diff --git a/src/engine/Graphics/GraphicsSystem.ts b/src/engine/Graphics/GraphicsSystem.ts index ccd773b6b..42674562c 100644 --- a/src/engine/Graphics/GraphicsSystem.ts +++ b/src/engine/Graphics/GraphicsSystem.ts @@ -65,7 +65,7 @@ export class GraphicsSystem extends System { this._graphicsContext = this._engine.graphicsContext; if (this._zHasChanged) { this._sortedTransforms.sort((a, b) => { - return a.z - b.z; + return a.globalZ - b.globalZ; }); this._zHasChanged = false; } @@ -265,7 +265,7 @@ export class GraphicsSystem extends System { } if (transform) { - this._graphicsContext.z = transform.z; + this._graphicsContext.z = transform.globalZ; this._graphicsContext.translate(tx.pos.x, tx.pos.y); this._graphicsContext.scale(tx.scale.x, tx.scale.y); this._graphicsContext.rotate(tx.rotation); diff --git a/src/engine/Math/transform.ts b/src/engine/Math/transform.ts index 5074392ef..91410f03f 100644 --- a/src/engine/Math/transform.ts +++ b/src/engine/Math/transform.ts @@ -160,6 +160,32 @@ export class Transform { }); } + private _z: number = 0; + + set z(z: number) { + this._z = z; + this.flagDirty(); + } + + get z() { + return this._z; + } + + set globalZ(z: number) { + if (this.parent) { + this.z = z - this.parent.globalZ; + } else { + this.z = z; + } + } + + get globalZ() { + if (this.parent) { + return this.z + this.parent.globalZ; + } + return this.z; + } + private _isDirty = false; private _isInverseDirty = false; private _matrix = AffineMatrix.identity(); @@ -223,6 +249,7 @@ export class Transform { public clone(dest?: Transform) { const target = dest ?? new Transform(); this._pos.clone(target._pos); + target._z = this._z; target._rotation = this._rotation; this._scale.clone(target._scale); target.flagDirty(); diff --git a/src/spec/GraphicsSystemSpec.ts b/src/spec/GraphicsSystemSpec.ts index 544741961..06527269c 100644 --- a/src/spec/GraphicsSystemSpec.ts +++ b/src/spec/GraphicsSystemSpec.ts @@ -15,11 +15,21 @@ describe('A Graphics ECS System', () => { entities = [ new ex.Entity().addComponent(new ex.TransformComponent()).addComponent(new ex.GraphicsComponent()), new ex.Entity().addComponent(new ex.TransformComponent()).addComponent(new ex.GraphicsComponent()), + new ex.Entity().addComponent(new ex.TransformComponent()).addComponent(new ex.GraphicsComponent()), + + // parent + new ex.Entity().addComponent(new ex.TransformComponent()).addComponent(new ex.GraphicsComponent()), + + // child of ^ new ex.Entity().addComponent(new ex.TransformComponent()).addComponent(new ex.GraphicsComponent()) ]; - entities[0].get(TransformComponent).z = 10; - entities[1].get(TransformComponent).z = 5; - entities[2].get(TransformComponent).z = 1; + entities[0].get(TransformComponent).z = 100; + entities[1].get(TransformComponent).z = 50; + entities[2].get(TransformComponent).z = 10; + + entities[3].get(TransformComponent).z = 5; + entities[3].addChild(entities[4]); + entities[4].get(TransformComponent).z = -1; }); afterEach(() => { @@ -32,7 +42,7 @@ describe('A Graphics ECS System', () => { expect(ex.GraphicsSystem).toBeDefined(); }); - it('sorts entities by transform.z', () => { + it('sorts entities by transform.globalZ', () => { const world = engine.currentScene.world; const sut = new ex.GraphicsSystem(world); engine.currentScene._initialize(engine); diff --git a/src/spec/TransformComponentSpec.ts b/src/spec/TransformComponentSpec.ts index 2816af201..2ddbff4f2 100644 --- a/src/spec/TransformComponentSpec.ts +++ b/src/spec/TransformComponentSpec.ts @@ -146,6 +146,26 @@ describe('A TransformComponent', () => { expect(childTx.rotation).toBeCloseTo(Math.PI); // Math.PI + Math.PI = 2PI = 0 global }); + it('can have parent/child relationships with z', () => { + const parent = new ex.Entity([new ex.TransformComponent()]); + const child = new ex.Entity([new ex.TransformComponent()]); + parent.addChild(child); + + const parentTx = parent.get(ex.TransformComponent); + const childTx = child.get(ex.TransformComponent); + + // Changing a parent z influences the child global z + parentTx.z = 100; + expect(childTx.z).toBe(0); + expect(childTx.globalZ).toBe(100); + + // Changing a child global z affects childs local and not parent z + parentTx.z = 100; + childTx.globalZ = 50; + expect(parentTx.z).toBe(100); + expect(childTx.z).toBe(-50); + }); + it('can retrieve the global transform', () => { const parent = new ex.Entity([new ex.TransformComponent()]); const child = new ex.Entity([new ex.TransformComponent()]);