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; //position component struct CLocation { //adds some extra functionality. Not required. Will be probably removed from library in the future. mixin ECS.Component; alias value this;//use component as it value //default values work properly vec2 value = vec2(0); } //scale component struct CScale { mixin ECS.Component; alias value this;//use component as it value vec2 value = vec2(16,16); } //rotation component struct CRotation { mixin ECS.Component; alias value this;//use component as it value float value = 0; } //depth component. Entity with higher depth will be rendered on top struct CDepth { mixin ECS.Component; alias value this; short value; } //color component struct CColor { mixin ECS.Component; alias value this; @GUIColor uint value; } //component used for selection struct CSelected { mixin ECS.Component; bool value = false; } //component indicating that entity should receive input from mouse, keyboard, etc. struct CInput { mixin ECS.Component; } //component with damping value struct CDamping { mixin ECS.Component; alias value this; @GUIRange(0,9) byte value = 1; } //velocity component struct CVelocity { mixin ECS.Component; alias value this; vec2 value = vec2(0,0); } //factor which is used for velocity masking struct CVelocityFactor { mixin ECS.Component; alias value this; vec2 value = vec2(1); } //flag indicating that entity is static and shouldn't be updated by most systems in every frame struct CStatic { mixin ECS.Component; } //system which slowing down entities struct DampingSystem { //system will generate up to 32 jobs mixin ECS.System!32; struct EntitiesData { uint length; const (Entity)[] entity; //entity is readonly @readonly CDamping[] damping;//damping is readonly. Marking with @readonly will help multithreading algorithm CVelocity[] velocity;//velocity is wirtable as it will be modified for entities in this system } //20 predefined damping speeds. Gives possibility to store damping as single byte. float[20] damp = 0; bool onBegin() { //calculate damping values foreach(i;0..20) { damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.deltaTime*0.1); } return true; } void onUpdate(EntitiesData data) { foreach(i; 0..data.length) { //constantly slow down entity data.velocity[i] = data.velocity[i] * damp[data.damping[i]]; } } } //system used for entity movement struct MoveSystem { mixin ECS.System!64; struct EntitiesData { uint length; CLocation[] location; @readonly CVelocity[] velocity; @optional @readonly CVelocityFactor[] vel_factor;//CVeclocityFactor is not required so entites without this component will be also updated } void onUpdate(EntitiesData data) { //split into two loops for two types of entities. Doing it in "normal" way by testing data.vel_factor inside loop in every iteration will be probably compiled as same machine code in release build (it works in LDC) if(data.vel_factor) { foreach(i; 0..data.length) { data.location[i] += data.velocity[i] * data.vel_factor[i] * launcher.deltaTime; } } else { foreach(i; 0..data.length) { data.location[i] += data.velocity[i] * launcher.deltaTime; } } } } /** *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) { foreach(i; 0..data.length) { data.velocity[i] += move_vector * launcher.deltaTime * 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; } } }