bubel-ecs/demos/source/demos/brick_breaker.d

498 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;
float clamp(float v, float min, float max)
{
if(v<min)return min;
else if (v>max)return max;
else return v;
}
/*#######################################################################################################################
------------------------------------------------ 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)
{
if(id == data.entity[i].id)return true;
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*0.5;
vec2 rel_pos = *location - data.location[i];
vec2 half_scale = *scale * 0.5f;
vec2 nearest_point;
nearest_point.x = clamp(rel_pos.x, -half_scale.x, half_scale.x);
nearest_point.y = clamp(rel_pos.y, -half_scale.y, half_scale.y);
vec2 vector;
if(nearest_point == rel_pos)
{
vector = nearest_point;
radius = float.max;
}
else vector = nearest_point - rel_pos;
float pow_dist = dot(vector, vector);
if(dot(data.velocity[i], vector) > 0.01)return true;
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 cast(bool)(hits--);
}
}
}
return true;
}
EntitiesData data;
uint i;
uint hits;
}
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;
state.hits = 1;
//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.
This demo is usnfinished yet but collision works well. Bricks can be destroyed. Spawning thousands of bricks and then thousands of balls is good way to try demo.
Bricks uses StaticBVH, ball don't collide witch each other, paddle is added to dynamic BVH and collide with balls. But nothing keeps you from adding dynamic collision for balls.
Currently dynamic collisions are pretty slow as dynamic BVH is rebuilded every frame on single thread.";
//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(
[becsID!CLocation, becsID!CScale, becsID!CColor,
becsID!CTexCoordsIndex, becsID!CBVH, becsID!CHitPoints,
becsID!CAABB, becsID!CStatic].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(
[becsID!CLocation, becsID!CScale, becsID!CInput,
becsID!CTexCoordsIndex, becsID!CPaddle, becsID!CVelocity,
becsID!CDamping, becsID!CVelocityFactor, becsID!CBVH,
becsID!CAABB].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(
[becsID!CLocation, becsID!CScale, //becsID!CDamping,
becsID!CTexCoordsIndex, becsID!CBall, becsID!CVelocity].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.addComponent(CBVH(), "BVH");
launcher.gui_manager.addComponent(CAABB(), "AABB");
launcher.gui_manager.addComponent(CStatic(), "Static Flag");
launcher.gui_manager.addSystem(becsID!MoveSystem, "Move System");
launcher.gui_manager.addSystem(becsID!EdgeCollisionSystem, "Edge Collision System");
launcher.gui_manager.addSystem(becsID!BallCollisionSystem, "Ball Collision System");
launcher.gui_manager.addSystem(becsID!InputMovementSystem, "Input Movement System");
launcher.gui_manager.addSystem(becsID!DampingSystem, "Damping System");
launcher.gui_manager.addSystem(becsID!DamageSystem, "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;
}