From 7bc07666d010ef9cfe7d66a8ca10fbf7362f3a28 Mon Sep 17 00:00:00 2001 From: Dawid Masiukiewicz Date: Sat, 2 May 2020 21:11:22 +0000 Subject: [PATCH] Initial release README.md --- README.md | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d3f6816..abc9709 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,180 @@ -# Dynamic Entity Component System +# Bubel Entity Component System [![pipeline status](https://gitlab.com/Mergul/bubel-ecs/badges/master/pipeline.svg)](https://gitlab.com/Mergul/bubel-ecs/-/commits/master) [![codecov](https://codecov.io/gl/Mergul/bubel-ecs/branch/master/graph/badge.svg?token=Unm0TJhFoW)](https://codecov.io/gl/Mergul/bubel-ecs) -Entity-Component-System implementation in D language. +BubelECS is Entity-Component-System architecture implementation in D language. +Library aims to delivery fast and flexible architecture for developing games. Library is @nogc and betterC compatible. Even Emscripten build works very well. +Important goal is to keep code without any external dependencies, i. eg. multithreading support don't contain any parallel execution +but emit jobs with delegate and array of dependencies. + +BubelECS was tested on Linux, Windows, Android and WASM. + +**Currently library is in beta stage so some significant API changes can appear.** + +## Design + +For core information about Entity-Component-System architectural pattern I recommend to read definition described at [Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system). + +Main design principles are: + + * **Data oriented design** - components memory is packed into tight block so iterating over entity components is cache friendly + * **Fast and safe EntityID** - every entity is referenced by its unique ID. If entity was destroyed EntityManager will return null pointer for given EntityID. Access to entity from ID is linear operation. + * **Multithreading support** - whole library was developed with multithreading in mind. Adding components is thread-friendly so you get usable EntityID as early as possible. Operations like adding or removing components are deferred to end of frame. Dependencies between systems update are generated automatically. + * **Good logic separation** - system needs information only about components which it's use, there is no relation between systems. Systems can be compiled as multiple separate libraries and used together. + * **Easy systems ordering** - systems are ordered by single priority value. Order of execution of systems with same priority is undefined but order for different priorites are always preserved. + * **Flexible entity to system assignment** - system iterate over any entity that has chosen components. Components can be marked as optional so isn't required but can be used by system. Additionaly system can contain list of components which makes entity excluded from calculation. + * **Builtin events handling** - along with EntityManager library contain EventsManager which makes easier to communicate between multiple entities. + * **Hot-reload** - hot-reloading for systems should be as easy as possible. **Currently not achived yet (WIP!)**. + +Currently library is incredibly fast to iterate over entities and fast enought in direct access to entity component if it's needed. \ +There are some assumptions that should be considered when developing application: + + * Iterating over same components is incredibly fast so it's should be main way of making calculations. + * Using of direct access and events should be used very wisely and not too much. + * Direct component access is faster than events, because events must buffer memory and call multiple system handler callbacks. + * Components can be used to marking entities, assingment to systems and changing logic of entity in runtime. But due to memory fragmentation there sould by many of entities with same type. In other words, sometimes making to many components can lead to performence drop even if adding or removing components is fast enough. + * Systems give great opporunity to separate game logic. Systems can be enabled and disabled easly in runtime. It's can be used to enabling some debug systems if needed. Additionaly this concept makes game logic changes easier to deal with. If you develop your appliactiona wisely it should be trivial to change some core logic by changing only one system, or adding new system. Every entity can easily takes some behaviour from different entity type by adding several components. + +### Features + + * ECS architectural pattern + * Data oriented design + * Safe identifiers (EntityID) + * EntityTemplates - used to add entities, contain list of components and it's data + * Basic events handling + * Easy systems ordering + * Automatic multithreaded jobs generating + * Runtime and fast components adding and removing + * Listeners for adding and removing entity components inside systems + * Passes - systems are grouped to passes. Pass update call update callback of assigned systems. + * Calling custom callbacks for system entity groups + * betterC compatibility + * Emscripten compatibility + +### Planned + + * Worlds - every world works as separate environment. Entities don't with entities from different world. Systems and components should be registered for every world separately. + * Hot-reload support - currently only reloading systems (their functions) works and only if code changes apperas only inside functions. There is possibility to serialize every entity and system, but it's should be provided by library itself with special callbacks and helpers. Additionaly planned is system which helps with taking new EntityID from serialized identifiers. + * External dependencies - ability to provide dependencies between system which isn't related to components. + * Static components - this components will have different memory model and can be accessed only directly. It will be slower with access but don't trigger memory copy when component is added or some different entity was destroyed. It should fix problem with big components which isn't needed by systems iteration callbacks or is required rarely. + * Better EventManager - currently implementy event handling system isn't it what I wanted. I'm not sure exacly what I can improve but multithreaded event execution is one of things which I considered and it probably will work. + * More demos and examples - demo appliaction is very basic now, but in future I planned more minigames and sanbox mode (opportunity to mix many components and systems). I planed some examples to show how to use basic functionality. + * C API - this is currenly in consideration. Everything is dependent on my free time and amount of work required to create good C API. + * More smaller improvement in draft stage... + +For more information about design and usage I recommend to read [documentation](https://mergul.gitlab.io/bubel-ecs/ecs.html)**(WIP)**. + +## Build Instructions + +To build library you needs recent D compiler and optionally Emscripten (with python) to build web version. Currenlty tested are: LDC, DMD and GDC. \ +Supported build systems are DUB and Meson. + +##### DUB +```shell +#available configurations: library, dynlib, library-betterC, dynlib-betterC +dub build -c library -b release +``` + +##### Meson +```shell +#use '-DbetterC=true ' to build betterC code +meson build . #add '--buildtype=release' to build release code +cd build +ninja +``` + +##### Emscripten +```shell +python compile_wasm.py -opt +``` + +For more detailed build options please check documentation for used build system. + +## Demos + +Repository contain demo application. You can check demo [online](https://mergul.gitlab.io/bubel-ecs/ecs_demo.html) or build it form source code using DUB. \ +Online demo support multithreading and page tries to check if client support WASM multithreading or not and loads properly JS and WASM code. It was tested on Chrome, Firefox, Opera, Brave on Linux and Android. On firefox there is problem with multithreaded version so if demo don't works please try to disable shared_memory in browser flags. + +## Code example + +```d + +struct Position +{ + mixin ECS.Components; //makes struct component + float x; + float y; +} + +struct Velocity +{ + mixin ECS.Components; + //default values works + float x = 0.1; + float y = 1; +} + +struct StaticFlag +{ + mixin ECS.Components; +} + +struct UpdateSystem +{ + mixin ECS.System; //makes struct system + + ECS.ExcludedComponents!(StaticFlag); //prevents static entities from update + + struct EntitiesData + { + int length; //entities count + @readonly Entity[] entities; //entities arrays, entity contain ID only + Position[] positions; //positions array + @readonly Velocity[] velocities; //veocities array, readonly (Multithreading tag) + } + + void onUpdate(EntitiesData data) //update callback, called multiple times per frame for every entities types + { + foreach(i; 0..data.length) //iterate over entities + { + data.positions[i].x += data.velocities[i].x * dt; + data.positions[i].y += data.velocities[i].y * dt; + } + } +} + +void main() +{ + manager.beginRegister(); + //register components + manager.registerComponent!Position; + manager.registerComponent!Velocity; + manager.registerComponent!StaticFlag; + //register system with priority 0 + manager.registerSystem!UpdateSystem(0); + manager.endRegister(); + + //allocate template + EntityTemplate* tmpl = manager.allocateEmplate([Velocity.component_id, Position.component_id].staticArray); + scope (exit) manager.freeTemplate(tmpl); + + //gets pointer to template component data + Position* position = tmpl.getComponent!Position; + foreach(i; 0 .. 100) + { + position.x = i % 10; + position.y = i / 10; + manager.addEntity(tmpl); + } + + manager.begin(); //start frame + manager.update(); //update all systems, there onUpdate callbacks are called + manager.end(); //end frame +} + +``` + +## Links + +Documentation: https://mergul.gitlab.io/bubel-ecs/ecs.html \ +Online demo: https://mergul.gitlab.io/bubel-ecs/ecs_demo.html