Skip to content

Commit

Permalink
perf: Add zIndexChanged$ + Improve pointer system perf (#2242)
Browse files Browse the repository at this point in the history
This PR adds `zIndexChanged$` to the `ex.TransformComponent` in order to know when the z index changes. This allows the `ex.PointerSystem` to maintain a sorted list which is much faster than using the ECS Query.
  • Loading branch information
eonarheim authored Feb 12, 2022
1 parent 7bd8a82 commit c42be8b
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Added faster `ex.BoundingBox.transform(...)` implementation.
- Added faster `ex.BoundingBox.overlap(...)` implementation.
- Added `ex.Vector.min(...)` and `ex.Vector.max(...)` to find the min/max of each vector component between 2 vectors.
- Added `ex.TransformComponent.zIndexChange$` observable to watch when z index changes.

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Matrix, MatrixLocations } from '../../Math/matrix';
import { VectorView } from '../../Math/vector-view';
import { Vector, vec } from '../../Math/vector';
import { Component } from '../Component';
import { Observable } from '../../Util/Observable';

export interface Transform {
/**
Expand Down Expand Up @@ -191,11 +192,27 @@ export class TransformComponent extends Component<'ex.transform'> implements Tra
}
}

/**
* 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 z: number = 0;
public get z(): number {
return this._z;
}

public set z(val: number) {
const oldz = this._z;
this._z = val;
if (oldz !== val) {
this.zIndexChanged$.notifyAll(val);
}
}

/**
* The rotation of the entity in radians. For example `Math.PI` radians is the same as 180 degrees.
Expand Down
54 changes: 47 additions & 7 deletions src/engine/Input/PointerSystem.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { ColliderComponent } from '../Collision/ColliderComponent';
import { Engine } from '../Engine';
import { System, TransformComponent, SystemType, Entity, CoordPlane } from '../EntityComponentSystem';
import {
System,
TransformComponent,
SystemType,
Entity,
CoordPlane,
AddedEntity,
RemovedEntity,
isAddedSystemEntity
} from '../EntityComponentSystem';
import { GraphicsComponent } from '../Graphics/GraphicsComponent';
import { Scene } from '../Scene';
import { PointerComponent } from './PointerComponent';
Expand Down Expand Up @@ -39,9 +48,40 @@ export class PointerSystem extends System<TransformComponent | PointerComponent>
this._receiver = this._engine.input.pointers;
}

public sort(a: Entity, b: Entity) {
// Sort from high to low, because things on 'top' receive the pointer events first
return b.get(TransformComponent).z - a.get(TransformComponent).z;
private _sortedTransforms: TransformComponent[] = [];
private _sortedEntities: Entity[] = [];

private _zHasChanged = false;
private _zIndexUpdate = () => {
this._zHasChanged = true;
};

public preupdate(): void {
if (this._zHasChanged) {
this._sortedTransforms.sort((a, b) => {
return b.z - a.z;
});
this._sortedEntities = this._sortedTransforms.map(t => t.owner);
this._zHasChanged = false;
}
}

public notify(entityAddedOrRemoved: AddedEntity | RemovedEntity): void {
if (isAddedSystemEntity(entityAddedOrRemoved)) {
const tx = entityAddedOrRemoved.data.get(TransformComponent);
this._sortedTransforms.push(tx);
this._sortedEntities.push(tx.owner);
tx.zIndexChanged$.subscribe(this._zIndexUpdate);
this._zHasChanged = true;
} else {
const tx = entityAddedOrRemoved.data.get(TransformComponent);
tx.zIndexChanged$.unsubscribe(this._zIndexUpdate);
const index = this._sortedTransforms.indexOf(tx);
if (index > -1) {
this._sortedTransforms.splice(index, 1);
this._sortedEntities.splice(index, 1);
}
}
}

public entityCurrentlyUnderPointer(entity: Entity, pointerId: number) {
Expand Down Expand Up @@ -73,12 +113,12 @@ export class PointerSystem extends System<TransformComponent | PointerComponent>
this.currentFrameEntityToPointers.set(entity.id, pointers.concat(pointerId));
}

public update(entities: Entity[]): void {
public update(_entities: Entity[]): void {
// Locate all the pointer/entity mappings
this._processPointerToEntity(entities);
this._processPointerToEntity(this._sortedEntities);

// Dispatch pointer events on entities
this._dispatchEvents(entities);
this._dispatchEvents(this._sortedEntities);

// Clear last frame's events
this._receiver.update();
Expand Down
11 changes: 11 additions & 0 deletions src/spec/TransformComponentSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,15 @@ describe('A TransformComponent', () => {
expect(childTx.getGlobalTransform().rotation).toBe(Math.PI);
expect(childTx.getGlobalTransform().scale).toBeVector(ex.vec(2, 3));
});

it('can observe a z index change', () => {
const tx = new ex.TransformComponent();
const zSpy = jasmine.createSpy('zSpy');
tx.zIndexChanged$.subscribe(zSpy);

tx.z = 19;

expect(zSpy).toHaveBeenCalledWith(19);
expect(tx.z).toBe(19);
});
});

0 comments on commit c42be8b

Please sign in to comment.