From 74179b4fc8e0f5a9b8276b9b2b4575b189495bd7 Mon Sep 17 00:00:00 2001 From: Mergul Date: Wed, 8 Jul 2020 22:09:10 +0200 Subject: [PATCH] Demo update -added 'dot' function to vector math -fixed Circle tool rendering -fixed some potentiall memory leaks -added 'collision' module which now separates ShootGrid from SpaceInvaders demo -separate some systems from demos to 'basic' module to better demos functionality sharing -slow down snake -added new graphics -BrickBreaker demo now works (without blocks breaking and with bugged collision detection) -some bug fixes --- demos/assets/textures/atlas.png | Bin 42614 -> 37325 bytes demos/source/app.d | 12 +- demos/source/demos/brick_breaker.d | 284 +++++++++++-- demos/source/demos/particles.d | 118 +++--- demos/source/demos/sandbox.d | 16 +- demos/source/demos/simple.d | 8 +- demos/source/demos/snake.d | 18 +- demos/source/demos/space_invaders.d | 444 ++++----------------- demos/source/game_core/basic.d | 196 +++++++++ demos/source/game_core/collision.d | 232 +++++++++++ demos/source/gui/manager.d | 2 +- demos/source/gui/system.d | 3 +- demos/source/gui/tool_circle.d | 1 + demos/utils/source/ecs_utils/math/vector.d | 5 + 14 files changed, 871 insertions(+), 468 deletions(-) create mode 100644 demos/source/game_core/collision.d diff --git a/demos/assets/textures/atlas.png b/demos/assets/textures/atlas.png index eea925e600dde7ea3133047620c037882fdd4b2c..b84c3fac2893e8d6d59cc78ebf30536c964665ab 100644 GIT binary patch literal 37325 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%0DFoV*h>^r~oo%I8wK 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 literal 42614 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 299)position.y = 299; + //if(position.y < 16)position.y = 16; + //else if(position.y > 299)position.y = 299; *location = position; } manager.addEntity(tmpl); @@ -875,8 +877,6 @@ void mainLoop(void* arg) launcher.renderer.present(); draw_time = launcher.getTime() - draw_time; - //import std.stdio; - //printf("Scalling: %f",launcher.scalling); if(launcher.tool_show)launcher.tool_circle.draw(&launcher.renderer, (launcher.mouse.position*launcher.scalling)-launcher.render_position, cast(float)launcher.tool_size, launcher.renderer.view_size.y*6*launcher.scalling); __gshared float plot_time = 0; @@ -1105,11 +1105,13 @@ int app_main(int argc, char** argv) import demos.simple; import demos.space_invaders; import demos.particles; + import demos.brick_breaker; // launcher.switchDemo(&spaceInvadersStart,&spaceInvadersLoop,&spaceInvadersEnd,&spaceInvadersEvent,SpaceInvaders.tips); // launcher.switchDemo(&particlesStart,&particlesLoop,&particlesEnd,&particlesEvent,ParticlesDemo.tips); // launcher.switchDemo(&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,Simple.tips); // launcher.switchDemo(getParticlesDemo()); - launcher.switchDemo(getSimpleDemo()); + // launcher.switchDemo(getSimpleDemo()); + launcher.switchDemo(getBrickBreakerDemo()); } int key_num; diff --git a/demos/source/demos/brick_breaker.d b/demos/source/demos/brick_breaker.d index 1af22b9..ec4571f 100644 --- a/demos/source/demos/brick_breaker.d +++ b/demos/source/demos/brick_breaker.d @@ -18,9 +18,12 @@ 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 ------------------------------------------------------------------ #######################################################################################################################*/ @@ -51,40 +54,60 @@ struct CBall ubyte radius; } -struct CVelocity -{ - mixin ECS.Component; +// struct CVelocityFactor +// { +// mixin ECS.Component; - alias value this; +// alias value this; - vec2 value; -} +// vec2 value = vec2(1); +// } + +// struct CVelocity +// { +// mixin ECS.Component; + +// alias value this; + +// vec2 value = vec2(0); +// } /*####################################################################################################################### ------------------------------------------------ Systems ------------------------------------------------------------------ #######################################################################################################################*/ -struct MoveSystem -{ - mixin ECS.System!64; +// struct MoveSystem +// { +// mixin ECS.System!64; - struct EntitiesData - { - uint length; - CLocation[] location; - @readonly CVelocity[] velocity; - } +// struct EntitiesData +// { +// uint length; +// CLocation[] location; +// @readonly CVelocity[] velocity; +// @optional @readonly CVelocityFactor[] vel_factor; +// } - void onUpdate(EntitiesData data) - { - foreach(i; 0..data.length) - { - data.location[i] += data.velocity[i] * launcher.delta_time; - } - } -} +// 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 BallCollisionSystem +struct EdgeCollisionSystem { mixin ECS.System!64; @@ -93,13 +116,117 @@ struct BallCollisionSystem 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); + + 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; + } + + ShootGrid* grid; + + bool onBegin() + { + grid = launcher.manager.getSystem!ShootGridManager().grid; + if(grid is null)return false; + else return true; + } + + void onUpdate(EntitiesData data) + { + EntityID id; + foreach(i; 0..data.length) + { + float radius = data.scale[i].x; + //if(grid.test(id, data.location[i], ubyte.max)) + if(grid.test(id, data.location[i] - radius, data.location[i] + radius, ubyte.max)) + { + Entity* entity = launcher.manager.getEntity(id); + if(entity) + { + CLocation* location = entity.getComponent!CLocation; + CScale* scale = entity.getComponent!CScale; + if(location && scale) + { + 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; + } + 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; + } + 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, EBulletHit(data.entity[i].id,data.bullet[i].damage)); + //launcher.manager.removeEntity(data.entity[i].id); + } } } } @@ -118,13 +245,17 @@ struct BrickBreakerDemo __gshared BrickBreakerDemo* demo; -void brickBreakerStart() +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; @@ -132,27 +263,109 @@ void brickBreakerStart() 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.registerSystem!MoveSystem(-100); - launcher.manager.registerSystem!BallCollisionSystem(-99); + launcher.manager.registerSystem!EdgeCollisionSystem(-99); + launcher.manager.registerSystem!BallCollisionSystem(-79); + launcher.manager.registerSystem!InputMovementSystem(-120); + launcher.manager.registerSystem!DampingSystem(-120); launcher.manager.endRegister(); +} - demo.texture.create(); - demo.texture.load("assets/textures/atlas.png"); +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; - launcher.manager.beginRegister(); + EntityTemplate* brick_tmpl = launcher.manager.allocateTemplate( + [CLocation.component_id, CScale.component_id, CColor.component_id, + CTexCoordsIndex.component_id, CShootGrid.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); - launcher.manager.registerComponent!CLocation; + 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, CShootGrid.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(BallCollisionSystem.system_id, "Ball Collision 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); - launcher.manager.endRegister(); } void brickBreakerEnd() { - launcher.manager.getSystem(MoveSystem.system_id).disable(); - launcher.manager.getSystem(DrawSystem.system_id).disable(); - demo.texture.destroy(); Mallocator.dispose(demo); @@ -185,6 +398,7 @@ bool brickBreakerLoop() DemoCallbacks getBrickBreakerDemo() { DemoCallbacks demo; + demo.register = &brickBreakerRegister; demo.initialize = &brickBreakerStart; demo.deinitialize = &brickBreakerEnd; demo.loop = &brickBreakerLoop; diff --git a/demos/source/demos/particles.d b/demos/source/demos/particles.d index 8dac7ff..ec64774 100644 --- a/demos/source/demos/particles.d +++ b/demos/source/demos/particles.d @@ -54,14 +54,14 @@ struct CTexCoords vec4 value; }*/ -struct CVelocity -{ - mixin ECS.Component; +// struct CVelocity +// { +// mixin ECS.Component; - alias value this; +// alias value this; - vec2 value = vec2(0); -} +// vec2 value = vec2(0); +// } struct CForceRange { @@ -85,14 +85,14 @@ struct CVortex float strength = 0.6; } -struct CDamping -{ - mixin ECS.Component; +// struct CDamping +// { +// mixin ECS.Component; - alias power this; +// alias power this; - @GUIRange(0,9) ubyte power = 0; -} +// @GUIRange(0,9) ubyte power = 0; +// } struct CGravity { @@ -165,25 +165,25 @@ struct DrawSystem } }*/ -struct MoveSystem -{ - mixin ECS.System!64; +// struct MoveSystem +// { +// mixin ECS.System!64; - struct EntitiesData - { - uint length; - CLocation[] locations; - @readonly CVelocity[] velocity; - } +// struct EntitiesData +// { +// uint length; +// CLocation[] locations; +// @readonly CVelocity[] velocity; +// } - void onUpdate(EntitiesData data) - { - foreach(i; 0..data.length) - { - data.locations[i] += data.velocity[i] * launcher.delta_time; - } - } -} +// void onUpdate(EntitiesData data) +// { +// foreach(i; 0..data.length) +// { +// data.locations[i] += data.velocity[i] * launcher.delta_time; +// } +// } +// } struct MouseAttractSystem { @@ -347,38 +347,38 @@ struct PlayAreaSystem } } -struct DampingSystem -{ - mixin ECS.System!32; +// struct DampingSystem +// { +// mixin ECS.System!32; - struct EntitiesData - { - uint length; - const (Entity)[] entity; - @readonly CDamping[] damping; - CVelocity[] velocity; - } +// struct EntitiesData +// { +// uint length; +// const (Entity)[] entity; +// @readonly CDamping[] damping; +// CVelocity[] velocity; +// } - float[10] damp = 0; +// float[10] damp = 0; - bool onBegin() - { - foreach(i;0..10) - { - damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.delta_time*0.1); - } +// bool onBegin() +// { +// foreach(i;0..10) +// { +// damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.delta_time*0.1); +// } - return true; - } +// return true; +// } - void onUpdate(EntitiesData data) - { - foreach(i; 0..data.length) - { - data.velocity[i] = data.velocity[i] * damp[data.damping[i]]; - } - } -} +// void onUpdate(EntitiesData data) +// { +// foreach(i; 0..data.length) +// { +// data.velocity[i] = data.velocity[i] * damp[data.damping[i]]; +// } +// } +// } struct ParticleLifeSystem { @@ -444,7 +444,7 @@ struct ParticlesDemo __gshared ParticlesDemo* particles_demo; -void particlesStart() +void particlesRegister() { particles_demo = Mallocator.make!ParticlesDemo; @@ -484,7 +484,10 @@ void particlesStart() launcher.manager.registerSystem!AttractorIterator(-1); launcher.manager.endRegister(); +} +void particlesStart() +{ DrawSystem* draw_system = launcher.manager.getSystem!DrawSystem; draw_system.default_data.size = vec2(2,2); draw_system.default_data.coords = vec4(246,64,2,2)*px; @@ -579,6 +582,7 @@ bool particlesLoop() DemoCallbacks getParticlesDemo() { DemoCallbacks demo; + demo.register = &particlesRegister; demo.initialize = &particlesStart; demo.deinitialize = &particlesEnd; demo.loop = &particlesLoop; diff --git a/demos/source/demos/sandbox.d b/demos/source/demos/sandbox.d index 69a2ef2..925ebb2 100644 --- a/demos/source/demos/sandbox.d +++ b/demos/source/demos/sandbox.d @@ -6,6 +6,7 @@ import demos.simple; import demos.snake; import demos.space_invaders; import demos.particles; +import demos.brick_breaker; import game_core.rendering; @@ -17,10 +18,17 @@ extern(C): void sandboxStart() { + simpleRegister(); + snakeRegister(); + spaceInvadersRegister(); + particlesRegister(); + brickBreakerRegister(); + simpleStart(); snakeStart(); spaceInvadersStart(); particlesStart(); + brickBreakerStart(); DrawSystem* draw_system = launcher.manager.getSystem!DrawSystem; draw_system.default_data.size = vec2(16,16); @@ -28,6 +36,10 @@ void sandboxStart() draw_system.default_data.material_id = 0; draw_system.default_data.texture = particles_demo.texture; draw_system.default_data.color = 0x80808080; + + launcher.manager.getSystem(MouseAttractSystem.system_id).disable(); + launcher.manager.getSystem(demos.simple.MoveSystem.system_id).disable(); + } void sandboxEnd() @@ -56,9 +68,9 @@ bool sandboxLoop() else */ time += delta_time; - while(time > 100) + while(time > 200) { - time -= 100; + time -= 200; launcher.manager.update("fixed"); } diff --git a/demos/source/demos/simple.d b/demos/source/demos/simple.d index 1aafcb2..ecccc12 100644 --- a/demos/source/demos/simple.d +++ b/demos/source/demos/simple.d @@ -107,7 +107,7 @@ struct Simple __gshared Simple* simple; -void simpleStart() +void simpleRegister() { simple = Mallocator.make!Simple; @@ -124,7 +124,10 @@ void simpleStart() // launcher.manager.registerSystem!DrawSystem(1); launcher.manager.endRegister(); - +} + +void simpleStart() +{ DrawSystem* draw_system = launcher.manager.getSystem!DrawSystem; draw_system.default_data.color = 0x80808080; draw_system.default_data.texture = simple.texture; @@ -198,6 +201,7 @@ bool simpleLoop() DemoCallbacks getSimpleDemo() { DemoCallbacks demo; + demo.register = &simpleRegister; demo.initialize = &simpleStart; demo.deinitialize = &simpleEnd; demo.loop = &simpleLoop; diff --git a/demos/source/demos/snake.d b/demos/source/demos/snake.d index 8ff0be6..525b4ab 100644 --- a/demos/source/demos/snake.d +++ b/demos/source/demos/snake.d @@ -235,10 +235,10 @@ struct CMovement Direction direction; } -struct CInput -{ - mixin ECS.Component; -} +// struct CInput +// { +// mixin ECS.Component; +// } struct AppleSystem { @@ -849,7 +849,7 @@ struct CopyLocationSystem __gshared Snake* snake; -void snakeStart() +void snakeRegister() { import game_core.rendering; @@ -890,7 +890,10 @@ void snakeStart() launcher.manager.registerSystem!SnakeSystem(101); launcher.manager.endRegister(); +} +void snakeStart() +{ launcher.gui_manager.addComponent(CApple(),"Apple"); launcher.gui_manager.addComponent(CSnake(),"Snake"); launcher.gui_manager.addComponent(CParticle(1000),"Particle"); @@ -975,9 +978,9 @@ bool snakeLoop() if(launcher.getKeyState(SDL_SCANCODE_SPACE))time += delta_time * 3; else time += delta_time; - while(time > 100) + while(time > 200) { - time -= 100; + time -= 200; launcher.manager.update("fixed"); } @@ -992,6 +995,7 @@ bool snakeLoop() DemoCallbacks getSnakeDemo() { DemoCallbacks demo; + demo.register = &snakeRegister; demo.initialize = &snakeStart; demo.deinitialize = &snakeEnd; demo.loop = &snakeLoop; diff --git a/demos/source/demos/space_invaders.d b/demos/source/demos/space_invaders.d index b249fc1..66be099 100644 --- a/demos/source/demos/space_invaders.d +++ b/demos/source/demos/space_invaders.d @@ -18,6 +18,7 @@ import ecs_utils.utils; import game_core.basic; import game_core.rendering; +import game_core.collision; //import std.math : PI; @@ -29,8 +30,6 @@ private enum float px = 1.0/512.0; extern(C): -enum ShootGridDependency = "ShootGridDependency"; - /*####################################################################################################################### ------------------------------------------------ Types ------------------------------------------------------------------ #######################################################################################################################*/ @@ -57,7 +56,7 @@ struct SpaceInvaders ~this() @nogc nothrow { - if(shoot_grid)Mallocator.dispose(shoot_grid); + // if(shoot_grid)Mallocator.dispose(shoot_grid); if(enemy_tmpl)launcher.manager.freeTemplate(enemy_tmpl); if(ship_tmpl)launcher.manager.freeTemplate(ship_tmpl); if(laser_tmpl)launcher.manager.freeTemplate(laser_tmpl); @@ -163,28 +162,14 @@ struct CTexture vec4 coords = vec4(0,0,0,1); }*/ -struct CColliderScale -{ - mixin ECS.Component; +// struct CVelocity +// { +// mixin ECS.Component; - alias value this; +// alias value this; - vec2 value = vec2(16,16); -} - -struct CVelocity -{ - mixin ECS.Component; - - alias value this; - - vec2 value = vec2(0,0); -} - -struct CInput -{ - mixin ECS.Component; -} +// vec2 value = vec2(0,0); +// } struct CEnemy { @@ -265,11 +250,6 @@ struct CSideMove byte group = -1; } -struct CShootGrid -{ - mixin ECS.Component; -} - struct CTargetParent { mixin ECS.Component; @@ -336,14 +316,7 @@ struct CAnimationLooped mixin ECS.Component; } -struct CDamping -{ - mixin ECS.Component; - alias value this; - - byte value = 0; -} struct CParticle { @@ -514,163 +487,6 @@ struct EDestroyedChild ------------------------------------------------ Systems ------------------------------------------------------------------ #######################################################################################################################*/ -struct ShootGrid -{ - - ~this() @nogc nothrow - { - if(nodes)Mallocator.dispose(nodes); - if(masks)Mallocator.dispose(masks); - } - - struct Node - { - alias entity this; - - EntityID entity; - } - - void create(ivec2 nodes_count, vec2 node_size) - { - this.size = nodes_count; - this.node_size = node_size; - inv_node_size = vec2(1.0/node_size.x, 1.0/node_size.y); - nodes = Mallocator.makeArray!Node(nodes_count.x * nodes_count.y); - masks = Mallocator.makeArray!ubyte(nodes.length); - } - - void mark(EntityID id, vec2 beg, vec2 end, byte mask) - { - ivec2 ibeg = cast(ivec2)(beg * inv_node_size); - ivec2 iend = cast(ivec2)((end * inv_node_size) + 0.5); - if(ibeg.x < 0)ibeg.x = 0; - if(ibeg.y < 0)ibeg.y = 0; - if(iend.x > size.x)iend.x = size.x; - if(iend.y > size.y)iend.y = size.y; - foreach(i; ibeg.y .. iend.y) - { - foreach(j; ibeg.x .. iend.x) - { - nodes[i * size.x + j] = id; - masks[i * size.x +j] = mask; - } - } - } - - void clear() - { - size_t size = nodes.length * EntityID.sizeof; - memset(nodes.ptr, 0, size); - memset(masks.ptr, 0, masks.length); - } - - bool test(out EntityID id, vec2 beg, vec2 end) - { - ivec2 ibeg = cast(ivec2)(beg * inv_node_size); - ivec2 iend = cast(ivec2)((end * inv_node_size) + 0.5); - if(ibeg.x < 0)ibeg.x = 0; - if(ibeg.y < 0)ibeg.y = 0; - if(iend.x > size.x)iend.x = size.x; - if(iend.y > size.y)iend.y = size.y; - foreach(i; ibeg.y .. iend.y) - { - foreach(j; ibeg.x .. iend.x) - { - if(nodes[i * size.x + j].id != 0) - { - id = nodes[i * size.x + j]; - return true; - } - } - } - return false; - } - - bool test(out EntityID id, vec2 pos, ubyte mask) - { - ivec2 ipos = cast(ivec2)(pos * inv_node_size - 0.5); - if(ipos.x < 0)ipos.x = 0; - if(ipos.y < 0)ipos.y = 0; - if(ipos.x >= size.x)ipos.x = size.x - 1; - if(ipos.y >= size.y)ipos.y = size.y - 1; - size_t index = ipos.y * size.x + ipos.x; - if((masks[index] & mask) == 0)return false; - if(nodes[index].id != 0) - { - id = nodes[index]; - return true; - } - return false; - } - - vec2 inv_node_size; - ivec2 size; - vec2 node_size; - Node[] nodes; - ubyte[] masks; -} - -struct ShootGridCleaner -{ - mixin ECS.System!1; - - struct EntitiesData - { - - } - - void onUpdate(EntitiesData data) - { - if(space_invaders.shoot_grid)space_invaders.shoot_grid.clear(); - } -} - -struct ShootGridManager -{ - mixin ECS.System!128; - - mixin ECS.WritableDependencies!(ShootGridDependency); - - struct EntitiesData - { - uint length; - //uint thread_id; - const (Entity)[] entity; - @readonly CLocation[] locations; - @readonly CShootGrid[] grid_flag; - @readonly CGuild[] guild; - @optional @readonly CScale[] scale; - @optional @readonly CColliderScale[] collider_scale; - } - - ShootGrid* grid; - - void onCreate() - { - grid = space_invaders.shoot_grid; - } - - bool onBegin() - { - if(!grid)return false; - //grid.clear(); - return true; - } - - void onUpdate(EntitiesData data) - { - vec2[] scale; - if(data.collider_scale)scale = cast(vec2[])data.collider_scale; - else if(data.scale)scale = cast(vec2[])data.scale; - else return; - foreach(i; 0..data.length) - { - vec2 half_scale = scale[i] * 0.5; - grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, cast(ubyte)(1 << data.guild[i].guild)); - } - } -} - struct ParentOwnerSystem { mixin ECS.System; @@ -833,7 +649,7 @@ struct ShipWeaponSystem CTexCoords.component_id, CScale.component_id, CEnemy.component_id, CShootGrid.component_id, CGuild.component_id, CInit.component_id, CChildren.component_id, CDepth.component_id, CTargetParent.component_id, - CSpawnUponDeath.component_id, CShootWaveUponDeath.component_id].staticArray + CSpawnUponDeath.component_id, CShootWaveUponDeath.component_id, CShootGridMask.component_id].staticArray ); /*CTexCoords* tex_comp = tower1_tmpl.getComponent!CTexCoords; @@ -1215,8 +1031,8 @@ struct ShootingSystem laser.shoot_time -= CWeapon.levels[laser.level - 1].reload_time; laser_location.value = data.location[i]; - laser_velocity.value = vec2((randomf()*2-1) * CWeapon.levels[laser.level - 1].dispersion,1);//data.shoot_direction[i].direction == Direction.up ? 1.0 : -1.0); - if(data.shoot_direction && data.shoot_direction[i].direction == Direction.down)laser_velocity.y = -1; + laser_velocity.value = vec2((randomf()*2-1) * CWeapon.levels[laser.level - 1].dispersion,0.5);//data.shoot_direction[i].direction == Direction.up ? 1.0 : -1.0); + if(data.shoot_direction && data.shoot_direction[i].direction == Direction.down)laser_velocity.y = -0.5; laser_guild.guild = data.guild[i].guild; @@ -1280,39 +1096,6 @@ struct ShootingSystem } } -struct DampingSystem -{ - mixin ECS.System!32; - - struct EntitiesData - { - uint length; - const (Entity)[] entity; - @readonly CDamping[] damping; - CVelocity[] velocity; - } - - float[10] damp = 0; - - bool onBegin() - { - foreach(i;0..10) - { - damp[i] = powf((0.98 - cast(float)i * 0.02),launcher.delta_time*0.1); - } - - return true; - } - - void onUpdate(EntitiesData data) - { - foreach(i; 0..data.length) - { - data.velocity[i] = data.velocity[i] * damp[data.damping[i]]; - } - } -} - struct BulletsCollisionSystem { mixin ECS.System!32; @@ -1343,6 +1126,29 @@ struct BulletsCollisionSystem } } +struct CollisionMaskSystem +{ + mixin ECS.System; + + mixin ECS.ReadOnlyDependencies!(ShootGridDependency); + + struct EntitiesData + { + ///variable named "length" contain entites count + uint length; + CShootGridMask[] mask; + @readonly CGuild[] guild; + } + + void onAddEntity(EntitiesData data) + { + foreach(i;0..data.length) + { + data.mask[i] = cast(ubyte)(1 << data.guild[i].guild); + } + } +} + struct ParticleEmittingSystem { mixin ECS.System!32; @@ -1670,7 +1476,7 @@ struct HitPointsSystem //tex_comp.tex = space_invaders.texture;//ship_tex; upgrade_tmpl.getComponent!CTexCoords().value = vec4(0*px,32*px,16*px,16*px); *upgrade_tmpl.getComponent!CAnimation = CAnimation(upgrade_laser_frames, 0, 1); - upgrade_tmpl.getComponent!CVelocity().value = vec2(0,-0.1); + upgrade_tmpl.getComponent!CVelocity().value = vec2(0,-0.05); explosion_tmpl = launcher.manager.allocateTemplate( [CDepth.component_id, CParticle.component_id, CLocation.component_id, @@ -1975,32 +1781,32 @@ struct ClampPositionSystem } } -struct MovementSystem -{ - mixin ECS.System!32; +// struct MovementSystem +// { +// mixin ECS.System!32; - struct EntitiesData - { - uint length; - //read only components can be marked with @readonly attribute or with const expression instead - const (CVelocity)[] velocity; - //components are treated as required by default - CLocation[] locations; - //@optional const (CBullet)[] laser; - const (Entity)[] entities; +// struct EntitiesData +// { +// uint length; +// //read only components can be marked with @readonly attribute or with const expression instead +// const (CVelocity)[] velocity; +// //components are treated as required by default +// CLocation[] locations; +// //@optional const (CBullet)[] laser; +// const (Entity)[] entities; - //@optional CSideMove[] side_move; - } +// //@optional CSideMove[] side_move; +// } - void onUpdate(EntitiesData data) - { - foreach(i;0..data.length) - { - data.locations[i].x += data.velocity[i].x * launcher.delta_time * 0.5; - data.locations[i].y += data.velocity[i].y * launcher.delta_time * 0.5; - } - } -} +// void onUpdate(EntitiesData data) +// { +// foreach(i;0..data.length) +// { +// data.locations[i].x += data.velocity[i].x * launcher.delta_time * 0.5; +// data.locations[i].y += data.velocity[i].y * launcher.delta_time * 0.5; +// } +// } +// } struct AnimationSystem { @@ -2229,114 +2035,20 @@ struct CShipIterator //void handleEvent(Entity* entity, ) }//*/ -/** -*System is responsible for movement of objects with CInput component. -*In this example every entity has same speed when using movement system. -*/ -struct InputMovementSystem -{ - mixin ECS.System!32; - - vec2 move_vector; - - struct EntitiesData - { - uint length; - //read only components can be marked with @readonly attribute or with const expression instead - const (CInput)[] input; - //components are treated as required by default - //CLocation[] locations; - CVelocity[] velocity; - //CTexture[] textures; - } - - /** - *onBegin gives opportunity to check keys once and call update on entities only when - *one key is pressed. - */ - bool onBegin() - { - move_vector = vec2(0,0); - if(launcher.getKeyState(SDL_SCANCODE_W)) - { - move_vector += vec2(0,1); - } - else if(launcher.getKeyState(SDL_SCANCODE_S)) - { - move_vector += vec2(0,-1); - } - if(launcher.getKeyState(SDL_SCANCODE_A)) - { - move_vector += vec2(-1,0); - } - else if(launcher.getKeyState(SDL_SCANCODE_D)) - { - move_vector += vec2(1,0); - } - - if(move_vector.x != 0 ||move_vector.y != 0) - { - move_vector = move_vector / sqrtf(move_vector.x * move_vector.x + move_vector.y * move_vector.y); - return true; - } - //don't call system update because no key pressed - return false; - } - - /** - *Update is called multiple times in one "manager.update()" call. - *Number of "onUpdate" calls is count of buffers which must be updated during pass. - *When multithreading is used, number of "onUpdate" calls can be greater due to fact that - *JobSystem can split buffers for better data packing. - */ - void onUpdate(EntitiesData data) - { - /*if(move_vector.x == 0) - { - foreach(i; 0..data.length) - { - data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); - } - //return; - }*/ - //move every entity using movement vector - //if(move_vector.x != 0 || move_vector.y != 0) - foreach(i; 0..data.length) - { - data.velocity[i] += move_vector * launcher.delta_time * 0.005; - if(data.velocity[i].x > 0.5)data.velocity[i].x = 0.5; - else if(data.velocity[i].x < -0.5)data.velocity[i].x = -0.5; - if(data.velocity[i].y > 0.5)data.velocity[i].y = 0.5; - else if(data.velocity[i].y < -0.5)data.velocity[i].y = -0.5; - //data.locations[i].x += move_vector.x * launcher.delta_time * 0.25; - //data.locations[i].y += move_vector.y * launcher.delta_time * 0.25; - //if(move_vector.x > 0)data.textures[i].coords = vec4(48*px,80*px,48*px,32*px); - //else data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); - } - /*else - foreach(i; 0..data.length) - { - data.velocity[i] = vec2(0,0); - }*/ - } -} - /*####################################################################################################################### ------------------------------------------------ Functions ------------------------------------------------------------------ #######################################################################################################################*/ __gshared SpaceInvaders* space_invaders; -void spaceInvadersStart() +void spaceInvadersRegister() { + space_invaders = Mallocator.make!SpaceInvaders; space_invaders.texture.create(); space_invaders.texture.load("assets/textures/atlas.png"); - space_invaders.shoot_grid = Mallocator.make!ShootGrid; - space_invaders.shoot_grid.create(ivec2(80,60), vec2(5,5)); - launcher.manager.beginRegister(); launcher.manager.registerDependency(ShootGridDependency); @@ -2381,6 +2093,7 @@ void spaceInvadersStart() launcher.manager.registerComponent!CParticleEmitterTime; launcher.manager.registerComponent!CSpawnUponDeath; launcher.manager.registerComponent!CShootWaveUponDeath; + launcher.manager.registerComponent!CShootGridMask; launcher.manager.registerEvent!EChangeDirection; launcher.manager.registerEvent!EDamage; @@ -2392,7 +2105,8 @@ void spaceInvadersStart() //launcher.manager.registerSystem!MoveSystem(0); launcher.manager.registerSystem!DrawSystem(100); launcher.manager.registerSystem!InputMovementSystem(-100); - launcher.manager.registerSystem!MovementSystem(-99); + //launcher.manager.registerSystem!MovementSystem(-99); + launcher.manager.registerSystem!MoveSystem(-99); launcher.manager.registerSystem!ClampPositionSystem(-90); launcher.manager.registerSystem!ShootingSystem(0); launcher.manager.registerSystem!ChangeDirectionSystem(0); @@ -2417,8 +2131,18 @@ void spaceInvadersStart() launcher.manager.registerSystem!ChildDestroySystem(-110); launcher.manager.registerSystem!ShootWaveSystem(-100); //launcher.manager.registerSystem!SpawnUponDeathSystem(-110); + launcher.manager.registerSystem!CollisionMaskSystem(-100); launcher.manager.endRegister(); +} + +void spaceInvadersStart() +{ + + // space_invaders.shoot_grid = Mallocator.make!ShootGrid; + // space_invaders.shoot_grid.create(ivec2(80,60), vec2(5,5)); + + space_invaders.shoot_grid = launcher.manager.getSystem!ShootGridManager().grid; DrawSystem* draw_system = launcher.manager.getSystem!DrawSystem; draw_system.default_data.color = 0x80808080; @@ -2463,7 +2187,8 @@ void spaceInvadersStart() launcher.gui_manager.addSystem(DrawSystem.system_id,"Draw System"); launcher.gui_manager.addSystem(InputMovementSystem.system_id,"Input Movement"); launcher.gui_manager.addSystem(ShootingSystem.system_id,"Shooting System"); - launcher.gui_manager.addSystem(MovementSystem.system_id,"Movement System"); + //launcher.gui_manager.addSystem(MovementSystem.system_id,"Movement System"); + launcher.gui_manager.addSystem(MoveSystem.system_id,"Move System"); launcher.gui_manager.addSystem(ClampPositionSystem.system_id,"Clamp Position System"); launcher.gui_manager.addSystem(ChangeDirectionSystem.system_id,"Change Direction System"); launcher.gui_manager.addSystem(BulletsCollisionSystem.system_id,"Bullets Collision System"); @@ -2494,14 +2219,16 @@ void spaceInvadersStart() CLocation.component_id, CTexCoords.component_id, CInput.component_id, CShip.component_id, CScale.component_id, CColliderScale.component_id, CShootDirection.component_id, CShootGrid.component_id, CGuild.component_id, - CDamping.component_id, CChildren.component_id, CInit.component_id].staticArray + CDamping.component_id, CChildren.component_id, CInit.component_id, + CShootGridMask.component_id, CVelocityFactor.component_id].staticArray ); space_invaders.ship_tmpl.getComponent!CTexCoords().value = vec4(0,80,48,32)*px; space_invaders.ship_tmpl.getComponent!CScale().value = vec2(48,32); space_invaders.ship_tmpl.getComponent!CHitPoints().value = 1000; - space_invaders.ship_tmpl.getComponent!CDamping().value = 7; + space_invaders.ship_tmpl.getComponent!CDamping().value = 14; space_invaders.ship_tmpl.getComponent!CInit().type = CInit.Type.space_ship; space_invaders.ship_tmpl.getComponent!CColliderScale().value = vec2(26,24); + space_invaders.ship_tmpl.getComponent!CVelocityFactor().value = vec2(0.5,0.5); launcher.manager.addEntity(space_invaders.ship_tmpl,[CLocation(vec2(64,64)).ref_].staticArray); } @@ -2512,7 +2239,7 @@ void spaceInvadersStart() space_invaders.laser_tmpl.getComponent!CTexCoords().value = vec4(0,24,2,8)*px; space_invaders.laser_tmpl.getComponent!CScale().value = vec2(2,8); - space_invaders.laser_tmpl.getComponent!CVelocity().value = vec2(0,1); + space_invaders.laser_tmpl.getComponent!CVelocity().value = vec2(0,0.5); } EntityTemplate* enemy_tmpl; @@ -2543,7 +2270,7 @@ void spaceInvadersStart() boss_tmpl.getComponent!CScale().value = vec2(96,48); boss_tmpl.getComponent!CDepth().value = -1; boss_tmpl.getComponent!CParts().count = 4; - boss_tmpl.getComponent!CVelocity().value = vec2(0.05,0); + boss_tmpl.getComponent!CVelocity().value = vec2(0.025,0); } { @@ -2551,7 +2278,7 @@ void spaceInvadersStart() [CColor.component_id, CHitMark.component_id, CHitPoints.component_id, CLocation.component_id, CTexCoords.component_id, CScale.component_id, CEnemy.component_id, CShootGrid.component_id, CGuild.component_id, CInit.component_id, - CChildren.component_id].staticArray + CChildren.component_id, CShootGridMask.component_id].staticArray ); tower_tmpl.getComponent!CTexCoords().value = vec4(96,96,16,16)*px; @@ -2566,12 +2293,12 @@ void spaceInvadersStart() CVelocity.component_id, CAutoShoot.component_id, CLocation.component_id, CTexCoords.component_id, CScale.component_id, CWeapon.component_id, CEnemy.component_id, CShootDirection.component_id, CShootGrid.component_id, - CGuild.component_id].staticArray + CGuild.component_id, CShootGridMask.component_id].staticArray ); space_invaders.enemy_tmpl.getComponent!CTexCoords().value = vec4(32,32,16,16)*px; space_invaders.enemy_tmpl.getComponent!CShootDirection().direction = Direction.down; - space_invaders.enemy_tmpl.getComponent!CVelocity().value = vec2(0.1,0); + space_invaders.enemy_tmpl.getComponent!CVelocity().value = vec2(0.05,0); space_invaders.enemy_tmpl.getComponent!CGuild().guild = 1; space_invaders.enemy_tmpl.getComponent!CWeaponLocation().rel_pos = vec2(0,-15); @@ -2603,7 +2330,7 @@ void spaceInvadersStart() { upgrade_tmpl = launcher.manager.allocateTemplate([CVelocity.component_id, CLocation.component_id, CTexCoords.component_id, CScale.component_id, CUpgrade.component_id, CAnimationLooped.component_id, CAnimation.component_id].staticArray); upgrade_tmpl.getComponent!CTexCoords().value = vec4(0,32,16,16)*px; - upgrade_tmpl.getComponent!CVelocity().value = vec2(0,-0.1); + upgrade_tmpl.getComponent!CVelocity().value = vec2(0,-0.05); *upgrade_tmpl.getComponent!CAnimation = CAnimation(HitPointsSystem.upgrade_laser_frames, 0, 0.75); } @@ -2746,6 +2473,7 @@ bool spaceInvadersLoop() DemoCallbacks getSpaceInvadersDemo() { DemoCallbacks demo; + demo.register = &spaceInvadersRegister; demo.initialize = &spaceInvadersStart; demo.deinitialize = &spaceInvadersEnd; demo.loop = &spaceInvadersLoop; diff --git a/demos/source/game_core/basic.d b/demos/source/game_core/basic.d index 156680b..b47ba52 100644 --- a/demos/source/game_core/basic.d +++ b/demos/source/game_core/basic.d @@ -1,11 +1,18 @@ module game_core.basic; import bubel.ecs.core; +import bubel.ecs.attributes; import ecs_utils.math.vector; import gui.attributes; +import ecs_utils.utils; + +import app : launcher; + +import bindbc.sdl; + struct CLocation { mixin ECS.Component; @@ -56,4 +63,193 @@ struct CSelected mixin ECS.Component; bool value = false; +} + +struct CInput +{ + mixin ECS.Component; +} + +struct CDamping +{ + mixin ECS.Component; + + alias value this; + + @GUIRange(0,9) byte value = 1; +} + +struct CVelocity +{ + mixin ECS.Component; + + alias value this; + + vec2 value = vec2(0,0); +} + +struct CVelocityFactor +{ + mixin ECS.Component; + + alias value this; + + vec2 value = vec2(1); +} + + +struct DampingSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + const (Entity)[] entity; + @readonly CDamping[] damping; + CVelocity[] velocity; + } + + float[20] damp = 0; + + bool onBegin() + { + foreach(i;0..20) + { + damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.delta_time*0.1); + } + + return true; + } + + void onUpdate(EntitiesData data) + { + foreach(i; 0..data.length) + { + data.velocity[i] = data.velocity[i] * damp[data.damping[i]]; + } + } +} + +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; + } + } + } +} + +/** +*System is responsible for movement of objects with CInput component. +*In this example every entity has same speed when using movement system. +*/ +struct InputMovementSystem +{ + mixin ECS.System!32; + + vec2 move_vector; + + struct EntitiesData + { + uint length; + //read only components can be marked with @readonly attribute or with const expression instead + const (CInput)[] input; + //components are treated as required by default + //CLocation[] locations; + CVelocity[] velocity; + //CTexture[] textures; + } + + /** + *onBegin gives opportunity to check keys once and call update on entities only when + *one key is pressed. + */ + bool onBegin() + { + move_vector = vec2(0,0); + if(launcher.getKeyState(SDL_SCANCODE_W)) + { + move_vector += vec2(0,1); + } + else if(launcher.getKeyState(SDL_SCANCODE_S)) + { + move_vector += vec2(0,-1); + } + if(launcher.getKeyState(SDL_SCANCODE_A)) + { + move_vector += vec2(-1,0); + } + else if(launcher.getKeyState(SDL_SCANCODE_D)) + { + move_vector += vec2(1,0); + } + + if(move_vector.x != 0 ||move_vector.y != 0) + { + move_vector = move_vector / sqrtf(move_vector.x * move_vector.x + move_vector.y * move_vector.y); + return true; + } + //don't call system update because no key pressed + return false; + } + + /** + *Update is called multiple times in one "manager.update()" call. + *Number of "onUpdate" calls is count of buffers which must be updated during pass. + *When multithreading is used, number of "onUpdate" calls can be greater due to fact that + *JobSystem can split buffers for better data packing. + */ + void onUpdate(EntitiesData data) + { + /*if(move_vector.x == 0) + { + foreach(i; 0..data.length) + { + data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); + } + //return; + }*/ + //move every entity using movement vector + //if(move_vector.x != 0 || move_vector.y != 0) + foreach(i; 0..data.length) + { + data.velocity[i] += move_vector * launcher.delta_time * 0.005; + if(data.velocity[i].x > 0.5)data.velocity[i].x = 0.5; + else if(data.velocity[i].x < -0.5)data.velocity[i].x = -0.5; + if(data.velocity[i].y > 0.5)data.velocity[i].y = 0.5; + else if(data.velocity[i].y < -0.5)data.velocity[i].y = -0.5; + //data.locations[i].x += move_vector.x * launcher.delta_time * 0.25; + //data.locations[i].y += move_vector.y * launcher.delta_time * 0.25; + //if(move_vector.x > 0)data.textures[i].coords = vec4(48*px,80*px,48*px,32*px); + //else data.textures[i].coords = vec4(0*px,80*px,48*px,32*px); + } + /*else + foreach(i; 0..data.length) + { + data.velocity[i] = vec2(0,0); + }*/ + } } \ No newline at end of file diff --git a/demos/source/game_core/collision.d b/demos/source/game_core/collision.d new file mode 100644 index 0000000..75c46f8 --- /dev/null +++ b/demos/source/game_core/collision.d @@ -0,0 +1,232 @@ +module game_core.collision; + +import bubel.ecs.attributes; +import bubel.ecs.core; +import bubel.ecs.std; + +import ecs_utils.math.vector; + +import game_core.basic; + + +void registerCollisionModule(EntityManager* manager) +{ + manager.registerDependency(ShootGridDependency); + + manager.registerComponent!CShootGrid; + manager.registerComponent!CShootGridMask; + manager.registerComponent!CColliderScale; + + manager.registerSystem!ShootGridManager(-80); + manager.registerSystem!ShootGridCleaner(-101); +} + +enum ShootGridDependency = "ShootGridDependency"; + +struct CShootGrid +{ + mixin ECS.Component; +} + +struct CShootGridMask +{ + mixin ECS.Component; + + alias value this; + + ubyte value; +} + +struct CColliderScale +{ + mixin ECS.Component; + + alias value this; + + vec2 value = vec2(16,16); +} + +struct ShootGrid +{ + + ~this() @nogc nothrow + { + if(nodes)Mallocator.dispose(nodes); + if(masks)Mallocator.dispose(masks); + } + + struct Node + { + alias entity this; + + EntityID entity; + } + + void create(ivec2 nodes_count, vec2 node_size) + { + this.size = nodes_count; + this.node_size = node_size; + inv_node_size = vec2(1.0/node_size.x, 1.0/node_size.y); + nodes = Mallocator.makeArray!Node(nodes_count.x * nodes_count.y); + masks = Mallocator.makeArray!ubyte(nodes.length); + } + + void mark(EntityID id, vec2 beg, vec2 end, ubyte mask) + { + ivec2 ibeg = cast(ivec2)(beg * inv_node_size); + ivec2 iend = cast(ivec2)(end * inv_node_size + 0.5); + if(ibeg.x < 0)ibeg.x = 0; + if(ibeg.y < 0)ibeg.y = 0; + if(iend.x > size.x)iend.x = size.x; + if(iend.y > size.y)iend.y = size.y; + foreach(i; ibeg.y .. iend.y) + { + foreach(j; ibeg.x .. iend.x) + { + nodes[i * size.x + j] = id; + masks[i * size.x +j] = mask; + } + } + } + + void clear() + { + size_t size = nodes.length * EntityID.sizeof; + memset(nodes.ptr, 0, size); + memset(masks.ptr, 0, masks.length); + } + + bool test(out EntityID id, vec2 beg, vec2 end, ubyte mask) + { + ivec2 ibeg = cast(ivec2)(beg * inv_node_size); + ivec2 iend = cast(ivec2)(end * inv_node_size + 0.5); + if(ibeg.x < 0)ibeg.x = 0; + if(ibeg.y < 0)ibeg.y = 0; + if(iend.x > size.x)iend.x = size.x; + if(iend.y > size.y)iend.y = size.y; + foreach(i; ibeg.y .. iend.y) + { + foreach(j; ibeg.x .. iend.x) + { + uint index = i * size.x + j; + if(nodes[index].id != 0) + { + if((masks[index] & mask) == 0)continue; + id = nodes[index]; + return true; + } + } + } + return false; + } + + bool test(out EntityID id, vec2 pos, ubyte mask) + { + ivec2 ipos = cast(ivec2)(pos * inv_node_size - 0.5); + if(ipos.x < 0)ipos.x = 0; + if(ipos.y < 0)ipos.y = 0; + if(ipos.x >= size.x)ipos.x = size.x - 1; + if(ipos.y >= size.y)ipos.y = size.y - 1; + size_t index = ipos.y * size.x + ipos.x; + if((masks[index] & mask) == 0)return false; + if(nodes[index].id != 0) + { + id = nodes[index]; + return true; + } + return false; + } + + vec2 inv_node_size; + ivec2 size; + vec2 node_size; + Node[] nodes; + ubyte[] masks; +} + +struct ShootGridCleaner +{ + mixin ECS.System!1; + + struct EntitiesData + { + + } + + ShootGrid* grid; + + bool onBegin() + { + grid = gEM.getSystem!ShootGridManager().grid; + if(grid != null)return true; + else return false; + } + + void onUpdate(EntitiesData data) + { + if(grid)grid.clear(); + } +} + +struct ShootGridManager +{ + mixin ECS.System!128; + + mixin ECS.WritableDependencies!(ShootGridDependency); + + struct EntitiesData + { + uint length; + //uint thread_id; + const (Entity)[] entity; + @readonly CLocation[] locations; + @readonly CShootGrid[] grid_flag; + //@readonly CGuild[] guild; + @optional @readonly CShootGridMask[] mask; + @optional @readonly CScale[] scale; + @optional @readonly CColliderScale[] collider_scale; + } + + ShootGrid* grid; + + void onCreate() + { + //grid = space_invaders.shoot_grid; + grid = Mallocator.make!ShootGrid; + grid.create(ivec2(80,60), vec2(5,5)); + } + + void onDestroy() + { + Mallocator.dispose(grid); + } + + // bool onBegin() + // { + // //if(!grid)return false; + // //grid.clear(); + // return true; + // } + + void onUpdate(EntitiesData data) + { + vec2[] scale; + if(data.collider_scale)scale = cast(vec2[])data.collider_scale; + else if(data.scale)scale = cast(vec2[])data.scale; + else return; + if(data.mask is null) + { + foreach(i; 0..data.length) + { + vec2 half_scale = scale[i] * 0.5; + grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, ubyte.max);//cast(ubyte)(1 << data.guild[i].guild)); + } + } + else foreach(i; 0..data.length) + { + vec2 half_scale = scale[i] * 0.5; + grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, data.mask[i]);//cast(ubyte)(1 << data.guild[i].guild)); + } + + } +} \ No newline at end of file diff --git a/demos/source/gui/manager.d b/demos/source/gui/manager.d index 3745d9d..b3381ef 100644 --- a/demos/source/gui/manager.d +++ b/demos/source/gui/manager.d @@ -213,7 +213,7 @@ struct GUIManager static if(hasUDA!(member,GUIRange)) { comp_edit.variables[comp_edit.used-1].int_.min = getUDAs!(member,GUIRange)[0].min; - comp_edit.variables[comp_edit.used-1].int_.max = getUDAs!(member,GUIRange)[1].max; + comp_edit.variables[comp_edit.used-1].int_.max = getUDAs!(member,GUIRange)[0].max; } /*{ comp_edit.variables[comp_edit.used-1].int_.min = int.min; diff --git a/demos/source/gui/system.d b/demos/source/gui/system.d index 112bed3..b4899fb 100644 --- a/demos/source/gui/system.d +++ b/demos/source/gui/system.d @@ -5,7 +5,8 @@ import bubel.ecs.system; struct SystemGUI { const (char)* name; - System* system; + //System* system; + ushort id; bool enabled = true; } \ No newline at end of file diff --git a/demos/source/gui/tool_circle.d b/demos/source/gui/tool_circle.d index c43ade3..c8968d4 100644 --- a/demos/source/gui/tool_circle.d +++ b/demos/source/gui/tool_circle.d @@ -26,6 +26,7 @@ struct ToolCircle void draw(Renderer* renderer, vec2 position, float size, float edge = 1) { + glDisable(GL_DEPTH_TEST); position = position * renderer.view_size + renderer.view_pos; vec2 sizes = renderer.view_size * size; vec2 sizes2 = vec2(edge,0); diff --git a/demos/utils/source/ecs_utils/math/vector.d b/demos/utils/source/ecs_utils/math/vector.d index adb3a36..c835d05 100644 --- a/demos/utils/source/ecs_utils/math/vector.d +++ b/demos/utils/source/ecs_utils/math/vector.d @@ -95,6 +95,11 @@ struct vec2 } } +float dot(vec2 a, vec2 b) +{ + return a.x*b.x + a.y*b.y; +} + struct vec4 { union