module demos.brick_breaker; 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; import game_core.collision; extern(C): private enum float px = 1.0/512.0; /*####################################################################################################################### ------------------------------------------------ Components ------------------------------------------------------------------ #######################################################################################################################*/ /*struct CLocation { mixin ECS.Component; alias location this; vec2 location; }*/ struct CBrick { mixin ECS.Component; } struct CPaddle { mixin ECS.Component; } struct CBall { mixin ECS.Component; ubyte radius; } struct CHitPoints { mixin ECS.Component; alias value this; short value; } // struct CVelocityFactor // { // mixin ECS.Component; // alias value this; // vec2 value = vec2(1); // } // struct CVelocity // { // mixin ECS.Component; // alias value this; // vec2 value = vec2(0); // } struct EDamage { mixin ECS.Event; ubyte damage = 1; } /*####################################################################################################################### ------------------------------------------------ Systems ------------------------------------------------------------------ #######################################################################################################################*/ // struct MoveSystem // { // mixin ECS.System!64; // struct EntitiesData // { // uint length; // CLocation[] location; // @readonly CVelocity[] velocity; // @optional @readonly CVelocityFactor[] vel_factor; // } // void onUpdate(EntitiesData data) // { // if(data.vel_factor) // { // foreach(i; 0..data.length) // { // data.location[i] += data.velocity[i] * data.vel_factor[i] * launcher.delta_time; // } // } // else // { // foreach(i; 0..data.length) // { // data.location[i] += data.velocity[i] * launcher.delta_time; // } // } // } // } struct EdgeCollisionSystem { mixin ECS.System!64; struct EntitiesData { uint length; CLocation[] location; CVelocity[] velocity; //CBall[] ball_flag; } void onUpdate(EntitiesData data) { foreach(i; 0..data.length) { if(data.location[i].x < 0) { if(data.velocity[i].x < 0)data.velocity[i].x = -data.velocity[i].x; data.location[i].x = 0; } else if(data.location[i].x > 400) { if(data.velocity[i].x > 0)data.velocity[i].x = -data.velocity[i].x; data.location[i].x = 400; } if(data.location[i].y < 0) { if(data.velocity[i].y < 0)data.velocity[i].y = -data.velocity[i].y; data.location[i].y = 0; } else if(data.location[i].y > 300) { if(data.velocity[i].y > 0)data.velocity[i].y = -data.velocity[i].y; data.location[i].y = 300; } } } } struct BallCollisionSystem { mixin ECS.System!64; mixin ECS.ReadOnlyDependencies!(ShootGridDependency, BVHDependency); struct EntitiesData { ///variable named "length" contain entites count uint length; const (Entity)[] entity; CVelocity[] velocity; @readonly CLocation[] location; @readonly CScale[] scale; @readonly CBall[] ball_flag; } struct State { bool test(EntityID id) { Entity* entity = launcher.manager.getEntity(id); if(entity) { CLocation* location = entity.getComponent!CLocation; CScale* scale = entity.getComponent!CScale; if(location && scale) { float radius = data.scale[i].x; vec2 rel_pos = *location - data.location[i]; vec2 abs_rel_pos = rel_pos; if(abs_rel_pos.x < 0)abs_rel_pos.x = -abs_rel_pos.x; if(abs_rel_pos.y < 0)abs_rel_pos.y = -abs_rel_pos.y; vec2 half_scale = *scale * 0.25f; if(abs_rel_pos.x < half_scale.x + radius && abs_rel_pos.y < half_scale.y + radius) { if(abs_rel_pos.x < half_scale.x) { if(rel_pos.y * data.velocity[i].y > 0) { data.velocity[i].y = -data.velocity[i].y; launcher.manager.sendEvent(id,EDamage(1)); return false; } } else if(abs_rel_pos.y < half_scale.y) { if(rel_pos.x * data.velocity[i].x > 0) { data.velocity[i].x = -data.velocity[i].x; launcher.manager.sendEvent(id,EDamage(1)); return false; } } else { vec2 vector = abs_rel_pos - half_scale; if(rel_pos.x > 0)vector.x = -vector.x; if(rel_pos.y > 0)vector.y = -vector.y; float pow_dist = vector.length2(); if(pow_dist < radius*radius) { vector = vector / sqrtf(pow_dist); data.velocity[i] = data.velocity[i] - vector * (2 * dot(vector, data.velocity[i])); launcher.manager.sendEvent(id,EDamage(1)); return false; } } } } } return true; } EntitiesData data; uint i; } ShootGrid* grid; BVHTree* tree; BVHTree* static_tree; bool onBegin() { //grid = launcher.manager.getSystem!ShootGridManager().grid; tree = launcher.manager.getSystem!BVHBuilder().tree; static_tree = launcher.manager.getSystem!StaticBVHBuilder().tree; //if(grid is null)return false; if(tree is null || static_tree is null)return false; else return true; } void onUpdate(EntitiesData data) { // State state; // state.data = data; // EntityID id; // foreach(i; 0..data.length) // { // state.i = i; // float radius = data.scale[i].x; // if(grid.test(id, data.location[i] - radius, data.location[i] + radius, ubyte.max)) // { // state.test(id); // } // } State state; state.data = data; foreach(i; 0..data.length) { state.i = i; //float radius = data.scale[i].x; AABB bounding = AABB(data.location[i]-data.scale[i], data.location[i]+data.scale[i]); tree.test(bounding, cast(bool delegate(EntityID id))&state.test); static_tree.test(bounding, cast(bool delegate(EntityID id))&state.test); } } } struct DamageSystem { mixin ECS.System!64; mixin ECS.ReadOnlyDependencies!(ShootGridDependency); struct EntitiesData { ///variable named "length" contain entites count uint length; const (Entity)[] entity; CHitPoints[] hit_points; } void handleEvent(Entity* entity, EDamage event) { EntityMeta meta = entity.getMeta(); CHitPoints* hp = meta.getComponent!CHitPoints; hp.value -= event.damage; if(hp.value < 0)launcher.manager.removeEntity(entity.id); } } /*####################################################################################################################### ------------------------------------------------ Functions ------------------------------------------------------------------ #######################################################################################################################*/ struct BrickBreakerDemo { __gshared const (char)* tips = "Brick breaker demo. It's a game about destroying evil bricks."; //EntityTemplate* tmpl; Texture texture; } __gshared BrickBreakerDemo* demo; void brickBreakerRegister() { demo = Mallocator.make!BrickBreakerDemo; demo.texture.create(); demo.texture.load("assets/textures/atlas.png"); launcher.manager.beginRegister(); registerRenderingModule(launcher.manager); registerCollisionModule(launcher.manager); launcher.manager.registerComponent!CLocation; launcher.manager.registerComponent!CRotation; launcher.manager.registerComponent!CScale; launcher.manager.registerComponent!CTexCoords; launcher.manager.registerComponent!CTexCoordsIndex; launcher.manager.registerComponent!CVelocity; launcher.manager.registerComponent!CInput; launcher.manager.registerComponent!CPaddle; launcher.manager.registerComponent!CDamping; launcher.manager.registerComponent!CVelocityFactor; launcher.manager.registerComponent!CBall; launcher.manager.registerComponent!CHitPoints; launcher.manager.registerEvent!EDamage; launcher.manager.registerSystem!MoveSystem(-100); launcher.manager.registerSystem!EdgeCollisionSystem(-99); launcher.manager.registerSystem!BallCollisionSystem(-79); launcher.manager.registerSystem!InputMovementSystem(-120); launcher.manager.registerSystem!DampingSystem(-120); launcher.manager.registerSystem!DamageSystem(-120); launcher.manager.endRegister(); } void brickBreakerStart() { DrawSystem* draw_system = launcher.manager.getSystem!DrawSystem; draw_system.default_data.color = 0x80808080; draw_system.default_data.texture = demo.texture; draw_system.default_data.size = vec2(16,16); draw_system.default_data.coords = vec4(246,64,2,2)*px; draw_system.default_data.material_id = 0; EntityTemplate* brick_tmpl = launcher.manager.allocateTemplate( [CLocation.component_id, CScale.component_id, CColor.component_id, CTexCoordsIndex.component_id, CBVH.component_id, CHitPoints.component_id, CAABB.component_id, CStatic.component_id].staticArray ); brick_tmpl.getComponent!CTexCoordsIndex().value = TexCoordsManager.instance.getCoordIndex(vec4(304,40,16,8)*px); brick_tmpl.getComponent!CColor().value = 0x80206020; brick_tmpl.getComponent!CScale().value = vec2(16,8); brick_tmpl.getComponent!CHitPoints().value = 2; //brick_tmpl.getComponent!CAABB().bounding = AABB(vec2(),vec2()); EntityTemplate* big_brick_tmpl = launcher.manager.allocateTemplate(brick_tmpl); big_brick_tmpl.getComponent!CTexCoordsIndex().value = TexCoordsManager.instance.getCoordIndex(vec4(320,32,16,16)*px); big_brick_tmpl.getComponent!CScale().value = vec2(16,16); EntityTemplate* paddle_tmpl = launcher.manager.allocateTemplate( [CLocation.component_id, CScale.component_id, CInput.component_id, CTexCoordsIndex.component_id, CPaddle.component_id, CVelocity.component_id, CDamping.component_id, CVelocityFactor.component_id, CBVH.component_id, CAABB.component_id].staticArray ); paddle_tmpl.getComponent!CTexCoordsIndex().value = TexCoordsManager.instance.getCoordIndex(vec4(272,48,64,10)*px); paddle_tmpl.getComponent!CScale().value = vec2(64,10); paddle_tmpl.getComponent!CDamping().value = 14; paddle_tmpl.getComponent!CVelocityFactor().value = vec2(1,0); EntityTemplate* ball_tmpl = launcher.manager.allocateTemplate( [CLocation.component_id, CScale.component_id, //CDamping.component_id, CTexCoordsIndex.component_id, CBall.component_id, CVelocity.component_id].staticArray ); ball_tmpl.getComponent!CTexCoordsIndex().value = TexCoordsManager.instance.getCoordIndex(vec4(304,32,8,8)*px); ball_tmpl.getComponent!CScale().value = vec2(8,8); ball_tmpl.getComponent!CVelocity().value = vec2(0.1,0.1); // paddle_tmpl.getComponent!CDamping().value = 14; launcher.gui_manager.addComponent(CLocation(), "Location"); launcher.gui_manager.addComponent(CRotation(), "Rotation"); launcher.gui_manager.addComponent(CScale(), "Scale"); launcher.gui_manager.addComponent(CColor(), "Color"); launcher.gui_manager.addComponent(CTexCoords(), "Tex Coords"); launcher.gui_manager.addComponent(CTexCoordsIndex(), "Tex Coords Index"); launcher.gui_manager.addComponent(CVelocity(), "Velocity"); launcher.gui_manager.addComponent(CInput(), "Velocity"); launcher.gui_manager.addComponent(CDamping(), "Damping"); launcher.gui_manager.addComponent(CBall(), "Ball"); launcher.gui_manager.addSystem(MoveSystem.system_id, "Move System"); launcher.gui_manager.addSystem(EdgeCollisionSystem.system_id, "Edge Collision System"); launcher.gui_manager.addSystem(BallCollisionSystem.system_id, "Ball Collision System"); launcher.gui_manager.addSystem(InputMovementSystem.system_id, "Input Movement System"); launcher.gui_manager.addSystem(DampingSystem.system_id, "Damping System"); launcher.gui_manager.addSystem(DamageSystem.system_id, "Damage System"); launcher.gui_manager.addTemplate(brick_tmpl, "Brick"); launcher.gui_manager.addTemplate(big_brick_tmpl, "Big Brick"); launcher.gui_manager.addTemplate(paddle_tmpl, "Paddle"); launcher.gui_manager.addTemplate(ball_tmpl, "Ball"); foreach(i;0..10) { CColor color; final switch(i) { case 0:color = 0x80206020;break; case 1:color = 0x80602020;break; case 2:color = 0x80202060;break; case 3:color = 0x80206060;break; case 4:color = 0x80606020;break; case 5:color = 0x80602060;break; case 6:color = 0x80606060;break; case 7:color = 0x80202020;break; case 8:color = 0x80008030;break; case 9:color = 0x80206080;break; } foreach (j; 0..20) { launcher.manager.addEntity(brick_tmpl,[CLocation(vec2(j*18,300-i*10)).ref_, color.ref_].staticArray); } } launcher.manager.addEntity(paddle_tmpl,[CLocation(vec2(190,20)).ref_].staticArray); launcher.manager.addEntity(ball_tmpl,[CLocation(vec2(190,40)).ref_].staticArray); } void brickBreakerEnd() { demo.texture.destroy(); Mallocator.dispose(demo); } void brickBreakerEvent(SDL_Event* event) { } bool brickBreakerLoop() { launcher.render_position = (vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling - vec2(400,300)) * 0.5; launcher.manager.begin(); if(launcher.multithreading) { launcher.job_updater.begin(); launcher.manager.updateMT(); launcher.job_updater.call(); } else { launcher.manager.update(); } launcher.manager.end(); return true; } DemoCallbacks getBrickBreakerDemo() { DemoCallbacks demo; demo.register = &brickBreakerRegister; demo.initialize = &brickBreakerStart; demo.deinitialize = &brickBreakerEnd; demo.loop = &brickBreakerLoop; demo.tips = .demo.tips; return demo; }