diff --git a/source/bubel/ecs/entity.d b/source/bubel/ecs/entity.d index f713c78..72131b5 100644 --- a/source/bubel/ecs/entity.d +++ b/source/bubel/ecs/entity.d @@ -128,6 +128,15 @@ export struct EntityTemplate if(T.component_id >= info.tmpl_deltas.length || info.tmpl_deltas[T.component_id] == ushort.max)return null; return cast(T*)(entity_data.ptr + info.tmpl_deltas[T.component_id]); } + + /************************************************************************************************************************ + Get specified component. If component doesn't exist function return null. Returned pointer is valid during EntityTemplate lifetime. + */ + void* getComponent(ushort component_id) const nothrow @nogc + { + if(component_id >= info.tmpl_deltas.length || info.tmpl_deltas[component_id] == ushort.max)return null; + return cast(void*)(entity_data.ptr + info.tmpl_deltas[component_id]); + } } /************************************************************************************************************************ diff --git a/source/bubel/ecs/manager.d b/source/bubel/ecs/manager.d index 1514cbc..e82add3 100644 --- a/source/bubel/ecs/manager.d +++ b/source/bubel/ecs/manager.d @@ -1110,6 +1110,32 @@ export struct EntityManager } } + static void catchEntityFilterFunction(string func_name, RetType = void)(void** member) + { + static if (hasMember!(Sys, func_name)) + { + foreach (func; __traits(getOverloads, Sys, func_name)) + { + static if ((Parameters!(func)).length == 1 + && is(Parameters!(func)[0] == EntityInfo*) + && is(ReturnType!(func) == RetType)) + { + static RetType callFunc(void* system_pointer, EntityInfo* info) + { + Sys* s = cast(Sys*) system_pointer; + static if (is(RetTyp == void)) + mixin("s." ~ func_name ~ "(info)"); + else + return mixin("s." ~ func_name ~ "(info)"); + } + + *member = cast(void*)&callFunc; + break; + } + } + } + } + catchFunction!("onEnable")(&system.m_enable); catchFunction!("onDisable")(&system.m_disable); catchFunction!("onCreate")(&system.m_create); @@ -1121,6 +1147,8 @@ export struct EntityManager catchEntityFunction!("onRemoveEntity")(&system.m_remove_entity); catchEntityFunction!("onChangeEntity")(&system.m_change_entity); + catchEntityFilterFunction!("filterEntity", bool)(&system.m_filter_entity); + system.m_system_pointer = cast(void*) Mallocator.make!Sys; system.m_priority = priority; //(cast(Sys*) system.m_system_pointer).__ecsInitialize(); @@ -2035,6 +2063,9 @@ export struct EntityManager is_: } + ///call Custom Entity Filter test if function exists + if(system.m_filter_entity && !(cast(bool function(void* system_pointer, EntityInfo* info) @nogc nothrow)system.m_filter_entity)(system, &entity))return; + entity.systems[system_id] = true; } @@ -2077,30 +2108,8 @@ export struct EntityManager { System* system = &systems[system_id]; - if (system.m_excluded_components) - { - foreach (id; system.m_excluded_components) - { - foreach (id2; info.components) - { - if (id == id2) - return; - } - } - } - - foreach (id; system.m_components) - { - foreach (i2, id2; info.components) - { - if (id2 == id) - goto is_; - } - return; - is_: - } - - info.systems[system_id] = true; + connectListenerToEntityInfo(info, system_id); + if(!info.systems[system_id])return; uint index = 0; for (; index < passes[system.m_pass].system_callers.length; index++) @@ -3483,6 +3492,12 @@ export struct EntityManager return new_info; } + export bool hasComponent(ushort component_id) + { + if(component_id >= deltas.length || !deltas[component_id])return false; + return true; + } + export ~this() @nogc nothrow { if (components) diff --git a/source/bubel/ecs/system.d b/source/bubel/ecs/system.d index cd8d2e3..e571e8b 100644 --- a/source/bubel/ecs/system.d +++ b/source/bubel/ecs/system.d @@ -194,6 +194,8 @@ package: void* m_remove_entity; void* m_change_entity; + void* m_filter_entity; + //void function(ref EntityManager.CallData data) m_initialize; //void function(ref EntityManager.CallData data) m_deinitilize; void* m_initialize; diff --git a/tests/basic.d b/tests/basic.d index 59f88e2..4a9dc6f 100644 --- a/tests/basic.d +++ b/tests/basic.d @@ -139,6 +139,7 @@ void afterEveryTest() unittest { EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CFloat.component_id, CFlag.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_); Entity* entity = gEM.addEntity(tmpl_); EntityMeta meta = entity.getMeta(); assert(meta.hasComponent(CInt.component_id)); @@ -1615,4 +1616,107 @@ unittest assert(pass.system_callers[3].dependencies[2].system_id == TestSystem3.system_id); assert(pass.system_callers[4].dependencies[0].system_id == TestSystem2.system_id); assert(pass.system_callers[4].dependencies[1].system_id == TestSystem4.system_id); -} \ No newline at end of file +} + + +@("CustomFilter") +unittest +{ + struct TestSystem + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @optional CInt[] int_; + @optional CLong[] long_; + @optional CFloat[] float_; + @optional CDouble[] double_; + } + + bool filterEntity(EntityManager.EntityInfo* info) + { + if(!info.hasComponent(CInt.component_id))return false; + int one_from = 0; + if(info.hasComponent(CLong.component_id))one_from++; + if(info.hasComponent(CFloat.component_id))one_from++; + if(info.hasComponent(CDouble.component_id))one_from++; + if(one_from == 1)return true; + return false; + } + + void onUpdate(EntitiesData entities) + { + updates++; + } + + uint updates = 0; + } + + struct TestSystem2 + { + mixin ECS.System; + + struct EntitiesData + { + uint length; + @optional CInt[] int_; + @optional CLong[] long_; + @optional CFloat[] float_; + @optional CDouble[] double_; + } + + bool filterEntity(EntityManager.EntityInfo* info) + { + if(info.hasComponent(CInt.component_id) && info.hasComponent(CFloat.component_id) && !info.hasComponent(CLong.component_id) && !info.hasComponent(CDouble.component_id))return true; + if(info.hasComponent(CLong.component_id) && info.hasComponent(CDouble.component_id) && !info.hasComponent(CInt.component_id) && !info.hasComponent(CFloat.component_id))return true; + return false; + } + + void onUpdate(EntitiesData entities) + { + updates++; + } + + uint updates = 0; + } + + gEM.beginRegister(); + + gEM.registerSystem!TestSystem(0); + gEM.registerSystem!TestSystem2(1); + + gEM.endRegister(); + + + EntityTemplate* tmpl_ = gEM.allocateTemplate([CInt.component_id, CLong.component_id, CFloat.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_); + EntityTemplate* tmpl_2 = gEM.allocateTemplate([CInt.component_id, CFloat.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_2); + EntityTemplate* tmpl_3 = gEM.allocateTemplate([CLong.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_3); + EntityTemplate* tmpl_4 = gEM.allocateTemplate([CInt.component_id, CLong.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_4); + EntityTemplate* tmpl_5 = gEM.allocateTemplate([CInt.component_id, CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_5); + EntityTemplate* tmpl_6 = gEM.allocateTemplate([CDouble.component_id].staticArray); + scope(exit)gEM.freeTemplate(tmpl_6); + + gEM.addEntity(tmpl_); + gEM.addEntity(tmpl_2); + gEM.addEntity(tmpl_3); + gEM.addEntity(tmpl_4); + gEM.addEntity(tmpl_5); + gEM.addEntity(tmpl_6); + + TestSystem* test_system = gEM.getSystem!TestSystem; + TestSystem2* test_system2 = gEM.getSystem!TestSystem2; + + gEM.begin(); + gEM.update(); + gEM.end(); + + assert(test_system.updates == 2); + assert(test_system2.updates == 2); +}