refactor: queries

This commit is contained in:
cha0s 2024-06-26 07:41:07 -05:00
parent 219f796570
commit 559d77c92c
13 changed files with 56 additions and 43 deletions

View File

@ -14,7 +14,7 @@ export default class ApplyControlMovement extends System {
};
}
tick() {
for (const [Controlled, Forces, Speed] of this.select('default')) {
for (const {Controlled, Forces, Speed} of this.select('default')) {
if (!Controlled.locked) {
Forces.impulseX += Speed.speed * (Controlled.moveRight - Controlled.moveLeft);
Forces.impulseY += Speed.speed * (Controlled.moveDown - Controlled.moveUp);

View File

@ -9,9 +9,9 @@ export default class ApplyForces extends System {
}
tick(elapsed) {
for (const [Position, Forces] of this.select('default')) {
Position.x += elapsed * Forces.impulseX;
Position.y += elapsed * Forces.impulseY;
for (const {Position, Forces} of this.select('default')) {
Position.x += elapsed * (Forces.impulseX + Forces.forceX);
Position.y += elapsed * (Forces.impulseY + Forces.forceY);
}
}

View File

@ -2,6 +2,12 @@ import {System} from '@/ecs/index.js';
export default class CalculateAabbs extends System {
static get priority() {
return {
after: 'ApplyForces',
};
}
tick() {
for (const {Position: {x, y}, VisibleAabb} of this.ecs.changed(['Position'])) {
if (VisibleAabb) {

View File

@ -1,8 +1,5 @@
import {RESOLUTION} from '@/constants.js'
import {System} from '@/ecs/index.js';
const [hx, hy] = [RESOLUTION.x / 2, RESOLUTION.y / 2];
export default class FollowCamera extends System {
static queries() {
@ -19,8 +16,8 @@ export default class FollowCamera extends System {
}
tick(elapsed) {
for (const [, , entityId] of this.select('default')) {
this.updateCamera(elapsed * 3, this.ecs.get(entityId));
for (const {id} of this.select('default')) {
this.updateCamera(elapsed * 3, this.ecs.get(id));
}
}

View File

@ -3,7 +3,7 @@ import {System} from '@/ecs/index.js';
export default class ResetForces extends System {
static get priority() {
return {phase: 'pre'};
return {phase: 'post'};
}
static queries() {
@ -13,7 +13,7 @@ export default class ResetForces extends System {
}
tick() {
for (const [Forces] of this.select('default')) {
for (const {Forces} of this.select('default')) {
Forces.impulseX = 0;
Forces.impulseY = 0;
}

View File

@ -9,7 +9,7 @@ export default class ControlMovement extends System {
}
tick(elapsed) {
for (const [Sprite] of this.select('default')) {
for (const {Sprite} of this.select('default')) {
Sprite.elapsed += elapsed / Sprite.speed;
while (Sprite.elapsed > 1) {
Sprite.elapsed -= 1;

View File

@ -15,7 +15,7 @@ export default class RunTickingPromises extends System {
}
tick(elapsed) {
for (const [Ticking] of this.select('default')) {
for (const {Ticking} of this.select('default')) {
if (Ticking.isTicking) {
Ticking.tick(elapsed);
}

View File

@ -9,11 +9,10 @@ export default class SpriteDirection extends System {
}
tick() {
for (const [Sprite, entityId] of this.select('default')) {
const entity = this.ecs.get(entityId);
for (const {Controlled, Direction, Sprite} of this.select('default')) {
const parts = [];
if (entity.Controlled) {
const {locked, moveUp, moveRight, moveDown, moveLeft} = entity.Controlled;
if (Controlled) {
const {locked, moveUp, moveRight, moveDown, moveLeft} = Controlled;
if (locked) {
continue;
}
@ -24,14 +23,14 @@ export default class SpriteDirection extends System {
parts.push('idle');
}
}
if (entity.Direction) {
if (Direction) {
const name = {
0: 'up',
1: 'right',
2: 'down',
3: 'left',
};
parts.push(name[entity.Direction.direction]);
parts.push(name[Direction.direction]);
}
if (parts.length > 0) {
Sprite.animation = parts.join(':');

View File

@ -54,6 +54,12 @@ class SpatialHash {
export default class UpdateSpatialHash extends System {
static get priority() {
return {
after: 'CalculateAabbs',
};
}
deindex(entities) {
super.deindex(entities);
for (const id of entities) {

View File

@ -134,10 +134,10 @@ test('ticks systems', () => {
}
tick(elapsed) {
for (const [position, momentum] of this.select('default')) {
position.x += momentum.x * elapsed;
position.y += momentum.y * elapsed;
position.z += momentum.z * elapsed;
for (const {Position, Momentum} of this.select('default')) {
Position.x += Momentum.x * elapsed;
Position.y += Momentum.y * elapsed;
Position.z += Momentum.z * elapsed;
}
}

View File

@ -1,18 +1,19 @@
export default class Query {
$$criteria = {with: [], without: []};
$$ecs;
$$index = new Set();
constructor(parameters, Components) {
constructor(parameters, ecs) {
this.$$ecs = ecs;
for (let i = 0; i < parameters.length; ++i) {
const parameter = parameters[i];
switch (parameter.charCodeAt(0)) {
case '!'.charCodeAt(0):
this.$$criteria.without.push(Components[parameter.slice(1)]);
this.$$criteria.without.push(ecs.Components[parameter.slice(1)]);
break;
default:
this.$$criteria.with.push(Components[parameter]);
this.$$criteria.with.push(ecs.Components[parameter]);
break;
}
}
@ -62,7 +63,6 @@ export default class Query {
select() {
const it = this.$$index.values();
const value = [];
return {
[Symbol.iterator]() {
return this;
@ -72,11 +72,7 @@ export default class Query {
if (result.done) {
return {done: true, value: undefined};
}
for (let i = 0; i < this.$$criteria.with.length; ++i) {
value[i] = this.$$criteria.with[i].get(result.value);
}
value[this.$$criteria.with.length] = result.value;
return {done: false, value};
return {done: false, value: this.$$ecs.get(result.value)};
},
};
}

View File

@ -23,15 +23,24 @@ Components.A.createMany([[2], [3]]);
Components.B.createMany([[1], [2]]);
Components.C.createMany([[2], [4]]);
const fakeEcs = (Components) => ({
Components,
get(id) {
return Object.fromEntries(
Object.entries(Components)
.map(([componentName, Component]) => [componentName, Component.get(id)])
.concat([['id', id]])
);
},
});
function testQuery(parameters, expected) {
const query = new Query(parameters, Components);
const query = new Query(parameters, fakeEcs(Components));
query.reindex([1, 2, 3]);
expect(query.count)
.to.equal(expected.length);
for (const _ of query.select()) {
expect(_.length)
.to.equal(parameters.filter((spec) => '!'.charCodeAt(0) !== spec.charCodeAt(0)).length + 1);
expect(expected.includes(_.pop()))
expect(expected.includes(_.id))
.to.equal(true);
}
}
@ -51,7 +60,7 @@ test('can query excluding', () => {
});
test('can deindex', () => {
const query = new Query(['A'], Components);
const query = new Query(['A'], fakeEcs(Components));
query.reindex([1, 2, 3]);
expect(query.count)
.to.equal(2);
@ -63,7 +72,7 @@ test('can deindex', () => {
test('can reindex', () => {
const Test = new (wrapSpecification('Test', {a: {type: 'int32', defaultValue: 420}}));
Test.createMany([[2], [3]]);
const query = new Query(['Test'], {Test});
const query = new Query(['Test'], fakeEcs({Test}));
query.reindex([2, 3]);
expect(query.count)
.to.equal(2);
@ -74,10 +83,10 @@ test('can reindex', () => {
});
test('can select', () => {
const query = new Query(['A'], Components);
const query = new Query(['A'], fakeEcs(Components));
query.reindex([1, 2, 3]);
const it = query.select();
const result = it.next();
expect(result.value[0].a)
const {value: {A}} = it.next();
expect(A.a)
.to.equal(420);
});

View File

@ -16,7 +16,7 @@ export default class System {
this.ecs = ecs;
const queries = this.constructor.queries();
for (const i in queries) {
this.queries[i] = new Query(queries[i], ecs.Components);
this.queries[i] = new Query(queries[i], ecs);
}
this.reindex(ecs.entities);
}