2022-09-15 12:51:46 -05:00
|
|
|
import {expect} from 'chai';
|
|
|
|
|
2022-09-17 04:15:16 -05:00
|
|
|
import Bundle from '../src/bundle';
|
2022-09-15 12:51:46 -05:00
|
|
|
import Ecs from '../src/ecs';
|
|
|
|
import System from '../src/system';
|
|
|
|
|
2022-09-15 14:27:57 -05:00
|
|
|
const Empty = {};
|
2022-09-15 12:51:46 -05:00
|
|
|
|
2022-09-15 14:27:57 -05:00
|
|
|
const Position = {
|
|
|
|
x: {type: 'int32', defaultValue: 32},
|
|
|
|
y: 'int32',
|
|
|
|
z: 'int32',
|
|
|
|
};
|
2022-09-15 12:51:46 -05:00
|
|
|
|
2022-09-17 09:09:41 -05:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
|
2022-09-15 12:51:46 -05:00
|
|
|
it('can create entities with components', () => {
|
|
|
|
const ecs = new Ecs({Empty, Position});
|
2022-09-15 14:09:10 -05:00
|
|
|
const entity = ecs.create({Empty: {}, Position: {y: 420}});
|
2022-09-15 12:51:46 -05:00
|
|
|
expect(JSON.stringify(ecs.get(entity)))
|
|
|
|
.to.deep.equal(JSON.stringify({Empty: {}, Position: {x: 32, y: 420, z: 0}}));
|
|
|
|
});
|
|
|
|
|
2022-09-17 09:09:59 -05:00
|
|
|
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: {}}));
|
|
|
|
});
|
|
|
|
|
2022-09-17 07:37:02 -05:00
|
|
|
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}}));
|
|
|
|
});
|
|
|
|
|
2022-09-17 07:25:54 -05:00
|
|
|
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}}));
|
|
|
|
});
|
|
|
|
|
2022-09-15 12:51:46 -05:00
|
|
|
it('can tick systems', () => {
|
2022-09-15 14:27:57 -05:00
|
|
|
const Momentum = {
|
|
|
|
x: 'int32',
|
|
|
|
y: 'int32',
|
|
|
|
z: 'int32',
|
|
|
|
};
|
2022-09-15 12:51:46 -05:00
|
|
|
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);
|
2022-09-15 14:09:10 -05:00
|
|
|
const entity = ecs.create({Momentum: {}, Position: {y: 420}});
|
2022-09-15 12:51:46 -05:00
|
|
|
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;
|
|
|
|
});
|
|
|
|
|
2022-09-17 09:09:59 -05:00
|
|
|
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;
|
|
|
|
});
|
|
|
|
|
2022-09-15 12:51:46 -05:00
|
|
|
it('can encode and decode an ecs', () => {
|
|
|
|
const ecs = new Ecs({Empty, Position});
|
2022-09-15 14:09:10 -05:00
|
|
|
const entity = ecs.create({Empty: {}, Position: {y: 420}});
|
2022-09-15 12:51:46 -05:00
|
|
|
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;
|
|
|
|
});
|
2022-09-17 04:15:16 -05:00
|
|
|
|
|
|
|
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);
|
|
|
|
});
|