Skip to content

Commit

Permalink
feat: z-indexes are relative to their parent # (#3070)
Browse files Browse the repository at this point in the history
## Changes:

- 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.
  • Loading branch information
mattjennings authored May 20, 2024
1 parent 863e998 commit 47cbb18
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions src/engine/Actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand Down
16 changes: 11 additions & 5 deletions src/engine/EntityComponentSystem/Components/TransformComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,31 @@ export class TransformComponent extends Component {
* Observable that emits when the z index changes on this component
*/
public zIndexChanged$ = new Observable<number>();
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.
Expand Down Expand Up @@ -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;
}
}
4 changes: 2 additions & 2 deletions src/engine/Graphics/GraphicsSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
27 changes: 27 additions & 0 deletions src/engine/Math/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
18 changes: 14 additions & 4 deletions src/spec/GraphicsSystemSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand All @@ -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);
Expand Down
20 changes: 20 additions & 0 deletions src/spec/TransformComponentSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()]);
Expand Down

0 comments on commit 47cbb18

Please sign in to comment.