1 /++ 2 The module describing the unit of the object - Instance. 3 4 An instance is an object in a scene with behavior only for itself with the 5 interaction of other instances, through collisions, any internal events. 6 Each instance has properties of execution conditions, rendering conditions, 7 rendering properties, conditions for the execution of some events. All of these 8 properties describe an instance, however, more behavior can be achieved using 9 inheritance (see `tida.localevent`). Instance functions are not inherited. but 10 they are directly written and marked with attributes that give execution 11 conditions (under what conditions it is necessary to execute, as if it is a 12 transfer of control between scenes, rendering of a frame, processing user input). 13 14 Also, an instance has a hard mask, where, when it touches another mask, 15 a collision event can be generated and such instances can handle this event, 16 if, of course, the corresponding functions have been marked with attributes. 17 18 --- 19 class MyObject : Instance 20 { 21 this() { ... } 22 23 @Init 24 void onInitFunction() 25 { 26 firstProperty = 0.0f; 27 position = vecf(32f, 11.5f); 28 ... 29 } 30 31 @Collision("OtherInstanceName") 32 void onCollsion(Instance other) 33 { 34 other.posiiton -= vecf(1, 0); 35 } 36 } 37 --- 38 39 Macros: 40 LREF = <a href="#$1">$1</a> 41 HREF = <a href="$1">$2</a> 42 43 Authors: $(HREF https://github.com/TodNaz,TodNaz) 44 Copyright: Copyright (c) 2020 - 2021, TodNaz. 45 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT) 46 +/ 47 module tida.instance; 48 49 /++ 50 Checks if an object is an instance. 51 +/ 52 template isInstance(T) 53 { 54 enum isInstance = is(T : Instance); 55 } 56 57 /++ 58 Instance object. Can be created for a render unit as well as for legacy 59 with a programmable model. 60 +/ 61 class Instance 62 { 63 import tida.vector; 64 import tida.sprite; 65 import tida.shape; 66 import tida.component; 67 68 protected: 69 /++ 70 Instance sprite. Will be output at the position of the instance. 71 +/ 72 Sprite sprite; 73 74 /++ 75 Components of an instance, complementing its functionality. 76 +/ 77 Component[] components; 78 79 /// only for call. 80 bool _destroy = false; 81 82 public: 83 /++ 84 The name of the instance, by which you can later identify 85 the collided or other events. 86 +/ 87 string name; 88 89 /++ 90 Instance tags. By this parameter, it is possible to distribute an instance 91 about groups, for example, instances that should not let the player in upon 92 collision will be marked with the "solid" tag, but not necessarily only 93 non-living creatures should be used, and others who should not squeeze 94 through are marked with such a tag. 95 (This is an example, there is no such implementation in the framework). 96 +/ 97 string[] tags; 98 99 /++ 100 The position of the instance. Serves for collision, rendering, and 101 other instance services. 102 +/ 103 Vector!float position = vec!float(0.0f, 0.0f); 104 105 /++ 106 An auxiliary variable that remembers the position in the previous 107 pass of the game loop. 108 +/ 109 Vector!float previous = vec!float(0.0f, 0.0f); 110 111 /++ 112 Collision mask. A mask is a kind of geometric shape (or several shapes) 113 that sets the collision boundary between other instances. 114 +/ 115 Shape!float mask; 116 117 /++ 118 A property that determines whether an instance can collide with 119 other instances. 120 +/ 121 bool solid = false; 122 123 /++ 124 Instance identifier (often means storage location in an instances array). 125 +/ 126 size_t id; 127 128 /++ 129 The identifier for the instance in the stream. Shows which thread the 130 instance is running on (changing the property does not affect thread selection). 131 +/ 132 size_t threadid; 133 134 /++ 135 A property indicating whether to handle all events for such an instance. 136 If false, no event will be processed for this instance, however, 137 it will exist. It is necessary if you do not need to delete the instance, 138 but you also do not need to process its events. 139 +/ 140 bool active = true; 141 142 /++ 143 A property that indicates whether to render the instance and perform 144 rendering functions. 145 +/ 146 bool visible = true; 147 148 /++ 149 A property that indicates whether it is only necessary to draw the object 150 without handling other events in it. 151 +/ 152 bool onlyDraw = false; 153 154 /++ 155 A property that indicates whether to transition to a new scene when 156 transferring control to another scene. 157 +/ 158 bool persistent = false; 159 160 /++ 161 The identifier of the layer in which the instance is placed. 162 The render queue is affected, the larger the number, the later the 163 instance will be rendered. 164 +/ 165 int depth = 0; 166 167 @safe: 168 this() 169 { 170 sprite = new Sprite(); 171 172 name = typeid(this).stringof; 173 } 174 175 /++ 176 Removes an instance. However, not immediately, this will only happen on 177 the next iteration of the game loop for thread safety. 178 +/ 179 final void destroy() 180 { 181 _destroy = true; 182 } 183 184 /++ 185 A method for adding an instance to an instance to expand functionality. 186 187 Params: 188 component = Component object. 189 +/ 190 void add(T)(T component) 191 { 192 import tida.scenemanager; 193 static assert(isComponent!T, T.stringof ~ " is not a component!"); 194 195 components ~= component; 196 if (component.name == "") 197 component.name = T.stringof; 198 199 sceneManager.componentExplore!T(this, component); 200 } 201 202 /++ 203 A method for adding an instance to an instance to expand functionality. 204 205 Params: 206 T = Component type. 207 +/ 208 void add(T)() 209 { 210 add(new T()); 211 } 212 213 /++ 214 A function that returns a component based on its class. 215 216 Params: 217 T = Component type. 218 +/ 219 T cmp(T)() 220 { 221 static assert(isComponent!T, T.stringof ~ " is not a component!"); 222 223 foreach (e; components) 224 { 225 if ((cast(T) e) !is null) 226 { 227 return cast(T) e; 228 } 229 } 230 231 return null; 232 } 233 234 /++ 235 Finds a component by its name. 236 237 Params: 238 name = Component name. 239 +/ 240 Component cmp(string name) 241 { 242 foreach (e; components) 243 { 244 if (e.name == name) 245 return e; 246 } 247 248 return null; 249 } 250 251 /++ 252 Detaches a component from an instance by finding it by class. 253 254 Params: 255 T = Component type. 256 +/ 257 void dissconnect(T)() 258 { 259 import std.algorithm : remove; 260 import tida.scenemanager; 261 static assert(isComponent!T, "`" ~ T.stringof ~ "` is not a component!"); 262 263 Component cmp; 264 265 foreach (i; 0 .. components.length) 266 { 267 if ((cast(T) components[i]) !is null) 268 { 269 cmp = components[i]; 270 271 foreach(fun; sceneManager.leaveComponents[cmp]) fun(); 272 273 components = components.remove(i); 274 break; 275 } 276 } 277 } 278 279 /++ 280 Detaches a component from an instance by finding it by name. 281 282 Params: 283 T = Component type. 284 +/ 285 void dissconnect(string name) 286 { 287 import std.algorithm : remove; 288 import tida.scenemanager; 289 290 foreach (i; 0 .. components.length) 291 { 292 if (components[i].name == name) 293 { 294 foreach(fun; sceneManager.leaveComponents[components[i]]) fun(); 295 296 components = components.remove(i); 297 break; 298 } 299 } 300 } 301 302 /++ 303 Detaches absolutely all components in this instance. 304 +/ 305 void dissconnectAll() @trusted 306 { 307 import tida.scenemanager; 308 import std.algorithm : remove; 309 310 foreach (i; 0 .. components.length) 311 { 312 if (sceneManager !is null) 313 { 314 foreach(fun; sceneManager.leaveComponents[components[i]]) fun(); 315 } 316 317 components = components.remove(i); 318 } 319 } 320 321 package(tida): 322 Sprite spriteDraw() 323 { 324 return sprite; 325 } 326 327 bool isDestroy() 328 { 329 return _destroy; 330 } 331 332 Component[] getComponents() 333 { 334 return components; 335 } 336 } 337 338 unittest 339 { 340 import tida.scenemanager; 341 import tida.component; 342 343 initSceneManager(); 344 345 class CComponent : Component { } 346 347 CComponent cmp = new CComponent(); 348 cmp.name = "Cmp"; 349 350 Instance instance = new Instance(); 351 instance.add(cmp); 352 353 assert(instance.cmp("Cmp") is (cmp)); 354 assert(instance.cmp!(CComponent) is (cmp)); 355 } 356 357 debug import tida.color; 358 359 debug template debugCollisionMask(Color!ubyte color = Color!ubyte(255, 0, 0)) 360 { 361 import tida.render; 362 import std.conv : to; 363 364 void __drawShapeConture(Vecf releative, Shapef shape, IRenderer render) @safe 365 { 366 switch (shape.type) 367 { 368 case ShapeType.point: 369 render.point(shape.begin + releative, color); 370 break; 371 372 case ShapeType.line: 373 render.line([ shape.begin + releative, 374 shape.end + releative], color); 375 break; 376 377 case ShapeType.rectangle: 378 render.rectangle( shape.begin + releative, 379 shape.width.to!uint, 380 shape.height.to!uint, 381 color, 382 false); 383 break; 384 385 case ShapeType.circle: 386 render.circle(shape.begin + releative, shape.radius, color, false); 387 break; 388 389 case ShapeType.triangle: 390 render.triangle([ shape.vertex!0 + releative, 391 shape.vertex!1 + releative, 392 shape.vertex!2 + releative], color, false); 393 break; 394 395 case ShapeType.polygon: 396 render.polygon(shape.begin + releative, shape.data, color, false); 397 break; 398 399 case ShapeType.multi: 400 foreach (sh; shape.shapes) 401 { 402 __drawShapeConture(releative + shape.begin, sh, render); 403 } 404 break; 405 406 default: 407 return; 408 } 409 } 410 411 @Event!Draw 412 void __debug_drawMask(IRenderer render) @safe 413 { 414 __drawShapeConture(position, mask, render); 415 } 416 }