Merge branch 'beta-release' into 'master'

Changes for beta release

See merge request Mergul/bubel-ecs!17
This commit is contained in:
Dawid Masiukiewicz 2021-03-06 22:51:11 +00:00
commit addb9095f5
16 changed files with 187 additions and 94 deletions

View file

@ -2,11 +2,11 @@
[![pipeline status](https://gitlab.com/Mergul/bubel-ecs/badges/master/pipeline.svg)](https://gitlab.com/Mergul/bubel-ecs/-/commits/master) [![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) [![codecov](https://codecov.io/gl/Mergul/bubel-ecs/branch/master/graph/badge.svg?token=Unm0TJhFoW)](https://codecov.io/gl/Mergul/bubel-ecs)
BubelECS is Entity-Component-System architectural pattern implementation in D language. **Bubel ECS** is Entity-Component-System architectural pattern implementation in D language.
Library aims to delivery fast and flexible architecture for developing games. Library is **@nogc** and **betterC** compatible. WASM is supported thorugh Emscripten. Library aims to delivery fast and flexible architecture for developing games. Library is **@nogc** and **betterC** compatible. WASM is supported thorugh Emscripten.
Project haven't any external dependencies. Project haven't any external dependencies.
BubelECS was tested on Linux, Windows, Android and WASM. Bubel ECS was tested on Linux, Windows, Android and WASM.
**Currently library is in beta stage so some significant API changes can appear.** **Currently library is in beta stage so some significant API changes can appear.**
@ -16,21 +16,21 @@ For core information about Entity-Component-System architectural pattern please
Main design principles are: Main design principles are:
* **Data oriented design** - components memory is packed into tight block so iterating over entity components is cache friendly * **Data oriented design** - components memory is packed into tight blocks so iterating over entity components is cache friendly
* **Fast and safe EntityID** - every entity is referenced by its unique ID. Accessing by ID is safe even if entity don'y exist. Access by ID is constant time operation. * **Fast and safe EntityID** - every entity is referenced by its unique ID. Accessing by ID is safe even if entity doesn't exists. Accessing by ID is constant time 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 parallel execution are generated automatically. * **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 parallel execution 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. * **Good logic separation** - system needs information only about components which it uses, there is no relation between systems. Systems can be compiled as multiple separate libraries and used together.
* **Flexible execution model** - system iterate over entities which meet specified conditions. Components can be marked as required, optional or excluded. Systems are exectued in specific order determined by system priority. * **Flexible execution model** - system iterate over entities which meet specified conditions. Components can be marked as required, optional or excluded. Systems are exectued in specific order determined by system priority.
* **Builtin events handling** - library has builtin support for event handlig to makes easier communication between different entities. * **Builtin events handling** - library has builtin support for event handlig to makes easier communication between different entities.
* **Hot-reload** - hot-reloading for systems should be as easy as possible. In other words library should give functionality to support hot-reload of systems and components with minimal work. **(WIP!)**. * **Hot-reload** - hot-reloading for systems should be as easy as possible. In other words library should give functionality to support hot-reload of systems and components with minimal work. **(WIP!)**.
There are some assumptions that should be considered when developing application: There are some assumptions that should be considered when developing application:
* Iterating over components is fastest way of access data so it's should be main way of making calculations. * Iterating over components throught system ```onUpdate()``` callback is fastest way of access data so it's should be main way of making calculations.
* Using of direct access and events should be used very wisely and minimized. * Using direct access and events should be used very wisely and minimized.
* Direct component access is faster than events, because events must buffer memory and call multiple system handler callbacks. * 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. Using too much component based marking can lead to memory fragmentation and performence drop. * Components can be used to marking entities, assingment to systems and changing logic of entity in runtime. Using too much component based marking can lead to memory fragmentation and performence drop.
* 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 application wisely it should be trivial to change some core logic by changing only several systems or adding some new systems. Every entity can easily takes some behaviour from different entity type by adding several components. * 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 application wisely it should be trivial to change some core logic by changing only several systems or adding some new. Every entity can easily takes some behaviour from different entity type by adding several components.
### Features ### Features
@ -40,26 +40,27 @@ There are some assumptions that should be considered when developing application
* EntityTemplates * EntityTemplates
* Basic events handling * Basic events handling
* Easy systems ordering * Easy systems ordering
* Automatic multithreaded jobs generating
* Runtime and fast components adding and removing * Runtime and fast components adding and removing
* Listeners for adding and removing entity components inside systems * Listeners for adding and removing entity components inside systems
* Update passes * Update passes
* Calling custom callbacks for system entity groups * Calling custom callbacks for system entity groups
* betterC compatibility * betterC compatibility
* Emscripten compatibility * Emscripten compatibility
* Automatic multithreaded jobs generation
* External dependencies - ability to provide dependencies between systems which aren't related to components
* Compatibility with all big compilers: DMD, LDC, GDC.
### Planned ### 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. * Worlds - every world works as separate environment. Entities don't interact 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. 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. * Hot-reload support - currently only reloading systems (their functions) works. 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 to access but won't trigger memory copy when component is added. It should fix problem with big components which isn't needed by systems iteration callbacks or is required rarely. * Static components - this components will have different memory model and can be accessed only directly. It will be slower to access but won't trigger memory copy when component is added. It should fix problem with big components which isn't needed by systems iteration callbacks or is required rarely.
* Better EventManager - there are several optimization and improvements that can be added in the future. * Better EventManager - there are several optimization and improvements that can be added in the future (e.g. multithreading).
* More demos and examples - demo appliaction is very basic now, but in future more minigames and sanbox mode (opportunity to mix many components and systems) are planned. * More demos and examples - demo appliaction is very basic now, but in future more minigames and sanbox mode (opportunity to mix many components and systems) are planned.
* C API - in highly depends on amount of work required. Makes possible to use library from different languages. * C API - it's highly depends on amount of work required. Makes it possible to use library from different languages.
* More smaller improvements... * More smaller improvements...
For more information about design and usage feel free to read [documentation](https://mergul.gitlab.io/bubel-ecs/ecs.html)**(WIP)** and [WIKI](https://gitlab.com/Mergul/bubel-ecs/-/wikis/home). For more information about design and usage feel free to read [documentation](https://mergul.gitlab.io/bubel-ecs/ecs.html) and [WIKI](https://gitlab.com/Mergul/bubel-ecs/-/wikis/home)**(WIP)**.
## Build Instructions ## Build Instructions
@ -68,7 +69,7 @@ Supported build systems are DUB and Meson.
##### DUB ##### DUB
```shell ```shell
#available configurations: library, dynlib, library-betterC, dynlib-betterC #available configurations: library, dynlib, library-betterC, dynlib-betterC, unittest-runner, unittest-runner-betterC
dub build -c library -b release dub build -c library -b release
``` ```
@ -90,12 +91,22 @@ python compile_wasm.py -opt
adrdox -i source/bubel/ecs/ -o docs/ -s skeleton.html adrdox -i source/bubel/ecs/ -o docs/ -s skeleton.html
``` ```
For more detailed build options please check documentation for used build system.
## Demos ## 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. \ 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 Meson. \
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. Online demo support multithreading and page tries to check if client support WASM multithreading or not and loads proper JS and WASM code. It was tested on Chrome, Firefox, Opera, Brave on Linux, Windows and Android. On firefox there is problem with multithreaded version so if demo doesn't work please try to disable shared_memory in browser flags.
Demos uses these libraries: SDL2, SDL2_Image, bindbc-loader, bindbc-sdl, cimgui, glad, mmutils. \
C++ compiler is required for building dependencies.
Meson is preferred way of building demos. It will download and compile dependencies automatically. DUB version is used only for library development. Build instructions:
```shell
#use '-DbetterC=true ' to build betterC code
meson build . -DBuildDemos=true #add '--buildtype=release' to build release code
cd build
ninja
```
## Code example ## Code example
@ -189,4 +200,5 @@ void main()
## Links ## Links
Documentation: https://mergul.gitlab.io/bubel-ecs/ecs.html \ Documentation: https://mergul.gitlab.io/bubel-ecs/ecs.html \
Wiki: https://gitlab.com/Mergul/bubel-ecs/-/wikis/home \
Online demo: https://mergul.gitlab.io/bubel-ecs/ecs_demo.html Online demo: https://mergul.gitlab.io/bubel-ecs/ecs_demo.html

View file

@ -58,7 +58,7 @@ struct CBall
{ {
mixin ECS.Component; mixin ECS.Component;
ubyte radius; //ubyte radius;
} }
struct CHitPoints struct CHitPoints
@ -394,12 +394,15 @@ void brickBreakerStart()
launcher.gui_manager.addComponent(CTexCoords(), "Tex Coords"); launcher.gui_manager.addComponent(CTexCoords(), "Tex Coords");
launcher.gui_manager.addComponent(CTexCoordsIndex(), "Tex Coords Index"); launcher.gui_manager.addComponent(CTexCoordsIndex(), "Tex Coords Index");
launcher.gui_manager.addComponent(CVelocity(), "Velocity"); launcher.gui_manager.addComponent(CVelocity(), "Velocity");
launcher.gui_manager.addComponent(CInput(), "Velocity"); launcher.gui_manager.addComponent(CInput(), "Input");
launcher.gui_manager.addComponent(CPaddle(), "Paddle");
launcher.gui_manager.addComponent(CDamping(), "Damping"); launcher.gui_manager.addComponent(CDamping(), "Damping");
launcher.gui_manager.addComponent(CBall(), "Ball"); launcher.gui_manager.addComponent(CBall(), "Ball");
launcher.gui_manager.addComponent(CBVH(), "BVH"); launcher.gui_manager.addComponent(CBVH(), "BVH");
launcher.gui_manager.addComponent(CAABB(), "AABB"); launcher.gui_manager.addComponent(CAABB(), "AABB");
launcher.gui_manager.addComponent(CStatic(), "Static Flag"); launcher.gui_manager.addComponent(CStatic(), "Static Flag");
launcher.gui_manager.addComponent(CVelocityFactor(), "Velocity Factor");
launcher.gui_manager.addComponent(CHitPoints(), "Hit Points");
launcher.gui_manager.addSystem(becsID!MoveSystem, "Move System"); launcher.gui_manager.addSystem(becsID!MoveSystem, "Move System");
launcher.gui_manager.addSystem(becsID!EdgeCollisionSystem, "Edge Collision System"); launcher.gui_manager.addSystem(becsID!EdgeCollisionSystem, "Edge Collision System");

View file

@ -402,12 +402,20 @@ void particlesStart()
launcher.gui_manager.addSystem(becsID!MouseAttractSystem,"Mouse Attract System"); launcher.gui_manager.addSystem(becsID!MouseAttractSystem,"Mouse Attract System");
launcher.gui_manager.addSystem(becsID!DampingSystem,"Damping System"); launcher.gui_manager.addSystem(becsID!DampingSystem,"Damping System");
launcher.gui_manager.addSystem(becsID!ParticleLifeSystem,"Particle Life System"); launcher.gui_manager.addSystem(becsID!ParticleLifeSystem,"Particle Life System");
launcher.gui_manager.addSystem(becsID!GravitySystem,"Gravity System");
// launcher.gui_manager.addComponent(CColor(),"Color (white)"); // launcher.gui_manager.addComponent(CColor(),"Color (white)");
// launcher.gui_manager.addComponent(CColor(0xFF101540),"Color (red)"); // launcher.gui_manager.addComponent(CColor(0xFF101540),"Color (red)");
// launcher.gui_manager.addComponent(CColor(0xFF251010),"Color (blue)"); // launcher.gui_manager.addComponent(CColor(0xFF251010),"Color (blue)");
// launcher.gui_manager.addComponent(CColor(0xFF102010),"Color (green)"); // launcher.gui_manager.addComponent(CColor(0xFF102010),"Color (green)");
launcher.gui_manager.addComponent(CColor(0xFF101540),"Color"); launcher.gui_manager.addComponent(CLocation(),"Location");
launcher.gui_manager.addComponent(CScale(),"Scale");
launcher.gui_manager.addComponent(CTexCoords(),"Texture Coords");
launcher.gui_manager.addComponent(CTexCoordsIndex(),"Texture Coords Index");
launcher.gui_manager.addComponent(CRotation(),"Rotation");
launcher.gui_manager.addComponent(CDepth(),"Depth");
launcher.gui_manager.addComponent(CMaterialIndex(),"Material ID");
launcher.gui_manager.addComponent(CVelocityFactor(),"Velocity Factor");
launcher.gui_manager.addComponent(CAttractor(0.1),"Attractor"); launcher.gui_manager.addComponent(CAttractor(0.1),"Attractor");
launcher.gui_manager.addComponent(CForceRange(vec2(5,40)),"ForceRange"); launcher.gui_manager.addComponent(CForceRange(vec2(5,40)),"ForceRange");
launcher.gui_manager.addComponent(CVelocity(),"Velocity"); launcher.gui_manager.addComponent(CVelocity(),"Velocity");

View file

@ -19,6 +19,7 @@ import ecs_utils.utils;
import game_core.basic; import game_core.basic;
import gui.attributes;
//import std.array : staticArray; //import std.array : staticArray;
enum float px = 1.0/512.0; enum float px = 1.0/512.0;
@ -133,7 +134,7 @@ struct CAnimation
mixin ECS.Component; mixin ECS.Component;
vec4[] frames; vec4[] frames;
float time = 0; @GUIRangeF(0,float.max)float time = 0;
} }
//CIlocation is integer location used as grid cell coordination //CIlocation is integer location used as grid cell coordination
@ -203,7 +204,7 @@ struct CSnake
} }
Parts parts; Parts parts;
CMovement.Direction direction; @GUIRange(0,3)CMovement.Direction direction;
} }
//flag for apple //flag for apple
@ -241,7 +242,7 @@ struct CMovement
right right
} }
Direction direction; @GUIRange(0,3)Direction direction;
} }
// struct CInput // struct CInput
@ -373,8 +374,10 @@ struct AnimationRenderSystem
draw_data.depth = -1; draw_data.depth = -1;
foreach(i;0..data.length) foreach(i;0..data.length)
{ {
uint frame = cast(uint)(data.animation[i].time);
if(frame >= data.animation[i].frames.length)frame = cast(uint)data.animation[i].frames.length - 1;
draw_data.position = data.location[i]; draw_data.position = data.location[i];
draw_data.coords = data.animation[i].frames[cast(int)(data.animation[i].time)]; draw_data.coords = data.animation[i].frames[frame];
launcher.renderer.draw(draw_data); launcher.renderer.draw(draw_data);
} }
} }
@ -910,8 +913,9 @@ void snakeStart()
launcher.gui_manager.addComponent(CParticleVector(vec2(0,1)),"Particle Vector"); launcher.gui_manager.addComponent(CParticleVector(vec2(0,1)),"Particle Vector");
launcher.gui_manager.addComponent(CInput(),"Input"); launcher.gui_manager.addComponent(CInput(),"Input");
launcher.gui_manager.addComponent(CMovement(CMovement.Direction.up),"Movement"); launcher.gui_manager.addComponent(CMovement(CMovement.Direction.up),"Movement");
//launcher.gui_manager.addComponent(CAnimation(),"Movement"); launcher.gui_manager.addComponent(CAnimation(),"Animation");
launcher.gui_manager.addComponent(CILocation(),"Int Location"); launcher.gui_manager.addComponent(CILocation(),"Int Location");
launcher.gui_manager.addComponent(CLocation(),"Location");
launcher.gui_manager.addSystem(becsID!MoveSystem,"Move System"); launcher.gui_manager.addSystem(becsID!MoveSystem,"Move System");
launcher.gui_manager.addSystem(becsID!InputSystem,"Input System"); launcher.gui_manager.addSystem(becsID!InputSystem,"Input System");
@ -922,6 +926,9 @@ void snakeStart()
launcher.gui_manager.addSystem(becsID!ParticleMovementSystem,"Particle Movement System"); launcher.gui_manager.addSystem(becsID!ParticleMovementSystem,"Particle Movement System");
launcher.gui_manager.addSystem(becsID!DrawAppleSystem,"Draw Apple System"); launcher.gui_manager.addSystem(becsID!DrawAppleSystem,"Draw Apple System");
launcher.gui_manager.addSystem(becsID!DrawSnakeSystem,"Draw Snake System"); launcher.gui_manager.addSystem(becsID!DrawSnakeSystem,"Draw Snake System");
launcher.gui_manager.addSystem(becsID!CopyLocationSystem,"Copy Location System");
//launcher.gui_manager.addSystem(becsID!AppleSystem,"Apple System");
//launcher.gui_manager.addSystem(becsID!SnakeSystem,"Snake System");
snake.snake_destroy_particle_frames = Mallocator.makeArray([vec4(64,144,16,16)*px,vec4(80,144,16,16)*px,vec4(96,144,16,16)*px,vec4(112,144,16,16)*px].staticArray); snake.snake_destroy_particle_frames = Mallocator.makeArray([vec4(64,144,16,16)*px,vec4(80,144,16,16)*px,vec4(96,144,16,16)*px,vec4(112,144,16,16)*px].staticArray);

