1 module game_core.basic; 2 3 import bubel.ecs.core; 4 import bubel.ecs.attributes; 5 6 import ecs_utils.math.vector; 7 8 import gui.attributes; 9 10 import ecs_utils.utils; 11 12 import app : launcher; 13 14 import bindbc.sdl; 15 16 //position component 17 struct CLocation 18 { 19 //adds some extra functionality. Not required. Will be probably removed from library in the future. 20 mixin ECS.Component; 21 22 alias value this;//use component as it value 23 24 //default values work properly 25 vec2 value = vec2(0); 26 } 27 28 //scale component 29 struct CScale 30 { 31 mixin ECS.Component; 32 33 alias value this;//use component as it value 34 35 vec2 value = vec2(16,16); 36 } 37 38 //rotation component 39 struct CRotation 40 { 41 mixin ECS.Component; 42 43 alias value this;//use component as it value 44 45 float value = 0; 46 } 47 48 //depth component. Entity with higher depth will be rendered on top 49 struct CDepth 50 { 51 mixin ECS.Component; 52 53 alias value this; 54 55 short value; 56 } 57 58 //color component 59 struct CColor 60 { 61 mixin ECS.Component; 62 63 alias value this; 64 65 @GUIColor uint value; 66 } 67 68 //component used for selection 69 struct CSelected 70 { 71 mixin ECS.Component; 72 73 bool value = false; 74 } 75 76 //component indicating that entity should receive input from mouse, keyboard, etc. 77 struct CInput 78 { 79 mixin ECS.Component; 80 } 81 82 //component with damping value 83 struct CDamping 84 { 85 mixin ECS.Component; 86 87 alias value this; 88 89 @GUIRange(0,9) byte value = 1; 90 } 91 92 //velocity component 93 struct CVelocity 94 { 95 mixin ECS.Component; 96 97 alias value this; 98 99 vec2 value = vec2(0,0); 100 } 101 102 //factor which is used for velocity masking 103 struct CVelocityFactor 104 { 105 mixin ECS.Component; 106 107 alias value this; 108 109 vec2 value = vec2(1); 110 } 111 112 //flag indicating that entity is static and shouldn't be updated by most systems in every frame 113 struct CStatic 114 { 115 mixin ECS.Component; 116 } 117 118 //system which slowing down entities 119 struct DampingSystem 120 { 121 //system will generate up to 32 jobs 122 mixin ECS.System!32; 123 124 struct EntitiesData 125 { 126 uint length; 127 const (Entity)[] entity; //entity is readonly 128 @readonly CDamping[] damping;//damping is readonly. Marking with @readonly will help multithreading algorithm 129 CVelocity[] velocity;//velocity is wirtable as it will be modified for entities in this system 130 } 131 132 //20 predefined damping speeds. Gives possibility to store damping as single byte. 133 float[20] damp = 0; 134 135 bool onBegin() 136 { 137 //calculate damping values 138 foreach(i;0..20) 139 { 140 damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.deltaTime*0.1); 141 } 142 143 return true; 144 } 145 146 void onUpdate(EntitiesData data) 147 { 148 foreach(i; 0..data.length) 149 { 150 //constantly slow down entity 151 data.velocity[i] = data.velocity[i] * damp[data.damping[i]]; 152 } 153 } 154 } 155 156 //system used for entity movement 157 struct MoveSystem 158 { 159 mixin ECS.System!64; 160 161 struct EntitiesData 162 { 163 uint length; 164 CLocation[] location; 165 @readonly CVelocity[] velocity; 166 @optional @readonly CVelocityFactor[] vel_factor;//CVeclocityFactor is not required so entites without this component will be also updated 167 } 168 169 void onUpdate(EntitiesData data) 170 { 171 //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) 172 if(data.vel_factor) 173 { 174 foreach(i; 0..data.length) 175 { 176 data.location[i] += data.velocity[i] * data.vel_factor[i] * launcher.deltaTime; 177 } 178 } 179 else 180 { 181 foreach(i; 0..data.length) 182 { 183 data.location[i] += data.velocity[i] * launcher.deltaTime; 184 } 185 } 186 } 187 } 188 189 /** 190 *System is responsible for movement of objects with CInput component. 191 *In this example every entity has same speed when using movement system. 192 */ 193 struct InputMovementSystem 194 { 195 mixin ECS.System!32; 196 197 vec2 move_vector; 198 199 struct EntitiesData 200 { 201 uint length; 202 //read only components can be marked with @readonly attribute or with const expression instead 203 const (CInput)[] input; 204 //components are treated as required by default 205 //CLocation[] locations; 206 CVelocity[] velocity; 207 //CTexture[] textures; 208 } 209 210 /** 211 *onBegin gives opportunity to check keys once and call update on entities only when 212 *one key is pressed. 213 */ 214 bool onBegin() 215 { 216 move_vector = vec2(0,0); 217 if(launcher.getKeyState(SDL_SCANCODE_W)) 218 { 219 move_vector += vec2(0,1); 220 } 221 else if(launcher.getKeyState(SDL_SCANCODE_S)) 222 { 223 move_vector += vec2(0,-1); 224 } 225 if(launcher.getKeyState(SDL_SCANCODE_A)) 226 { 227 move_vector += vec2(-1,0); 228 } 229 else if(launcher.getKeyState(SDL_SCANCODE_D)) 230 { 231 move_vector += vec2(1,0); 232 } 233 234 if(move_vector.x != 0 ||move_vector.y != 0) 235 { 236 move_vector = move_vector / sqrtf(move_vector.x * move_vector.x + move_vector.y * move_vector.y); 237 return true; 238 } 239 //don't call system update because no key pressed 240 return false; 241 } 242 243 /** 244 *Update is called multiple times in one "manager.update()" call. 245 *Number of "onUpdate" calls is count of buffers which must be updated during pass. 246 *When multithreading is used, number of "onUpdate" calls can be greater due to fact that 247 *JobSystem can split buffers for better data packing. 248 */ 249 void onUpdate(EntitiesData data) 250 { 251 foreach(i; 0..data.length) 252 { 253 data.velocity[i] += move_vector * launcher.deltaTime * 0.005; 254 if(data.velocity[i].x > 0.5)data.velocity[i].x = 0.5; 255 else if(data.velocity[i].x < -0.5)data.velocity[i].x = -0.5; 256 if(data.velocity[i].y > 0.5)data.velocity[i].y = 0.5; 257 else if(data.velocity[i].y < -0.5)data.velocity[i].y = -0.5; 258 } 259 } 260 }