Add simple usage unittest
This commit is contained in:
parent
a984824ec5
commit
8285bde71d
1 changed files with 154 additions and 60 deletions
212
source/ecs/ecs.d
212
source/ecs/ecs.d
|
|
@ -1,36 +1,38 @@
|
||||||
module ecs.ecs;
|
|
||||||
|
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
alias SytemFuncType = void function(ref SystemCallData data, void* componentsStart);
|
alias SytemFuncType = void function(ref SystemCallData data, void* componentsStart);
|
||||||
|
|
||||||
struct HasComponentsStore {
|
struct HasComponentsStore
|
||||||
|
{
|
||||||
ulong[4] bits; //256 components
|
ulong[4] bits; //256 components
|
||||||
|
|
||||||
bool has(HasComponentsStore components) {
|
bool has(HasComponentsStore components)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notIn(HasComponentsStore components) {
|
bool notIn(HasComponentsStore components)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int length() {
|
int length()
|
||||||
|
{
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Informacje o kompnencie
|
// Informacje o kompnencie
|
||||||
struct ComponentInfo {
|
struct ComponentInfo
|
||||||
|
{
|
||||||
int size;
|
int size;
|
||||||
int aligment;
|
int aligment;
|
||||||
SerializeJSON funsSerJ;
|
SerializeJSON funsSerJ;
|
||||||
SerializeBiN funcSerB;
|
SerializeBiN funcSerB;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct System {
|
struct System
|
||||||
|
{
|
||||||
HasComponentsStore requiredComponents;
|
HasComponentsStore requiredComponents;
|
||||||
HasComponentsStore absenComponents;
|
HasComponentsStore absenComponents;
|
||||||
HasComponentsStore maybeComponents;
|
HasComponentsStore maybeComponents;
|
||||||
|
|
@ -39,13 +41,15 @@ struct System {
|
||||||
SytemFuncType func;
|
SytemFuncType func;
|
||||||
}
|
}
|
||||||
// Informacje o systemie dla konkretnego entitiesa
|
// Informacje o systemie dla konkretnego entitiesa
|
||||||
struct SystemCallData {
|
struct SystemCallData
|
||||||
|
{
|
||||||
System* system;
|
System* system;
|
||||||
int[] componentsDt;
|
int[] componentsDt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Informacje o entitiesie danego typu
|
// Informacje o entitiesie danego typu
|
||||||
struct EntityTypeData {
|
struct EntityTypeData
|
||||||
|
{
|
||||||
HasComponentsStore components;
|
HasComponentsStore components;
|
||||||
int[] deltas;
|
int[] deltas;
|
||||||
int totalSize;
|
int totalSize;
|
||||||
|
|
@ -53,21 +57,25 @@ struct EntityTypeData {
|
||||||
SystemCallData[] systems;
|
SystemCallData[] systems;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EntitiesBlock {
|
struct EntitiesBlock
|
||||||
|
{
|
||||||
EntityTypeData* typeData;
|
EntityTypeData* typeData;
|
||||||
Entity* freeEntitySlot;
|
Entity* freeEntitySlot;
|
||||||
EntitiesBlock* nextBlock;
|
EntitiesBlock* nextBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EntityID {
|
struct EntityID
|
||||||
|
{
|
||||||
ulong id = ulong.max;
|
ulong id = ulong.max;
|
||||||
static immutable notUsedValue = EntityID(ulong.max);
|
static immutable notUsedValue = EntityID(ulong.max);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dane konkretnego Entitiesa
|
// Dane konkretnego Entitiesa
|
||||||
struct Entity {
|
struct Entity
|
||||||
|
{
|
||||||
EntityID entityID = EntityID.notUsedValue;
|
EntityID entityID = EntityID.notUsedValue;
|
||||||
union {
|
union
|
||||||
|
{
|
||||||
string name;
|
string name;
|
||||||
Entity* nextFreeSlot;
|
Entity* nextFreeSlot;
|
||||||
}
|
}
|
||||||
|
|
@ -78,12 +86,14 @@ struct Entity {
|
||||||
//ubyte[XX] thereIsComponentsMemory;
|
//ubyte[XX] thereIsComponentsMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Template {
|
struct Template
|
||||||
|
{
|
||||||
HasComponentsStore hasComp;
|
HasComponentsStore hasComp;
|
||||||
Entity* entity;
|
Entity* entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Manager {
|
struct Manager
|
||||||
|
{
|
||||||
EntityAllocator entityArrayAllcoator;
|
EntityAllocator entityArrayAllcoator;
|
||||||
|
|
||||||
ComponentInfo[] components;
|
ComponentInfo[] components;
|
||||||
|
|
@ -92,19 +102,24 @@ struct Manager {
|
||||||
HashMapTwoWays!(string, Entity*) nameMap;
|
HashMapTwoWays!(string, Entity*) nameMap;
|
||||||
HashMapTwoWays!(EntityID, Entity*) idMap;
|
HashMapTwoWays!(EntityID, Entity*) idMap;
|
||||||
|
|
||||||
EntitiesBlock* getEntitiesBlock(HasComponentsStore hasComponents) {
|
EntitiesBlock* getEntitiesBlock(HasComponentsStore hasComponents)
|
||||||
|
{
|
||||||
EntitiesBlock* block = entitiesDatas.get(hasComponents, null);
|
EntitiesBlock* block = entitiesDatas.get(hasComponents, null);
|
||||||
if (block is null) {
|
if (block is null)
|
||||||
|
{
|
||||||
// If such component combination was never present, add it
|
// If such component combination was never present, add it
|
||||||
block = addNewBlock(hasComponents, block);
|
block = addNewBlock(hasComponents, block);
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
// Iterate over list of components until free slot is found or lists ends
|
// Iterate over list of components until free slot is found or lists ends
|
||||||
do {
|
do
|
||||||
if (block.freeEntitySlot !is null) {
|
{
|
||||||
|
if (block.freeEntitySlot !is null)
|
||||||
|
{
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
if (block.nextBlock is null) {
|
if (block.nextBlock is null)
|
||||||
|
{
|
||||||
block = addNewBlock(hasComponents);
|
block = addNewBlock(hasComponents);
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
@ -114,11 +129,14 @@ struct Manager {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EntitiesBlock* addNewBlock(HasComponentsStore hasComponents, EntitiesBlock* firstBlock) {
|
EntitiesBlock* addNewBlock(HasComponentsStore hasComponents, EntitiesBlock* firstBlock)
|
||||||
|
{
|
||||||
// Get last block so order of blocks is preserved, and first blocks are filled first
|
// Get last block so order of blocks is preserved, and first blocks are filled first
|
||||||
EntitiesBlock* lastBlock = firstBlock;
|
EntitiesBlock* lastBlock = firstBlock;
|
||||||
if (lastBlock !is null) {
|
if (lastBlock !is null)
|
||||||
while (lastBlock.nextBlock !is null) {
|
{
|
||||||
|
while (lastBlock.nextBlock !is null)
|
||||||
|
{
|
||||||
lastBlock = lastBlock.nextBlock;
|
lastBlock = lastBlock.nextBlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,32 +144,39 @@ struct Manager {
|
||||||
|
|
||||||
ubyte[] memory = new ubyte[](4096);
|
ubyte[] memory = new ubyte[](4096);
|
||||||
EntitiesBlock* block = cast(EntitiesBlock*) memory.ptr;
|
EntitiesBlock* block = cast(EntitiesBlock*) memory.ptr;
|
||||||
if (lastBlock is null) {
|
if (lastBlock is null)
|
||||||
|
{
|
||||||
EntityTypeData* entityTypeData = newEntityTypeData(hasComponents);
|
EntityTypeData* entityTypeData = newEntityTypeData(hasComponents);
|
||||||
block.typeData = entityTypeData;
|
block.typeData = entityTypeData;
|
||||||
block.nextBlock = null;
|
block.nextBlock = null;
|
||||||
entitiesDatas.add(hasComponents, block);
|
entitiesDatas.add(hasComponents, block);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
lastBlock.nextBlock = block;
|
lastBlock.nextBlock = block;
|
||||||
block.typeData = lastBlock.typeData;
|
block.typeData = lastBlock.typeData;
|
||||||
block.nextBlock = null;
|
block.nextBlock = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void alignNum(ref int num, int aligment) {
|
void alignNum(ref int num, int aligment)
|
||||||
|
{
|
||||||
int reminder = num % aligment;
|
int reminder = num % aligment;
|
||||||
if (reminder != 0) {
|
if (reminder != 0)
|
||||||
|
{
|
||||||
num += aligment - reminder;
|
num += aligment - reminder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTypeData* newEntityTypeData(HasComponentsStore hasComponents) {
|
EntityTypeData* newEntityTypeData(HasComponentsStore hasComponents)
|
||||||
|
{
|
||||||
EntityTypeData* typeData = new EntityTypeData();
|
EntityTypeData* typeData = new EntityTypeData();
|
||||||
typeData.components = hasComponents;
|
typeData.components = hasComponents;
|
||||||
ComponentInfo[] components = getComponentsInfo(hasComponents);
|
ComponentInfo[] components = getComponentsInfo(hasComponents);
|
||||||
typeData.deltas.length = hasComponents.length;
|
typeData.deltas.length = hasComponents.length;
|
||||||
|
|
||||||
foreach (i, comp; components) {
|
foreach (i, comp; components)
|
||||||
|
{
|
||||||
typeData.deltas[i] = typeData.totalSize;
|
typeData.deltas[i] = typeData.totalSize;
|
||||||
typeData.totalAligment.max(comp.aligment);
|
typeData.totalAligment.max(comp.aligment);
|
||||||
typeData.totalSize += comp.size;
|
typeData.totalSize += comp.size;
|
||||||
|
|
@ -159,9 +184,11 @@ struct Manager {
|
||||||
}
|
}
|
||||||
alignNum(typeData.totalSize, typeData.totalAligment);
|
alignNum(typeData.totalSize, typeData.totalAligment);
|
||||||
|
|
||||||
foreach (sys; systems) {
|
foreach (sys; systems)
|
||||||
|
{
|
||||||
if (!typeData.hasComp.has(sys.requiredComponents)
|
if (!typeData.hasComp.has(sys.requiredComponents)
|
||||||
|| !typeData.hasComp.notIn(sys.absenComponents)) {
|
|| !typeData.hasComp.notIn(sys.absenComponents))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
entTypeData.systems ~= sys
|
entTypeData.systems ~= sys
|
||||||
|
|
@ -170,7 +197,8 @@ struct Manager {
|
||||||
return typeData;
|
return typeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addEntity(Template* templ) {
|
void addEntity(Template* templ)
|
||||||
|
{
|
||||||
EntitiesBlock* block = getEntitiesBlock(templ.hasComp);
|
EntitiesBlock* block = getEntitiesBlock(templ.hasComp);
|
||||||
Entity* newEntity = block.freeEntitySlot;
|
Entity* newEntity = block.freeEntitySlot;
|
||||||
block.freeEntitySlot = newEntity.nextFreeSlot;
|
block.freeEntitySlot = newEntity.nextFreeSlot;
|
||||||
|
|
@ -178,21 +206,25 @@ struct Manager {
|
||||||
memcpy(temp.entity, newEntity, block.typeData.totalSize);
|
memcpy(temp.entity, newEntity, block.typeData.totalSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addSystem(Func)(int priority) {
|
void addSystem(Func)(int priority)
|
||||||
|
{
|
||||||
HasComponentsStore requiredComponents;
|
HasComponentsStore requiredComponents;
|
||||||
HasComponentsStore absenComponents;
|
HasComponentsStore absenComponents;
|
||||||
HasComponentsStore maybeComponents;
|
HasComponentsStore maybeComponents;
|
||||||
|
|
||||||
void systemCaller(ref SystemCallData data, void * componentsStart) {
|
void systemCaller(ref SystemCallData data, void * componentsStart)
|
||||||
|
{
|
||||||
Func(cast(FUnc.par1Type)(componentsStart + data.componentsDt[0]),
|
Func(cast(FUnc.par1Type)(componentsStart + data.componentsDt[0]),
|
||||||
cast(FUnc.par1Type)(componentsStart + data.componentsDt[1])...);
|
cast(FUnc.par1Type)(componentsStart + data.componentsDt[1])...);
|
||||||
}
|
}
|
||||||
System* system = new System(&systemCaller, entTypeData);
|
System* system = new System(&systemCaller, entTypeData);
|
||||||
systems ~= system;
|
systems ~= system;
|
||||||
|
|
||||||
foreach (ref entTypeData; entitiesDatas) {
|
foreach (ref entTypeData; entitiesDatas)
|
||||||
|
{
|
||||||
if (!entTypeData.hasComp.has(requiredComponents)
|
if (!entTypeData.hasComp.has(requiredComponents)
|
||||||
|| !entTypeData.hasComp.notIn(absenComponents)) {
|
|| !entTypeData.hasComp.notIn(absenComponents))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
entTypeData.systems ~= system;
|
entTypeData.systems ~= system;
|
||||||
|
|
@ -200,42 +232,104 @@ struct Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void someSystem(CompA a, CompB b, CompC* c) {
|
void someSystem(CompA a, CompB b, CompC* c)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main()
|
||||||
|
{
|
||||||
writeln("Edit source/app.d to start your project.");
|
writeln("Edit source/app.d to start your project.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class System
|
||||||
|
{
|
||||||
|
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class System{
|
|
||||||
|
|
||||||
void start(){
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void end(){
|
void end()
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(ref ObjRend a){
|
void update(ref ObjRend a)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void useEvent(EventData evvv, ref ObjRend a){
|
void useEvent(EventData evvv, ref ObjRend a)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alias SerializeVector = ubyte[];
|
||||||
|
|
||||||
|
__ghsared EntityManager gEntityManager;
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
struct ComponentA
|
||||||
|
{
|
||||||
|
__gshared static int component_id;
|
||||||
|
int a;
|
||||||
|
ulong b;
|
||||||
|
|
||||||
|
static void serializeComponent(ref ComponentA comp, SerializeVector output)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deerializeComponent(ref ComponentA comp, ubyte[] data)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
gEM.addComponet!ComponentA();
|
||||||
|
assert(ComponentA.component_id == 0);
|
||||||
|
ComponentData* ccc = &gEM.componnets[ComponentA.component_id];
|
||||||
|
assert(ccc.totalAligment == 8);
|
||||||
|
assert(ccc.totalSize == 8);
|
||||||
|
|
||||||
|
HasComponentsStore hasComponents;
|
||||||
|
hasComponents.addComponet(ComponentA.component_id);
|
||||||
|
EntityTempalte* tmpl = gEM.allocateTemplate(hasComponents);
|
||||||
|
|
||||||
|
ComponentA* comp = tmpl.getComponent!ComponentA(ComponentA.component_id);
|
||||||
|
comp.a = 111;
|
||||||
|
comp.b = 222;
|
||||||
|
|
||||||
|
gEM.addEntity(tmpl);
|
||||||
|
|
||||||
|
struct SystemAdd
|
||||||
|
{
|
||||||
|
void update(ref ComponentA a)
|
||||||
|
{
|
||||||
|
a.a+=1000;
|
||||||
|
b.b+=2000;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEvent(EventData evvv, ref ComponentA a)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int priority=10;
|
||||||
|
gEM.registerSystem!(SystemAdd)(priority);
|
||||||
|
gEM.updateStepAll();
|
||||||
|
foreach(EntityID id; gEM.IterateByAllEntiteis){
|
||||||
|
assert(id.getComponent(ComponentA.component_id));
|
||||||
|
ComponentA* ccc=id.getComponent(ComponentA.component_id);
|
||||||
|
assert(ccc.a==1111);
|
||||||
|
assert(ccc.b==2222);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue