498 lines
No EOL
16 KiB
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;
|
|
} |