View file

@ -20,6 +20,8 @@ import game_core.basic;
import game_core.rendering; import game_core.rendering;
import game_core.collision; import game_core.collision;
import gui.attributes;
private enum float px = 1.0/512.0; private enum float px = 1.0/512.0;
@ -222,8 +224,8 @@ struct CWeapon
} }
float shoot_time = 0; float shoot_time = 0;
Type type; @GUIRange(0, 4) Type type;
ubyte level = 1; @GUIRange(0, 11) ubyte level = 1;
} }
struct CWeaponLocation struct CWeaponLocation
@ -237,7 +239,7 @@ struct CShootDirection
{ {
mixin ECS.Component; mixin ECS.Component;
Direction direction; @GUIRange(0, 3) Direction direction;
} }
struct CSideMove struct CSideMove
@ -304,8 +306,8 @@ struct CAnimation
mixin ECS.Component; mixin ECS.Component;
vec4[] frames; vec4[] frames;
float time = 0; @GUIRangeF(0, float.max)float time = 0;
float speed = 1; @GUIRangeF(0, float.max)float speed = 1;
} }
struct CAnimationLooped struct CAnimationLooped
@ -350,7 +352,7 @@ struct CParts
{ {
mixin ECS.Component; mixin ECS.Component;
ubyte count; @GUIDisabled ubyte count;
} }
struct CInit struct CInit
@ -364,7 +366,7 @@ struct CInit
boss boss
} }
Type type; @GUIRange(0, 2)Type type;
} }
struct CParticleEmitter struct CParticleEmitter
@ -373,9 +375,8 @@ struct CParticleEmitter
vec2 range = vec2(0,0); vec2 range = vec2(0,0);
vec2 time_range = vec2(500,1000); vec2 time_range = vec2(500,1000);
///due to multithreading there should be separate template for every thread.
///It can be array of tempaltes or (like in this demo) simply index of template; ///It can be array of tempaltes or (like in this demo) simply index of template;
uint tmpl_id; //uint tmpl_id;
//EntityTemplate* tmpl; //EntityTemplate* tmpl;
} }
@ -404,7 +405,7 @@ struct CSpawnUponDeath
//EntityID parent; //EntityID parent;
//EntityTemplate* tmpl; //EntityTemplate* tmpl;
Type type; @GUIRange(0,0) Type type;
} }
///This component can be replaced by "CSpawnUponDeath" but I want to gives possibility to add this component to every entity ///This component can be replaced by "CSpawnUponDeath" but I want to gives possibility to add this component to every entity
@ -413,7 +414,7 @@ struct CShootWaveUponDeath
{ {
mixin ECS.Component; mixin ECS.Component;
CWeapon.Type bullet_type; @GUIRange(0, 4) CWeapon.Type bullet_type;
} }
/*####################################################################################################################### /*#######################################################################################################################
@ -1582,7 +1583,7 @@ struct PartsDestroySystem
becsID!CVelocity, becsID!CLocation, becsID!CParticleEmitter, becsID!CVelocity, becsID!CLocation, becsID!CParticleEmitter,
becsID!CParticleEmitterTime, becsID!CTargetParent, becsID!CDepth becsID!CParticleEmitterTime, becsID!CTargetParent, becsID!CDepth
].staticArray); ].staticArray);
*flashes_emitter.getComponent!CParticleEmitter() = CParticleEmitter(vec2(0,0), vec2(800,1600), 0); *flashes_emitter.getComponent!CParticleEmitter() = CParticleEmitter(vec2(0,0), vec2(800,1600));
} }
void onDestroy() void onDestroy()
@ -1783,7 +1784,8 @@ struct AnimationSystem
{ {
data.animation[i].time += dt * data.animation[i].speed; data.animation[i].time += dt * data.animation[i].speed;
while(cast(uint)data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time -= cast(float)data.animation[i].frames.length; while(cast(uint)data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time -= cast(float)data.animation[i].frames.length;
if(cast(uint)(data.animation[i].time) >= data.animation[i].frames.length)assert(0); //if(cast(uint)(data.animation[i].time) >= data.animation[i].frames.length)assert(0);
assert(cast(uint)(data.animation[i].time) < data.animation[i].frames.length);
uint index = cast(uint)(data.animation[i].time); uint index = cast(uint)(data.animation[i].time);
if(index < data.animation[i].frames.length)data.texcoords[i].value = data.animation[i].frames[index]; if(index < data.animation[i].frames.length)data.texcoords[i].value = data.animation[i].frames[index];
} }
@ -2102,9 +2104,13 @@ void spaceInvadersStart()
draw_system.default_data.color = 0x80808080; draw_system.default_data.color = 0x80808080;
draw_system.default_data.texture = space_invaders.texture; draw_system.default_data.texture = space_invaders.texture;
launcher.gui_manager.addComponent(CLocation(),"Location");
launcher.gui_manager.addComponent(CRotation(),"Rotation");
launcher.gui_manager.addComponent(CTexCoords(),"TexCoords");
launcher.gui_manager.addComponent(CInput(),"Input"); launcher.gui_manager.addComponent(CInput(),"Input");
launcher.gui_manager.addComponent(CShip(),"Ship"); launcher.gui_manager.addComponent(CShip(),"Ship");
launcher.gui_manager.addComponent(CEnemy(),"Enemy"); launcher.gui_manager.addComponent(CEnemy(),"Enemy");
launcher.gui_manager.addComponent(CShootDirection(),"Shoot Direction");
launcher.gui_manager.addComponent(CAutoShoot(),"Auto Shoot"); launcher.gui_manager.addComponent(CAutoShoot(),"Auto Shoot");
launcher.gui_manager.addComponent(CWeapon(0, CWeapon.Type.laser),"Weapon (laser)"); launcher.gui_manager.addComponent(CWeapon(0, CWeapon.Type.laser),"Weapon (laser)");
launcher.gui_manager.addComponent(CVelocity(vec2(0,0)),"Velocity (0,0)"); launcher.gui_manager.addComponent(CVelocity(vec2(0,0)),"Velocity (0,0)");
@ -2112,6 +2118,7 @@ void spaceInvadersStart()
launcher.gui_manager.addComponent(CSideMove(),"Side Move"); launcher.gui_manager.addComponent(CSideMove(),"Side Move");
launcher.gui_manager.addComponent(CSideMove(0),"Side Move (g1)"); launcher.gui_manager.addComponent(CSideMove(0),"Side Move (g1)");
launcher.gui_manager.addComponent(CSideMove(1),"Side Move (g2)"); launcher.gui_manager.addComponent(CSideMove(1),"Side Move (g2)");
launcher.gui_manager.addComponent(CDepth(),"Depth");
launcher.gui_manager.addComponent(CShootGrid(),"Shoot Grid"); launcher.gui_manager.addComponent(CShootGrid(),"Shoot Grid");
launcher.gui_manager.addComponent(CGuild(),"Guild (Player)"); launcher.gui_manager.addComponent(CGuild(),"Guild (Player)");
launcher.gui_manager.addComponent(CGuild(1),"Guild (Enemy)"); launcher.gui_manager.addComponent(CGuild(1),"Guild (Enemy)");
@ -2119,24 +2126,29 @@ void spaceInvadersStart()
launcher.gui_manager.addComponent(CHitMark(),"Hit Mark"); launcher.gui_manager.addComponent(CHitMark(),"Hit Mark");
launcher.gui_manager.addComponent(CUpgrade(CUpgrade.Upgrade.laser),"Upgrade (laser)"); launcher.gui_manager.addComponent(CUpgrade(CUpgrade.Upgrade.laser),"Upgrade (laser)");
launcher.gui_manager.addComponent(CParticle(1000),"Particle (1s)"); launcher.gui_manager.addComponent(CParticle(1000),"Particle (1s)");
//launcher.gui_manager.addComponent(CMaxHitPoints(),"Max Hit Points"); launcher.gui_manager.addComponent(CMaxHitPoints(),"Max Hit Points");
launcher.gui_manager.addComponent(CAnimation(),"Animation");
launcher.gui_manager.addComponent(CDamping(0),"Damping (0)"); launcher.gui_manager.addComponent(CDamping(0),"Damping (0)");
launcher.gui_manager.addComponent(CDamping(4),"Damping (4)"); launcher.gui_manager.addComponent(CDamping(4),"Damping (4)");
launcher.gui_manager.addComponent(CDamping(8),"Damping (8)"); launcher.gui_manager.addComponent(CDamping(8),"Damping (8)");
launcher.gui_manager.addComponent(CAnimationLooped(),"Animation loop flag");
launcher.gui_manager.addComponent(CTargetParent(),"Target Parent"); launcher.gui_manager.addComponent(CTargetParent(),"Target Parent");
launcher.gui_manager.addComponent(CTargetPlayerShip(),"Target Player Ship"); launcher.gui_manager.addComponent(CTargetPlayerShip(),"Target Player Ship");
launcher.gui_manager.addComponent(CTarget(),"Target"); launcher.gui_manager.addComponent(CTarget(),"Target");
launcher.gui_manager.addComponent(CChildren(),"Children"); launcher.gui_manager.addComponent(CChildren(),"Children");
launcher.gui_manager.addComponent(CVelocityFactor(),"Velocity Factor");
launcher.gui_manager.addComponent(CWeaponLocation(vec2(0,16)),"Weapon Location (0,16)"); launcher.gui_manager.addComponent(CWeaponLocation(vec2(0,16)),"Weapon Location (0,16)");
launcher.gui_manager.addComponent(CInit(CInit.Type.space_ship),"Init (Ship)"); launcher.gui_manager.addComponent(CInit(CInit.Type.space_ship),"Init (Ship)");
launcher.gui_manager.addComponent(CInit(CInit.Type.boss),"Init (Boss)"); launcher.gui_manager.addComponent(CInit(CInit.Type.boss),"Init (Boss)");
launcher.gui_manager.addComponent(CInit(CInit.Type.tower),"Init (Tower)"); launcher.gui_manager.addComponent(CInit(CInit.Type.tower),"Init (Tower)");
launcher.gui_manager.addComponent(CBoss(),"Boss"); launcher.gui_manager.addComponent(CBoss(),"Boss");
launcher.gui_manager.addComponent(CParts(),"Parts");
launcher.gui_manager.addComponent(CColliderScale(),"Collider Scale"); launcher.gui_manager.addComponent(CColliderScale(),"Collider Scale");
launcher.gui_manager.addComponent(CParticleEmitter(),"Particle Emitter"); launcher.gui_manager.addComponent(CParticleEmitter(),"Particle Emitter");
launcher.gui_manager.addComponent(CParticleEmitterTime(),"Particle Emitter Time"); launcher.gui_manager.addComponent(CParticleEmitterTime(),"Particle Emitter Time");
//launcher.gui_manager.addComponent(CSpawnUponDeath(),"Spawn Upon Death"); launcher.gui_manager.addComponent(CSpawnUponDeath(),"Spawn Upon Death");
launcher.gui_manager.addComponent(CShootWaveUponDeath(CWeapon.Type.canon),"Wave Upon Death"); launcher.gui_manager.addComponent(CShootWaveUponDeath(CWeapon.Type.canon),"Wave Upon Death");
launcher.gui_manager.addComponent(CShootGridMask(),"Shoot grid mask");
launcher.gui_manager.addSystem(becsID!DrawSystem,"Draw System"); launcher.gui_manager.addSystem(becsID!DrawSystem,"Draw System");
launcher.gui_manager.addSystem(becsID!InputMovementSystem,"Input Movement"); launcher.gui_manager.addSystem(becsID!InputMovementSystem,"Input Movement");
@ -2165,6 +2177,7 @@ void spaceInvadersStart()
launcher.gui_manager.addSystem(becsID!ChildDestroySystem,"Child Destroy System"); launcher.gui_manager.addSystem(becsID!ChildDestroySystem,"Child Destroy System");
launcher.gui_manager.addSystem(becsID!ShootWaveSystem,"Shoot Wave System"); launcher.gui_manager.addSystem(becsID!ShootWaveSystem,"Shoot Wave System");
//launcher.gui_manager.addSystem(becsID!SpawnUponDeathSystem,"Child Destroy System"); //launcher.gui_manager.addSystem(becsID!SpawnUponDeathSystem,"Child Destroy System");
//launcher.gui_manager.addSystem(becsID!CollisionMaskSystem,"Collision Mask");
//gEntityManager.getSystem(becsID!CleanSystem).disable(); //gEntityManager.getSystem(becsID!CleanSystem).disable();
{ {

View file

@ -11,6 +11,7 @@ import ecs_utils.utils;
import game_core.basic; import game_core.basic;
import gui.attributes;
void registerCollisionModule(EntityManager* manager) void registerCollisionModule(EntityManager* manager)
{ {
@ -46,7 +47,7 @@ struct CBVH
{ {
mixin ECS.Component; mixin ECS.Component;
uint index; @GUIDisabled uint index;
} }
struct CAABB struct CAABB
@ -64,7 +65,7 @@ struct CShootGridMask
alias value this; alias value this;
ubyte value; @GUIDisabled ubyte value;
} }
struct CColliderScale struct CColliderScale

View file

@ -4,17 +4,20 @@ enum GUIColor = "GUIColor";
struct GUIRange struct GUIRange
{ {
union struct
{ {
struct int min;
{ int max;
int min;
int max;
}
struct
{
float minf;
float maxf;
}
} }
} }
struct GUIRangeF
{
struct
{
float minf;
float maxf;
}
}
enum GUIDisabled = "GUIDisabled";

View file

@ -54,6 +54,7 @@ struct VariableGUI
Type type; Type type;
const (char)* name; const (char)* name;
ushort offset; ushort offset;
bool disabled = false;
union union
{ {
Int int_; Int int_;

View file

@ -152,6 +152,7 @@ struct GUIManager
//pragma(msg,member_type); //pragma(msg,member_type);
//pragma(msg,__traits(getMember, T, member).offsetof); //pragma(msg,__traits(getMember, T, member).offsetof);
ushort offset = member.offsetof;//cast(ushort)__traits(getMember, T, member).offsetof; ushort offset = member.offsetof;//cast(ushort)__traits(getMember, T, member).offsetof;
static if(is(member_type == vec2)) static if(is(member_type == vec2))
{ {
comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.vec2,member_str,offset); comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.vec2,member_str,offset);
@ -236,14 +237,20 @@ struct GUIManager
} }
static if(hasUDA!(member,GUIRange)) static if(hasUDA!(member,GUIRange))
{ {
comp_edit.variables[comp_edit.used-1].float_.min = getUDAs!(member,GUIRange)[0].minf; comp_edit.variables[comp_edit.used-1].float_.min = getUDAs!(member,GUIRange)[0].min;
comp_edit.variables[comp_edit.used-1].float_.max = getUDAs!(member,GUIRange)[1].maxf; comp_edit.variables[comp_edit.used-1].float_.max = getUDAs!(member,GUIRange)[0].max;
} }
else static if(hasUDA!(member,GUIRangeF))
{ {
comp_edit.variables[comp_edit.used-1].float_.min = getUDAs!(member,GUIRangeF)[0].minf;
comp_edit.variables[comp_edit.used-1].float_.max = getUDAs!(member,GUIRangeF)[0].maxf;
}
else {
comp_edit.variables[comp_edit.used-1].float_.min = -float.max; comp_edit.variables[comp_edit.used-1].float_.min = -float.max;
comp_edit.variables[comp_edit.used-1].float_.max = float.max; comp_edit.variables[comp_edit.used-1].float_.max = float.max;
} }
} }
static if(hasUDA!(member,GUIDisabled))comp_edit.variables[comp_edit.used - 1].disabled = true;
} }
edit_components[becsID(comp)] = comp_edit; edit_components[becsID(comp)] = comp_edit;
} }
@ -256,12 +263,14 @@ struct GUIManager
igIndent(8); igIndent(8);
foreach(ref SystemGUI system;systems) foreach(ref SystemGUI system;systems)
{ {
igPushIDPtr(&system);
if(igCheckbox(system.name,&system.enabled)) if(igCheckbox(system.name,&system.enabled))
{ {
System* sys = gEntityManager.getSystem(system.id); System* sys = gEntityManager.getSystem(system.id);
if(system.enabled)sys.enable(); if(system.enabled)sys.enable();
else sys.disable(); else sys.disable();
} }
igPopID();
} }
igUnindent(8); igUnindent(8);
} }
@ -337,13 +346,15 @@ struct GUIManager
{ {
vec4 color; vec4 color;
if(comp_id >= edit_components.length)return; if(comp_id >= edit_components.length)return;
if(edit_components[comp_id].used) //if(edit_components[comp_id].used)
if(edit_components[comp_id].name)
{ {
if(igCollapsingHeader(edit_components[comp_id].name, ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen)) if(igCollapsingHeader(edit_components[comp_id].name, ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen))
{ {
igIndent(8); igIndent(8);
foreach(ref VariableGUI var;edit_components[comp_id].variables[0 .. edit_components[comp_id].used]) foreach(ref VariableGUI var;edit_components[comp_id].variables[0 .. edit_components[comp_id].used])
{ {
igPushIDPtr(&var); igPushIDPtr(&var);
switch(var.type) switch(var.type)
{ {
@ -351,7 +362,12 @@ struct GUIManager
igDragScalarClamp(var.name, ImGuiDataType_S8, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); igDragScalarClamp(var.name, ImGuiDataType_S8, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
break; break;
case VariableGUI.Type.ubyte_: case VariableGUI.Type.ubyte_:
igDragScalarClamp(var.name, ImGuiDataType_U8, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); if(var.disabled)
{
ubyte v = *cast(ubyte*)(data_ptr+var.offset);
igDragScalarClamp(var.name, ImGuiDataType_U8, &v, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
}
else igDragScalarClamp(var.name, ImGuiDataType_U8, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
break; break;
case VariableGUI.Type.short_: case VariableGUI.Type.short_:
igDragScalarClamp(var.name, ImGuiDataType_S16, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); igDragScalarClamp(var.name, ImGuiDataType_S16, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
@ -363,7 +379,12 @@ struct GUIManager
igDragScalarClamp(var.name, ImGuiDataType_S32, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); igDragScalarClamp(var.name, ImGuiDataType_S32, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
break; break;
case VariableGUI.Type.uint_: case VariableGUI.Type.uint_:
igDragScalarClamp(var.name, ImGuiDataType_U32, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1); if(var.disabled)
{
uint v = *cast(uint*)(data_ptr+var.offset);
igDragScalarClamp(var.name, ImGuiDataType_U32, &v, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
}
else igDragScalarClamp(var.name, ImGuiDataType_U32, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
break; break;
case VariableGUI.Type.float_: case VariableGUI.Type.float_:
igDragScalarClamp(var.name, ImGuiDataType_Float, data_ptr+var.offset, 0.1, cast(void*)&var.float_.min, cast(void*)&var.float_.max, "%2.2f", 1); igDragScalarClamp(var.name, ImGuiDataType_Float, data_ptr+var.offset, 0.1, cast(void*)&var.float_.min, cast(void*)&var.float_.max, "%2.2f", 1);

View file

@ -448,6 +448,7 @@ struct Renderer
void draw(scope ref const(DrawData) data) void draw(scope ref const(DrawData) data)
{ {
if(prepared_items >= MaxObjects)return; if(prepared_items >= MaxObjects)return;
if(threads[data.thread_id].blocks.length <= data.material_id)return;
__draw(this,data);//tex,pos,size,coords,depth,color,angle,material_id,mesh_id,thread_id); __draw(this,data);//tex,pos,size,coords,depth,color,angle,material_id,mesh_id,thread_id);
} }

View file

@ -3,10 +3,12 @@ project('decs', 'd', version : '0.5.0')
# Options # Options
betterC_opt = get_option('betterC') betterC_opt = get_option('betterC')
BuildDemos_opt = get_option('BuildDemos') BuildDemos_opt = get_option('BuildDemos')
BuildTests_opt = get_option('BuildTests')
LTO_otp = get_option('LTO') LTO_otp = get_option('LTO')
summary('betterC enabled', betterC_opt) summary('betterC enabled', betterC_opt)
summary('build demos', BuildDemos_opt) summary('build demos', BuildDemos_opt)
summary('build tests', BuildTests_opt)
summary('LTO enabled', LTO_otp) summary('LTO enabled', LTO_otp)
meson_minimum_version = '>=0.57.1' meson_minimum_version = '>=0.57.1'
@ -65,7 +67,9 @@ decs_dep = declare_dependency(
) )
# Tests # Tests
subdir('tests') if BuildTests_opt
subdir('tests')
endif
# Demos # Demos
if BuildDemos_opt if BuildDemos_opt

View file

@ -1,3 +1,4 @@
option('betterC', type: 'boolean', value: false) option('betterC', type: 'boolean', value: false)
option('BuildDemos', type: 'boolean', value: false) option('BuildDemos', type: 'boolean', value: false)
option('BuildTests', type: 'boolean', value: false)
option('LTO', type: 'boolean', value: false) option('LTO', type: 'boolean', value: false)

View file

@ -6,6 +6,7 @@ $(LIST
* Component: make component structure * Component: make component structure
* Event: make event structure * Event: make event structure
) )
This mixins are optional and are used to adding some additional capabilities, e.g. ECS.System is used to configuring default number of jobs for system.
--- ---
Struct System1 Struct System1

View file

@ -59,6 +59,9 @@ grouped by component type so entity can be fracted in big memory chunk.
There is two types of update: There is two types of update:
- update(): function used to call update pass. - update(): function used to call update pass.
- updateMT(): function used to call update pass multithreaded. This call only generates jobs which must be called by user. - updateMT(): function used to call update pass multithreaded. This call only generates jobs which must be called by user.
WARNING! No fileds from EntityManager should be used directly, they are not private for special, advanced things which are not implemented in library.
So if you don't need anything special, simply use only EntityManager methods.
*/ */
export struct EntityManager export struct EntityManager
{ {
@ -1611,11 +1614,6 @@ export struct EntityManager
m_thread_id_func = cast(uint delegate() nothrow @nogc) get_id_callback; m_thread_id_func = cast(uint delegate() nothrow @nogc) get_id_callback;
} }
/*export void setJobDispachFunc(void delegate(JobGroup) @nogc nothrow func) nothrow @nogc
{
m_dispatch_jobs = func;
}*/
/************************************************************************************************************************ /************************************************************************************************************************
Return size of single page (block). Every entity data block has size of page. Return size of single page (block). Every entity data block has size of page.
*/ */
@ -3762,20 +3760,6 @@ export struct EntityManager
return 0; return 0;
} }
/*uint thread_id() @nogc nothrow
{
if (m_thread_id_func)
return (cast(uint delegate() nothrow @nogc) m_thread_id_func)();
else
return 0;
}
void thread_id(uint) @nogc nothrow
{
}*/
//static uint thread_id;
ThreadData[] threads; ThreadData[] threads;
Vector!(UpdatePass*) passes; Vector!(UpdatePass*) passes;

