bubel-ecs/demos/source/demos/simple.d
Mergul 27154c809e -removed launcher.manager (gEntityManager used instead)
-removed somee comments, unneded code
-added some comments/documentation
2021-03-02 21:05:05 +01:00

283 lines
No EOL
9.9 KiB
D

module demos.simple;
import app;
import bindbc.sdl;
import bubel.ecs.attributes;
import bubel.ecs.core;
import bubel.ecs.entity;
import bubel.ecs.manager;
import bubel.ecs.std;
import cimgui.cimgui;
import ecs_utils.gfx.texture;
import ecs_utils.math.vector;
import ecs_utils.utils;
import game_core.basic;
import game_core.rendering;
extern(C):
enum float px = 1.0/512.0;
/**************************************************
All demos uses same patten. Every demo is self contained except systems and components which was splitted into different files to enable sharing them between demos.
Every demo has same functions:
* register - called on start for registering proces
* initialize - called after register to initialize demo data
* deinitiliaze - called when demo is switching. There data is deisposed
* loop - it's called every frame
Demos uses some non-ECS functions to register components, systems and templates into GUI. Then components are showing by GUI and can be added to entities.
*/
/*#######################################################################################################################
------------------------------------------------ Components ------------------------------------------------------------------
#######################################################################################################################*/
//CLocation component was moved to game_code.basic
/*struct CLocation
{
mixin ECS.Component;
alias location this;
vec2 location;
}*/
/*#######################################################################################################################
------------------------------------------------ Systems ------------------------------------------------------------------
#######################################################################################################################*/
//DrawSystem was moved to game_code.basic
/*
struct DrawSystem
{
mixin ECS.System!32;
struct EntitiesData
{
uint length;
//uint thread_id;
uint job_id;
@readonly CLocation[] locations;
}
void onUpdate(EntitiesData data)
{
if(launcher.renderer.prepared_items >= launcher.renderer.MaxObjects)return;//simple leave loop if max visible objects count was reached
import ecs_utils.gfx.renderer;
Renderer.DrawData draw_data;
draw_data.size = vec2(16,16);
draw_data.coords = vec4(0,0,1,1);
draw_data.color = 0x80808080;
draw_data.material_id = 0;
draw_data.thread_id = data.job_id;
draw_data.texture = simple.texture;
foreach(i; 0..data.length)
{
draw_data.position = data.locations[i];
draw_data.depth = cast(ushort)(data.locations[i].y);
launcher.renderer.draw(draw_data);
}
}
}*/
//simple system which moves entities
struct MoveSystem
{
//system will generate up to 64 jobs for multithreaded extecution
mixin ECS.System!64;
//structe contains components used by system
struct EntitiesData
{
uint length;
//system will use one component which is required. Only entities with CLocation component will be processed by this system
CLocation[] locations;
}
//onUpdate is called several times and covers all entities
void onUpdate(EntitiesData data)
{
//loop over entities in batch
foreach(i; 0..data.length)
{
//inscrease entity position in 'y' coordinate
data.locations[i].y = data.locations[i].y + 1;
//move entity to 0 if exceeded 300
if(data.locations[i].y > 300)data.locations[i].y = 0;
}
}
}
/*#######################################################################################################################
------------------------------------------------ Functions ------------------------------------------------------------------
#######################################################################################################################*/
struct Simple
{
//tips showed in GUI
__gshared const (char)* tips = "Use \"space\" to spwan entities.\n\nSystems can be enabled/disabled from \"Demo\" window.
\"Tools\" window exists of three tools which can be used to manipulate game.
Options:
* Show Tool - enable/disable rendering of blue circle around cursor
* Show Filtered - enable/disable higliting filtered entities. For \"Component manipulator\" tool it shows entities which has selected component.
* Add/Remove - select primary action. LMB - primary action, RMB - secondary action
* Tools size - size of tool
* Tool repeat - how many times in one second tool should take action (e.g. 1000 means every second \"Entity spawner\" will spawn 1000 enties)
* Override - enabled means that \"Component manipulator\" will override components data if entity already has that component
Tools:
* Entity spawner - used to spawn new entities
* Component manipulator - used to add/remove components to/from entities
* Selector - allow to select entity, show and modify his data. Only one entity can be selected, selector selects entity nearest co cursor.
ShortCuts:
* CRTL*1/2/3 - change tool
* Mouse wheel - change tool size
* SHIFT + Mouse wheel - change entity/component in tool list
* LBM - primary action (default: add entity / add component)
* RMB - secondary action (default: remove entity / remove component)
\"Statistic\" windows shows FPS and entities count.
From top menu bar (upper left corner) you can select different demos or change some options. Multihtreading is highly recommended, but it can not working on mobile phones or Firefox browser.
Demo is capable rendering of hundreds of thousands of entities. Playable area is heavily too small to show that count of entities, but you can try it :)";
EntityTemplate* tmpl;
Texture texture;
}
__gshared Simple* simple;
//called when demo starts
void simpleRegister()
{
simple = Mallocator.make!Simple;
//load texture (atlas)
simple.texture.create();
simple.texture.load("assets/textures/atlas.png");
//start registering process
gEntityManager.beginRegister();
//register basic components and systems
registerRenderingModule(gEntityManager);
//register location component. It also registered inside registerRenderingModule() function, but it's there for clarity
gEntityManager.registerComponent!CLocation;
gEntityManager.registerSystem!MoveSystem(0);
// DrawSystem is registered as RenderingModule
// gEntityManager.registerSystem!DrawSystem(1);
//end registering process
gEntityManager.endRegister();
}
//called after simpleRegister
void simpleStart()
{
//get DrawSystem instance and change some data
DrawSystem* draw_system = gEntityManager.getSystem!DrawSystem;
draw_system.default_data.color = 0x80808080;
draw_system.default_data.texture = simple.texture;
draw_system.default_data.size = vec2(16,16);
draw_system.default_data.coords = vec4(0,48,16,16)*px;//vec4(0,0,1,1);
//add systems to GUI. It's non ECS part
launcher.gui_manager.addSystem(becsID!MoveSystem,"Move Up System");
launcher.gui_manager.addSystem(becsID!DrawSystem,"Draw System");
//add components to GUI. It's non ECS part
launcher.gui_manager.addComponent(CLocation(), "Location");
launcher.gui_manager.addComponent(CDrawDefault(), "DrawDefault");
//allocate new template with two components
simple.tmpl = gEntityManager.allocateTemplate([becsID!CLocation, becsID!CDrawDefault].staticArray);
//add template to GUI. It's non ECS part
launcher.gui_manager.addTemplate(simple.tmpl, "Basic");
//add 100 entities
foreach(i; 0..10)
foreach(j; 0..10)
{
//add entities in grid locations. "ref_" return ComponentRef structure. I'm not sure if adding component inside array generation isn't undefined behaviour but it works on tested platforms
gEntityManager.addEntity(simple.tmpl,[CLocation(vec2(i*16+64,j*16+64)).ref_].staticArray);
}
}
//called when demo is switched to different one
void simpleEnd()
{
//disable systems used by this demo.
gEntityManager.getSystem(becsID!MoveSystem).disable();
gEntityManager.getSystem(becsID!DrawSystem).disable();
//free texture memory
simple.texture.destroy();
//GUI manager will free template
//gEntityManager.freeTemplate(simple.tmpl);
Mallocator.dispose(simple);
}
void simpleEvent(SDL_Event* event)
{
}
void spawnEntity()
{
//spawn entity in random location
gEntityManager.addEntity(simple.tmpl,[CLocation(vec2(randomf() * 400,0)).ref_].staticArray);
}
bool simpleLoop()
{
launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(400,300)) * 0.5;
if(launcher.getKeyState(SDL_SCANCODE_SPACE))
{
foreach(i;0..20)spawnEntity();
}
//begin frame
gEntityManager.begin();
//if multithreading is enabled different path is used
if(launcher.multithreading)
{
//prepare data for multithreading. Clear previous jobs data.
launcher.job_updater.begin();
//generate jobs
gEntityManager.updateMT();
//call jobs in multithreaded fashion
launcher.job_updater.call();
}
else
{
//update call will call inUpdate for all systems
gEntityManager.update();
}
//end ECS frame
gEntityManager.end();
return true;
}
DemoCallbacks getSimpleDemo()
{
DemoCallbacks demo;
demo.register = &simpleRegister;
demo.initialize = &simpleStart;
demo.deinitialize = &simpleEnd;
demo.loop = &simpleLoop;
demo.tips = simple.tips;
return demo;
}