From 0b924ae77c8084d39b360f90b688a8461a04d043 Mon Sep 17 00:00:00 2001 From: Mergul Date: Tue, 19 May 2020 20:27:18 +0200 Subject: [PATCH] Demos update (SpaceInvaders demo) -added new sprites -added new functions to vectors -fixed rendering rotated sprites -small performance improvement and some bug fixes -added animations support -bullets get initial velocity from parent -added partiles for fire from gun and explosion of enemies --- demos/assets/textures/atlas.png | Bin 24668 -> 35204 bytes demos/dub.json | 3 + demos/source/app.d | 4 +- demos/source/demos/snake.d | 2 +- demos/source/demos/space_invaders.d | 362 ++++++++++++++++++-- demos/utils/source/ecs_utils/gfx/renderer.d | 18 + demos/utils/source/ecs_utils/math/vector.d | 56 +++ 7 files changed, 411 insertions(+), 34 deletions(-) diff --git a/demos/assets/textures/atlas.png b/demos/assets/textures/atlas.png index c628969c0e7149419eb62313fb5c3274457838f3..1e269e513e42c5dc797f568af05816d2562995f0 100644 GIT binary patch literal 35204 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
-U`yoWDlL!y69I7g$; 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 diff --git a/demos/dub.json b/demos/dub.json index 43d248c..1c1fe65 100644 --- a/demos/dub.json +++ b/demos/dub.json @@ -18,6 +18,9 @@ "libs-windows-x86_64": ["libs/windows/x64/SDL2","libs/windows/x64/SDL2_Image","libs/windows/x64/cimgui"], "libs-linux-x86_64": ["cimgui","SDL2","SDL2_image"], "lflags-linux-x86_64": ["-rpath=libs/linux/x64/","-Llibs/linux/x64/"], + "dflags-ldc" : [ + "--ffast-math" + ], "configurations" : [ { "name" : "default", diff --git a/demos/source/app.d b/demos/source/app.d index 10fc80e..3af24b5 100644 --- a/demos/source/app.d +++ b/demos/source/app.d @@ -262,7 +262,7 @@ void mainLoop(void* arg) if(launcher.tool && launcher.tool_repeat != 0 && launcher.mouse.left && !igIsWindowHovered(ImGuiHoveredFlags_AnyWindow) && !igIsWindowFocused(ImGuiFocusedFlags_AnyWindow)) { float range = 500.0 / cast(float)launcher.tool_repeat; - launcher.repeat_time += launcher.delta_time; + launcher.repeat_time += launcher.delta_time * 100; while(launcher.repeat_time > range) { launcher.repeat_time -= range; @@ -716,7 +716,7 @@ int main(int argc, char** argv) launcher.job_updater = Mallocator.make!ECSJobUpdater(1); //launcher.job_updater.onCreate(); - EntityManager.initialize(32); + EntityManager.initialize(32, 1<<16); launcher.manager = EntityManager.instance; //launcher.manager.m_thread_id_func = &launcher.job_updater.getThreadID; diff --git a/demos/source/demos/snake.d b/demos/source/demos/snake.d index 3c62ef6..50f7729 100644 --- a/demos/source/demos/snake.d +++ b/demos/source/demos/snake.d @@ -285,7 +285,7 @@ struct ParticleSystem { uint length; @readonly Entity[] entities; - @readonly CParticle[] particle; + CParticle[] particle; } void onUpdate(EntitiesData data) diff --git a/demos/source/demos/space_invaders.d b/demos/source/demos/space_invaders.d index bef65fe..56d891d 100644 --- a/demos/source/demos/space_invaders.d +++ b/demos/source/demos/space_invaders.d @@ -16,6 +16,8 @@ import ecs_utils.gfx.texture; import ecs_utils.math.vector; import ecs_utils.utils; +import std.math : PI; + //import std.array : staticArray; enum float px = 1.0/512.0; @@ -121,6 +123,16 @@ struct CScale vec2 value = vec2(16,16); } +struct CRotation +{ + mixin ECS.Component; + + ///use component as it value + alias value this; + + float value = 0; +} + struct CTexture { mixin ECS.Component; @@ -176,6 +188,17 @@ struct CLaserWeapon { mixin ECS.Component; + static struct Level + { + float reload_time; + float dispersion; + } + + __gshared Level[12] levels = [Level(4000,0),Level(4000,0.1), + Level(500,0),Level(350,0),Level(250,0.02),Level(175,0.03),Level(110,0.04), + Level(80,0.05),Level(50,0.08),Level(20,0.1),Level(10,0.12),Level(2,0.14)]; + + ubyte level = 1; float shoot_time = 0; } @@ -208,13 +231,30 @@ struct CShootGrid mixin ECS.Component; } +struct CTargetPartent +{ + mixin ECS.Component; + + EntityID parent; + vec2 rel_pos; +} + struct CHitPoints { mixin ECS.Component; alias value this; - int value = 10; + int value = 3; +} + +struct CMaxHitPoints +{ + mixin ECS.Component; + + alias value this; + + int value = 3; } struct CHitMark @@ -229,6 +269,47 @@ struct CHitMark struct CUpgrade { mixin ECS.Component; + + alias value this; + + enum Upgrade : ubyte + { + hit_points, + regeneration, + laser + } + + Upgrade value; +} + +struct CAnimation +{ + mixin ECS.Component; + + vec4[] frames; + float time = 0; + float speed = 1; +} + +struct CAnimationLooped +{ + mixin ECS.Component; +} + +struct CDamping +{ + mixin ECS.Component; + + alias value this; + + byte value = 0; +} + +struct CParticle +{ + mixin ECS.Component; + + float life = 0; } /*####################################################################################################################### @@ -436,6 +517,7 @@ struct DrawSystem @readonly CTexture[] textures; @readonly CLocation[] locations; @readonly CScale[] scale; + @readonly @optional CRotation[] rotation; @readonly @optional CDepth[] depth; @readonly @optional CHitMark[] hit_mark; } @@ -452,6 +534,13 @@ struct DrawSystem launcher.renderer.draw(data.textures[i].tex, data.locations[i].value, data.scale[i], data.textures[i].coords, cast(short)data.locations[i].y, color, 0, 0, 0, data.thread_id); } } + else if(data.rotation) + { + foreach(i; 0..data.length) + { + launcher.renderer.draw(data.textures[i].tex, data.locations[i].value, data.scale[i], data.textures[i].coords, cast(short)data.locations[i].y, 0x80808080, data.rotation[i], 0, 0, data.thread_id); + } + } else { foreach(i; 0..data.length) @@ -463,18 +552,36 @@ struct DrawSystem else { if(data.hit_mark) + { + if(data.rotation) + { + foreach(i; 0..data.length) + { + uint color = 0x80808080 + 0x01010101 * data.hit_mark[i]; + launcher.renderer.draw(data.textures[i].tex, data.locations[i].value, data.scale[i], data.textures[i].coords, cast(short)(data.depth[i] * 4 + data.locations[i].y), color, data.rotation[i], 0, 0, data.thread_id); + } + } + else + { + foreach(i; 0..data.length) + { + uint color = 0x80808080 + 0x01010101 * data.hit_mark[i]; + launcher.renderer.draw(data.textures[i].tex, data.locations[i].value, data.scale[i], data.textures[i].coords, cast(short)(data.depth[i] * 4 + data.locations[i].y), color, 0, 0, 0, data.thread_id); + } + } + } + else if(data.rotation) { foreach(i; 0..data.length) { - uint color = 0x80808080 + 0x01010101 * data.hit_mark[i]; - launcher.renderer.draw(data.textures[i].tex, data.locations[i].value, data.scale[i], data.textures[i].coords, cast(short)(data.depth[i] * 64 + data.locations[i].y), color, 0, 0, 0, data.thread_id); + launcher.renderer.draw(data.textures[i].tex, data.locations[i].value, data.scale[i], data.textures[i].coords, cast(short)(data.depth[i] * 4 + data.locations[i].y), 0x80808080, data.rotation[i], 0, 0, data.thread_id); } } else { foreach(i; 0..data.length) { - launcher.renderer.draw(data.textures[i].tex, data.locations[i].value, data.scale[i], data.textures[i].coords, cast(short)(data.depth[i] * 64 + data.locations[i].y), 0x80808080, 0, 0, 0, data.thread_id); + launcher.renderer.draw(data.textures[i].tex, data.locations[i].value, data.scale[i], data.textures[i].coords, cast(short)(data.depth[i] * 4 + data.locations[i].y), 0x80808080, 0, 0, 0, data.thread_id); } } } @@ -502,12 +609,19 @@ struct LaserShootingSystem mixin ECS.System!32; bool shoot = false; - static float[18] laser_shoot_times = [500,400,300,200,100,50,25,10,5,2,1,0.8,0.6,0.5,0.4,0.3,0.2,0.1]; + //static float[18] laser_shoot_times = [500,400,300,200,100,50,25,10,5,2,1,0.8,0.6,0.5,0.4,0.3,0.2,0.1]; + //static float[18] laser_shoot_disp = [0,0,0,0,0.05,0.06,0.08,0.1,0.14,0.18,0.2,0.25,0.26,0.27,0.28,0.29,0.3,0.4]; + + __gshared vec4[] fire_frames = [vec4(96,64,8,16)*px,vec4(104,64,8,16)*px,vec4(112,64,8,16)*px,vec4(120,64,8,16)*px,vec4(128,64,8,16)*px, + vec4(136,64,8,16)*px,vec4(144,64,8,16)*px,vec4(152,64,8,16)*px,vec4(160,64,8,16)*px]; + + // __gshared vec4[] fire_frames = [vec4(0,160,8,16)*px,vec4(16,160,16,16)*px,vec4(32,160,16,16)*px,vec4(48,160,16,16)*px,vec4(64,160,16,16)*px, + // vec4(80,160,16,16)*px,vec4(96,160,16,16)*px,vec4(112,160,16,16)*px]; /*CLocation* laser_location; CVelocity* laser_velocity; CGuild* laser_guild;*/ - + struct EntitiesData { ///variable named "length" contain entites count @@ -518,6 +632,7 @@ struct LaserShootingSystem @readonly @optional CAutoShoot[] auto_shoot; @readonly CLocation[] location; @readonly CGuild[] guild; + @optional @readonly CVelocity[] velocity; CLaserWeapon[] laser; } @@ -527,6 +642,11 @@ struct LaserShootingSystem CLocation* laser_location; CVelocity* laser_velocity; CGuild* laser_guild; + + EntityTemplate* fire_tmpl; + CLocation* fire_location; + CVelocity* fire_velocity; + CRotation* fire_rotation; } ThreadData[] threads; @@ -535,7 +655,10 @@ struct LaserShootingSystem void onCreate() { threads = Mallocator.makeArray!ThreadData(32); - threads[0].laser_tmpl = launcher.manager.allocateTemplate([CLocation.component_id, CTexture.component_id, CVelocity.component_id, CScale.component_id, CLaser.component_id, CGuild.component_id].staticArray); + threads[0].laser_tmpl = launcher.manager.allocateTemplate( + [CLocation.component_id, CTexture.component_id, CVelocity.component_id, + CScale.component_id, CLaser.component_id, CGuild.component_id].staticArray + ); CTexture* tex_comp = threads[0].laser_tmpl.getComponent!CTexture; tex_comp.tex = space_invaders.texture;//laser_tex; @@ -546,12 +669,33 @@ struct LaserShootingSystem threads[0].laser_velocity = threads[0].laser_tmpl.getComponent!CVelocity; threads[0].laser_guild = threads[0].laser_tmpl.getComponent!CGuild; + threads[0].fire_tmpl = launcher.manager.allocateTemplate( + [CLocation.component_id, CTexture.component_id, CScale.component_id, + CAnimation.component_id, CParticle.component_id, CRotation.component_id, + CVelocity.component_id, CDamping.component_id].staticArray + ); + + tex_comp = threads[0].fire_tmpl.getComponent!CTexture; + tex_comp.tex = space_invaders.texture;//laser_tex; + tex_comp.coords = vec4(96*px,64*px,8*px,16*px); + scale_comp = threads[0].fire_tmpl.getComponent!CScale; + scale_comp.value = vec2(8,16); + threads[0].fire_location = threads[0].fire_tmpl.getComponent!CLocation; + threads[0].fire_rotation = threads[0].fire_tmpl.getComponent!CRotation; + threads[0].fire_velocity = threads[0].fire_tmpl.getComponent!CVelocity; + threads[0].fire_tmpl.getComponent!(CParticle).life = 300; + *threads[0].fire_tmpl.getComponent!(CAnimation) = CAnimation(fire_frames, 0, 3); + foreach(ref ThreadData thread;threads[1..$]) { thread.laser_tmpl = launcher.manager.allocateTemplate(threads[0].laser_tmpl); thread.laser_location = thread.laser_tmpl.getComponent!CLocation; thread.laser_velocity = thread.laser_tmpl.getComponent!CVelocity; thread.laser_guild = thread.laser_tmpl.getComponent!CGuild; + thread.fire_tmpl = launcher.manager.allocateTemplate(threads[0].fire_tmpl); + thread.fire_location = thread.fire_tmpl.getComponent!CLocation; + thread.fire_rotation = thread.fire_tmpl.getComponent!CRotation; + thread.fire_velocity = thread.fire_tmpl.getComponent!CVelocity; } //laser_location = space_invaders.laser_tmpl.getComponent!CLocation; } @@ -588,13 +732,34 @@ struct LaserShootingSystem { CLaserWeapon* laser = &data.laser[i]; laser.shoot_time += launcher.delta_time; - while(laser.shoot_time > laser_shoot_times[laser.level - 1]) + while(laser.shoot_time > CLaserWeapon.levels[laser.level - 1].reload_time) { - laser.shoot_time -= laser_shoot_times[laser.level - 1]; + laser.shoot_time -= CLaserWeapon.levels[laser.level - 1].reload_time; thread.laser_location.value = data.location[i]; - thread.laser_velocity.value = vec2(randomf()*0.5-0.25,data.shoot_direction[i].direction == Direction.up ? 1.0 : -1.0); + thread.laser_velocity.value = vec2((randomf()*2-1) * CLaserWeapon.levels[laser.level - 1].dispersion,data.shoot_direction[i].direction == Direction.up ? 1.0 : -1.0); thread.laser_guild.guild = data.guild[i].guild; + + if(data.velocity) + { + thread.fire_velocity.value = data.velocity[i]; + //thread.laser_velocity.value += data.velocity[i] * 0.5; + } + else thread.fire_velocity.value = vec2(0,0); + launcher.manager.addEntity(thread.laser_tmpl); + + thread.fire_location.value = data.location[i]; + if(data.shoot_direction[i].direction == Direction.down) + { + thread.fire_rotation.value = PI; + thread.fire_location.value.y -= 16; + } + else + { + thread.fire_rotation.value = 0; + thread.fire_location.value.y += 24; + } + launcher.manager.addEntity(thread.fire_tmpl); } } } @@ -604,13 +769,47 @@ struct LaserShootingSystem { CLaserWeapon* laser = &data.laser[i]; laser.shoot_time += launcher.delta_time; - if(laser.shoot_time > laser_shoot_times[laser.level - 1])laser.shoot_time = laser_shoot_times[laser.level - 1]; + if(laser.shoot_time > CLaserWeapon.levels[laser.level - 1].reload_time)laser.shoot_time = CLaserWeapon.levels[laser.level - 1].reload_time; } } } } +struct DampingSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + const (Entity)[] entity; + @readonly CDamping[] damping; + CVelocity[] velocity; + } + + float[10] damp = 0; + + bool onBegin() + { + import core.stdc.math : powf; + 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 LaserCollisionSystem { mixin ECS.System!32; @@ -689,7 +888,7 @@ struct UpgradeSystem CLaserWeapon* laser = entity.getComponent!CLaserWeapon; if(laser) { - if(laser.level < LaserShootingSystem.laser_shoot_times.length)laser.level++; + if(laser.level < CLaserWeapon.levels.length)laser.level++; } } } @@ -884,9 +1083,15 @@ struct HitPointsSystem { mixin ECS.System; + __gshared vec4[] upgrade_laser_frames = [vec4(96,80,16,16)*px,vec4(112,80,16,16)*px,vec4(128,80,16,16)*px,vec4(144,80,16,16)*px,vec4(128,80,16,16)*px,vec4(112,80,16,16)*px]; + __gshared vec4[] explosion_laser_frames = [vec4(80,128,16,16)*px,vec4(96,128,16,16)*px,vec4(112,128,16,16)*px,vec4(128,128,16,16)*px,vec4(144,128,16,16)*px,vec4(160,128,16,16)*px,vec4(176,128,16,16)*px,vec4(192,128,16,16)*px,vec4(208,128,16,16)*px]; + EntityTemplate* upgrade_tmpl; CLocation* upgrade_location; + EntityTemplate* explosion_tmpl; + CLocation* explosion_location; + struct EntitiesData { CHitPoints[] hp; @@ -894,13 +1099,21 @@ struct HitPointsSystem void onCreate() { - upgrade_tmpl = launcher.manager.allocateTemplate([CVelocity.component_id, CLocation.component_id, CTexture.component_id, CScale.component_id, CUpgrade.component_id].staticArray); + upgrade_tmpl = launcher.manager.allocateTemplate([CVelocity.component_id, CLocation.component_id, CTexture.component_id, CScale.component_id, CUpgrade.component_id, CAnimation.component_id, CAnimationLooped.component_id].staticArray); CTexture* tex_comp = upgrade_tmpl.getComponent!CTexture; tex_comp.tex = space_invaders.texture;//ship_tex; tex_comp.coords = vec4(0*px,32*px,16*px,16*px); + *upgrade_tmpl.getComponent!CAnimation = CAnimation(upgrade_laser_frames, 0, 1); CVelocity* vel_comp = upgrade_tmpl.getComponent!CVelocity; vel_comp.value = vec2(0,-0.1); upgrade_location = upgrade_tmpl.getComponent!CLocation; + + explosion_tmpl = launcher.manager.allocateTemplate([CDepth.component_id, CParticle.component_id, CLocation.component_id, CTexture.component_id, CScale.component_id, CAnimation.component_id].staticArray); + explosion_tmpl.getComponent!(CTexture).tex = space_invaders.texture; + *explosion_tmpl.getComponent!CAnimation = CAnimation(explosion_laser_frames, 0, 1.333); + explosion_tmpl.getComponent!(CParticle).life = 600; + *explosion_tmpl.getComponent!CDepth = -1; + explosion_location = explosion_tmpl.getComponent!CLocation; } void onDestroy() @@ -911,9 +1124,9 @@ struct HitPointsSystem void handleEvent(Entity* entity, EDamage event) { CHitPoints* hp = entity.getComponent!CHitPoints; - if(*hp < 0)return; + if(*hp <= 0)return; *hp -= event.damage; - if(*hp < 0) + if(*hp <= 0) { launcher.manager.sendEvent(entity.id, EDeath()); //launcher.manager.removeEntity(entity.id); @@ -927,14 +1140,16 @@ struct HitPointsSystem CEnemy* enemy = entity.getComponent!CEnemy; if(enemy) { - if(randomRange(0, 1000) < 5) + CLocation* location = entity.getComponent!CLocation; + if(location) { - CLocation* location = entity.getComponent!CLocation; - if(location) + if(randomRange(0, 1000) < 5) { *upgrade_location = *location; launcher.manager.addEntity(upgrade_tmpl); } + *explosion_location = *location; + launcher.manager.addEntity(explosion_tmpl); } } launcher.manager.removeEntity(entity.id); @@ -1031,7 +1246,7 @@ struct MovementSystem //@optional const (CLaser)[] laser; const (Entity)[] entities; - @optional CSideMove[] side_move; + //@optional CSideMove[] side_move; } void onUpdate(EntitiesData data) @@ -1044,6 +1259,63 @@ struct MovementSystem } } +struct AnimationSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + CAnimation[] animation; + CTexture[] texture; + @optional @readonly CAnimationLooped[] looped; + } + + void onUpdate(EntitiesData data) + { + float dt = launcher.delta_time * 0.01; + if(data.looped) + { + foreach(i;0..data.length) + { + data.animation[i].time += dt * data.animation[i].speed; + while(data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time -= cast(float)data.animation[i].frames.length; + data.texture[i].coords = data.animation[i].frames[cast(int)(data.animation[i].time)]; + } + } + else + { + foreach(i;0..data.length) + { + data.animation[i].time += dt * data.animation[i].speed; + if(data.animation[i].time >= data.animation[i].frames.length)data.animation[i].time = data.animation[i].frames.length - 0.1; + data.texture[i].coords = data.animation[i].frames[cast(int)(data.animation[i].time)]; + } + } + + } +} + +struct ParticleSystem +{ + mixin ECS.System!32; + + struct EntitiesData + { + uint length; + @readonly Entity[] entitiy; + CParticle[] particle; + } + + void onUpdate(EntitiesData data) + { + foreach(i;0..data.length) + { + data.particle[i].life -= launcher.delta_time; + if(data.particle[i].life < 0)launcher.manager.removeEntity(data.entitiy[i].id); + } + } +} extern(C) float sqrtf(float x) @nogc nothrow @system; @@ -1063,7 +1335,8 @@ struct InputMovementSystem //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; + //CLocation[] locations; + CVelocity[] velocity; CTexture[] textures; } @@ -1108,22 +1381,33 @@ struct InputMovementSystem */ void onUpdate(EntitiesData data) { - if(move_vector.x == 0) + /*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.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); + 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); + }*/ } } @@ -1165,6 +1449,12 @@ void spaceInvadersStart() launcher.manager.registerComponent!CHitPoints; launcher.manager.registerComponent!CHitMark; launcher.manager.registerComponent!CUpgrade; + launcher.manager.registerComponent!CParticle; + launcher.manager.registerComponent!CMaxHitPoints; + launcher.manager.registerComponent!CAnimation; + launcher.manager.registerComponent!CRotation; + launcher.manager.registerComponent!CAnimationLooped; + launcher.manager.registerComponent!CDamping; launcher.manager.registerEvent!EChangeDirection; launcher.manager.registerEvent!EDamage; @@ -1185,6 +1475,9 @@ void spaceInvadersStart() launcher.manager.registerSystem!HitMarkingSystem(-100); launcher.manager.registerSystem!UpgradeCollisionSystem(-70); launcher.manager.registerSystem!UpgradeSystem(-100); + launcher.manager.registerSystem!ParticleSystem(-100); + launcher.manager.registerSystem!AnimationSystem(-100); + launcher.manager.registerSystem!DampingSystem(-101); launcher.manager.endRegister(); @@ -1197,8 +1490,13 @@ void spaceInvadersStart() //launcher.manager.getSystem(CleanSystem.system_id).disable(); { - ushort[11] components = [CHitMark.component_id, CHitPoints.component_id, CLocation.component_id, CTexture.component_id, CInput.component_id, CShip.component_id, CScale.component_id, CLaserWeapon.component_id, CShootDirection.component_id, CShootGrid.component_id, CGuild.component_id]; - space_invaders.ship_tmpl = launcher.manager.allocateTemplate(components); + space_invaders.ship_tmpl = launcher.manager.allocateTemplate( + [CVelocity.component_id, CHitMark.component_id, CHitPoints.component_id, + CLocation.component_id, CTexture.component_id, CInput.component_id, + CShip.component_id, CScale.component_id, CLaserWeapon.component_id, + CShootDirection.component_id, CShootGrid.component_id, CGuild.component_id, + CDamping.component_id].staticArray + ); CScale* scale_comp = space_invaders.ship_tmpl.getComponent!CScale; scale_comp.value = vec2(48,32); @@ -1208,8 +1506,9 @@ void spaceInvadersStart() CLocation* loc_comp = space_invaders.ship_tmpl.getComponent!CLocation; loc_comp.value = vec2(64,64); CLaserWeapon* weapon = space_invaders.ship_tmpl.getComponent!CLaserWeapon; - weapon.level = 1; + weapon.level = 3; space_invaders.ship_tmpl.getComponent!CHitPoints().value = 1000; + space_invaders.ship_tmpl.getComponent!CDamping().value = 7; launcher.manager.addEntity(space_invaders.ship_tmpl); } @@ -1273,12 +1572,13 @@ void spaceInvadersStart() EntityTemplate* upgrade_tmpl; { - upgrade_tmpl = launcher.manager.allocateTemplate([CVelocity.component_id, CLocation.component_id, CTexture.component_id, CScale.component_id, CUpgrade.component_id].staticArray); + upgrade_tmpl = launcher.manager.allocateTemplate([CVelocity.component_id, CLocation.component_id, CTexture.component_id, CScale.component_id, CUpgrade.component_id, CAnimationLooped.component_id, CAnimation.component_id].staticArray); CTexture* tex_comp = upgrade_tmpl.getComponent!CTexture; tex_comp.tex = space_invaders.texture;//ship_tex; tex_comp.coords = vec4(0*px,32*px,16*px,16*px); CVelocity* vel_comp = upgrade_tmpl.getComponent!CVelocity; vel_comp.value = vec2(0,-0.1); + *upgrade_tmpl.getComponent!CAnimation = CAnimation(HitPointsSystem.upgrade_laser_frames, 0, 0.75); } launcher.manager.commit(); @@ -1326,7 +1626,7 @@ void spaceInvadersTool(vec2 position, Tool tool, int size) CLaserWeapon* laser_weapon = tmpl.getComponent!CLaserWeapon; if(laser_weapon) { - laser_weapon.shoot_time = randomf * LaserShootingSystem.laser_shoot_times[laser_weapon.level - 1]; + laser_weapon.shoot_time = randomf * CLaserWeapon.levels[laser_weapon.level - 1].reload_time; } launcher.manager.addEntity(tmpl); } diff --git a/demos/utils/source/ecs_utils/gfx/renderer.d b/demos/utils/source/ecs_utils/gfx/renderer.d index 206364f..95f8aa6 100644 --- a/demos/utils/source/ecs_utils/gfx/renderer.d +++ b/demos/utils/source/ecs_utils/gfx/renderer.d @@ -535,12 +535,30 @@ struct Renderer verts[item_id*28] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[0] * coss + GfxConfig.meshes[mesh_id].vertices[1] * sinn) * size.x + pos.x) * 8191); verts[item_id*28+1] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[1] * coss - GfxConfig.meshes[mesh_id].vertices[0] * sinn) * size.y + pos.y) * 8191); + verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767); + verts[item_id*28+3] = cast(short)((GfxConfig.meshes[mesh_id].vertices[3] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+4,mem.ptr,6); + + verts[item_id*28+7] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[4] * coss + GfxConfig.meshes[mesh_id].vertices[5] * sinn) * size.x + pos.x) * 8191); verts[item_id*28+8] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[5] * coss - GfxConfig.meshes[mesh_id].vertices[4] * sinn) * size.y + pos.y) * 8191); + verts[item_id*28+9] = cast(short)((GfxConfig.meshes[mesh_id].vertices[6] * coords.z + coords.x)*32767); + verts[item_id*28+10] = cast(short)((GfxConfig.meshes[mesh_id].vertices[7] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+11,mem.ptr,6); + + verts[item_id*28+14] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[8] * coss + GfxConfig.meshes[mesh_id].vertices[9] * sinn) * size.x + pos.x) * 8191); verts[item_id*28+15] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[9] * coss - GfxConfig.meshes[mesh_id].vertices[8] * sinn) * size.y + pos.y) * 8191); + verts[item_id*28+16] = cast(short)((GfxConfig.meshes[mesh_id].vertices[10] * coords.z + coords.x)*32767); + verts[item_id*28+17] = cast(short)((GfxConfig.meshes[mesh_id].vertices[11] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+18,mem.ptr,6); + + verts[item_id*28+21] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[12] * coss + GfxConfig.meshes[mesh_id].vertices[13] * sinn) * size.x + pos.x) * 8191); verts[item_id*28+22] = cast(short)(((GfxConfig.meshes[mesh_id].vertices[13] * coss - GfxConfig.meshes[mesh_id].vertices[12] * sinn) * size.y + pos.y) * 8191); + verts[item_id*28+23] = cast(short)((GfxConfig.meshes[mesh_id].vertices[14] * coords.z + coords.x)*32767); + verts[item_id*28+24] = cast(short)((GfxConfig.meshes[mesh_id].vertices[15] * coords.w + coords.y)*32767); + memcpy(verts.ptr+item_id*28+25,mem.ptr,6); } /*verts[item_id*28+2] = cast(short)((GfxConfig.meshes[mesh_id].vertices[2] * coords.z + coords.x)*32767); diff --git a/demos/utils/source/ecs_utils/math/vector.d b/demos/utils/source/ecs_utils/math/vector.d index a74b6db..a8f14f8 100644 --- a/demos/utils/source/ecs_utils/math/vector.d +++ b/demos/utils/source/ecs_utils/math/vector.d @@ -2,6 +2,18 @@ module ecs_utils.math.vector; struct vec2 { + this(float v) @nogc nothrow + { + x = v; + y = v; + } + + this(float x, float y) @nogc nothrow + { + this.x = x; + this.y = y; + } + union { struct @@ -75,6 +87,22 @@ struct vec4 float[4] data; } + this(float v) @nogc nothrow + { + x = v; + y = v; + z = v; + w = v; + } + + this(float x, float y, float z, float w) @nogc nothrow + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + vec4 opBinary(string op)(float v) { static if (op == "+") return vec4(x + v, y + v, z + v, w + v); @@ -96,6 +124,18 @@ struct ivec2 } int[2] data; } + + this(int v) @nogc nothrow + { + x = v; + y = v; + } + + this(int x, int y) @nogc nothrow + { + this.x = x; + this.y = y; + } ivec2 opBinary(string op, T)(T v) { @@ -125,4 +165,20 @@ struct ivec4 } int[4] data; } + + this(int v) @nogc nothrow + { + x = v; + y = v; + z = v; + w = v; + } + + this(int x, int y, int z, int w) @nogc nothrow + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } } \ No newline at end of file