View file

@ -1,10 +1,18 @@
tests_src = files( tests_src = files(
'tests.d', 'access_perf.d',
'basic.d',
'bugs.d',
'id_manager.d',
'map.d',
'perf.d',
'runner.d',
'time.d',
'vector.d'
) )
exe = executable('decs-tests', exe = executable('decs-tests',
tests_src, tests_src,
include_directories : [inc], include_directories : [inc, include_directories('..')],
d_args : args, d_args : args,
link_args : link_args, link_args : link_args,
dependencies : decs_dep, dependencies : decs_dep,

View file

@ -3,7 +3,6 @@ module tests.runner;
import core.stdc.stdio; import core.stdc.stdio;
import core.stdc.string; import core.stdc.string;
import core.sys.posix.setjmp;
import bubel.ecs.vector; import bubel.ecs.vector;
import bubel.ecs.simple_vector; import bubel.ecs.simple_vector;
@ -25,11 +24,36 @@ else
enum int ASSERTED = 123; enum int ASSERTED = 123;
enum string OUT_FILE = "test_report.xml"; enum string OUT_FILE = "test_report.xml";
static jmp_buf gEnvBuffer;
static AssertInfo gAssertInfo;
version (D_BetterC) version (D_BetterC)
{ {
version(Posix)
{
import core.sys.posix.setjmp;
}
else version(Windows)
{
version(X86)
alias jmp_buf = ubyte[64];
else version(X86_64)
alias jmp_buf = ubyte[256];
else version(IA64)
alias jmp_buf = ubyte[512];
extern (C) {
int _setjmp(ref jmp_buf);
void longjmp(ref jmp_buf, int);
}
alias setjmp = _setjmp;
}
static jmp_buf gEnvBuffer;
static AssertInfo gAssertInfo;
extern (C) void __assert(const char* msg, const char* file, int line)
{
gAssertInfo = AssertInfo(msg, file, line);
longjmp(gEnvBuffer, ASSERTED);
}
} }
else version = notBetterC; else version = notBetterC;
@ -40,12 +64,6 @@ struct AssertInfo
int line; int line;
} }
extern (C) void __assert(const char* msg, const char* file, int line)
{
gAssertInfo = AssertInfo(msg, file, line);
longjmp(gEnvBuffer, ASSERTED);
}
struct Test struct Test
{ {
string file; string file;
@ -427,6 +445,13 @@ extern (C) int main(int argc, char** args)
} }
} }
static import tests.id_manager;
static import tests.vector;
static import tests.basic;
static import tests.perf;
static import tests.access_perf;
static import tests.bugs;
static import tests.map;
TestRunner!(tests.id_manager, tests.vector, tests.basic, tests.perf, tests.access_perf, tests.bugs, tests.map) runner; TestRunner!(tests.id_manager, tests.vector, tests.basic, tests.perf, tests.access_perf, tests.bugs, tests.map) runner;
runner.runTests(include[], exclude[]); runner.runTests(include[], exclude[]);