bubel-ecs/demos/source/demos/brick_breaker.d
Mergul 64dc099e0a Demos big update
-Added some more math functions
-fixed many memory leaks
-added AABB and BVHTree support to collision.d
 *BVHTree has only incrementally adding entities implemented by now (and bad BottomUp algorithm)
 *ECS Systems use two trees, one for static and one for dynamic entities, dynamic BVH is builded every frame from scratch by now
-BrickBreaker now uses BVHTree to collision detection
 *balls only use tree for checks (they aren't adding to tree)
-fixed bug leading to crash
2020-07-17 13:38:41 +02:00

499 lines
No EOL
16 KiB
D

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;
}