avocado/packages/ecs/test/ecs.js

232 lines
5.8 KiB
JavaScript

import {expect} from 'chai';
import Bundle from '../src/bundle';
import Ecs from '../src/ecs';
import System from '../src/system';
const Empty = {};
const Position = {
x: {type: 'int32', defaultValue: 32},
y: 'int32',
z: 'int32',
};
it('can add and remove systems at runtime', () => {
const ecs = new Ecs();
let oneCount = 0;
let twoCount = 0;
const oneSystem = () => {
oneCount++;
};
ecs.addSystem(oneSystem);
ecs.tick();
expect(oneCount)
.to.equal(1);
const twoSystem = () => {
twoCount++;
};
ecs.addSystem(twoSystem);
ecs.tick();
expect(oneCount)
.to.equal(2);
expect(twoCount)
.to.equal(1);
ecs.removeSystem(oneSystem);
ecs.tick();
expect(oneCount)
.to.equal(2);
expect(twoCount)
.to.equal(2);
});
it('can create entities with components', () => {
const ecs = new Ecs({Empty, Position});
const entity = ecs.create({Empty: {}, Position: {y: 420}});
expect(JSON.stringify(ecs.get(entity)))
.to.deep.equal(JSON.stringify({Empty: {}, Position: {x: 32, y: 420, z: 0}}));
});
it("can remove entities' components", () => {
const ecs = new Ecs({Empty, Position});
const entity = ecs.create({Empty: {}, Position: {y: 420}});
ecs.remove(entity, ['Position']);
expect(JSON.stringify(ecs.get(entity)))
.to.deep.equal(JSON.stringify({Empty: {}}));
});
it('can get entities unsafely', () => {
const ecs = new Ecs({Empty, Position});
const entity = ecs.create({Empty: {}, Position: {y: 420}});
expect(JSON.stringify(ecs.getUnsafe(entity)))
.to.deep.equal(JSON.stringify({Empty: {}, Position: {x: 32, y: 420, z: 0}}));
});
it('can insert components into entities', () => {
const ecs = new Ecs({Empty, Position});
const entity = ecs.create({Empty: {}});
ecs.insert(entity, {Position: {y: 420}});
expect(JSON.stringify(ecs.get(entity)))
.to.deep.equal(JSON.stringify({Empty: {}, Position: {x: 32, y: 420, z: 0}}));
ecs.insert(entity, {Position: {y: 69}});
expect(JSON.stringify(ecs.get(entity)))
.to.deep.equal(JSON.stringify({Empty: {}, Position: {x: 32, y: 69, z: 0}}));
});
it('can tick systems', () => {
const Momentum = {
x: 'int32',
y: 'int32',
z: 'int32',
};
const ecs = new Ecs({Momentum, Position});
class Physics extends System {
static queries() {
return {
default: ['Position', 'Momentum'],
};
}
tick(elapsed) {
expect(elapsed)
.to.equal(1);
// eslint-disable-next-line no-restricted-syntax
for (const [position, momentum] of this.select('default')) {
position.x += momentum.x * elapsed;
position.y += momentum.y * elapsed;
position.z += momentum.z * elapsed;
}
}
}
ecs.addSystem(Physics);
const entity = ecs.create({Momentum: {}, Position: {y: 420}});
const position = JSON.stringify(ecs.get(entity).Position);
ecs.tick(1);
expect(JSON.stringify(ecs.get(entity).Position))
.to.deep.equal(position);
ecs.get(1).Momentum.y = 30;
ecs.tick(1);
expect(JSON.stringify(ecs.get(entity).Position))
.to.deep.equal(JSON.stringify({x: 32, y: 450, z: 0}));
});
it('can create entities when ticking systems', () => {
const ecs = new Ecs();
class Spawn extends System {
tick() {
this.createEntity();
}
}
ecs.addSystem(Spawn);
ecs.create();
expect(ecs.get(2))
.to.be.undefined;
ecs.tick(1);
expect(ecs.get(2))
.to.not.be.undefined;
});
it('can schedule entities to be deleted when ticking systems', () => {
const ecs = new Ecs();
class Despawn extends System {
tick() {
this.destroyEntity(1);
}
}
ecs.addSystem(Despawn);
ecs.createExact(1);
ecs.tick(1);
expect(ecs.get(1))
.to.not.be.undefined;
ecs.tickFinalize();
expect(ecs.get(1))
.to.be.undefined;
});
it('can add components to and remove components from entities when ticking systems', () => {
const Foo = {bar: 'uint8'};
const ecs = new Ecs({Foo});
class AddComponent extends System {
tick() {
this.insertComponents(1n, {Foo: {}});
}
}
class RemoveComponent extends System {
tick() {
this.removeComponents(1n, ['Foo']);
}
}
ecs.addSystem(AddComponent);
ecs.createExact(1);
ecs.tick(1);
expect(ecs.get(1).Foo)
.to.not.be.undefined;
ecs.removeSystem(AddComponent);
ecs.addSystem(RemoveComponent);
ecs.tick(1);
expect(ecs.get(1).Foo)
.to.be.undefined;
});
it('can encode and decode an ecs', () => {
const ecs = new Ecs({Empty, Position});
const entity = ecs.create({Empty: {}, Position: {y: 420}});
const view = new DataView(new ArrayBuffer(1024));
ecs.encode([[entity, true]], view);
const newEcs = new Ecs({Empty, Position});
newEcs.decode(view);
expect(JSON.stringify(newEcs.get(entity)))
.to.deep.equal(JSON.stringify(ecs.get(entity)));
ecs.setClean();
ecs.encode([[entity, true]], view);
const newEcs2 = new Ecs({Empty, Position});
newEcs2.decode(view);
expect(newEcs2.get(entity))
.to.be.undefined;
ecs.encode([entity], view);
newEcs2.decode(view);
expect(newEcs2.get(entity))
.to.not.be.undefined;
});
it('can add bundles', () => {
const Height = {height: 'uint32'};
const Width = {width: 'uint32'};
const Area = ['Height', 'Width'];
class ConfiguredArea extends Bundle {
static configure({height, width}) {
return {Height: {height}, Width: {width}};
}
static Components = ['Height', 'Width'];
}
const ecs = new Ecs({
Area,
ConfiguredArea,
Height,
Width,
});
const entity = ecs.get(ecs.create({Area: {Width: {width: 420}}}));
expect(entity.Height.height)
.to.equal(0);
expect(entity.Width.width)
.to.equal(420);
const configuredEntity = ecs.get(ecs.create({ConfiguredArea: {width: 420}}));
expect(configuredEntity.Height.height)
.to.equal(0);
expect(configuredEntity.Width.width)
.to.equal(420);
});