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() { tick() {
for (const [Controlled, Forces, Speed] of this.select('default')) { for (const {Controlled, Forces, Speed} of this.select('default')) {
if (!Controlled.locked) { if (!Controlled.locked) {
Forces.impulseX += Speed.speed * (Controlled.moveRight - Controlled.moveLeft); Forces.impulseX += Speed.speed * (Controlled.moveRight - Controlled.moveLeft);
Forces.impulseY += Speed.speed * (Controlled.moveDown - Controlled.moveUp); Forces.impulseY += Speed.speed * (Controlled.moveDown - Controlled.moveUp);

View File

@ -9,9 +9,9 @@ export default class ApplyForces extends System {
} }
tick(elapsed) { tick(elapsed) {
for (const [Position, Forces] of this.select('default')) { for (const {Position, Forces} of this.select('default')) {
Position.x += elapsed * Forces.impulseX; Position.x += elapsed * (Forces.impulseX + Forces.forceX);
Position.y += elapsed * Forces.impulseY; 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 { export default class CalculateAabbs extends System {
static get priority() {
return {
after: 'ApplyForces',
};
}
tick() { tick() {
for (const {Position: {x, y}, VisibleAabb} of this.ecs.changed(['Position'])) { for (const {Position: {x, y}, VisibleAabb} of this.ecs.changed(['Position'])) {
if (VisibleAabb) { if (VisibleAabb) {

View File

@ -1,8 +1,5 @@
import {RESOLUTION} from '@/constants.js'
import {System} from '@/ecs/index.js'; import {System} from '@/ecs/index.js';
const [hx, hy] = [RESOLUTION.x / 2, RESOLUTION.y / 2];
export default class FollowCamera extends System { export default class FollowCamera extends System {
static queries() { static queries() {
@ -19,8 +16,8 @@ export default class FollowCamera extends System {
} }
tick(elapsed) { tick(elapsed) {
for (const [, , entityId] of this.select('default')) { for (const {id} of this.select('default')) {
this.updateCamera(elapsed * 3, this.ecs.get(entityId)); 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 { export default class ResetForces extends System {
static get priority() { static get priority() {
return {phase: 'pre'}; return {phase: 'post'};
} }
static queries() { static queries() {
@ -13,7 +13,7 @@ export default class ResetForces extends System {
} }
tick() { tick() {
for (const [Forces] of this.select('default')) { for (const {Forces} of this.select('default')) {
Forces.impulseX = 0; Forces.impulseX = 0;
Forces.impulseY = 0; Forces.impulseY = 0;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ export default class System {
this.ecs = ecs; this.ecs = ecs;
const queries = this.constructor.queries(); const queries = this.constructor.queries();
for (const i in 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); this.reindex(ecs.entities);
} }