1 module collider; 2 3 import optional; 4 import std.algorithm; 5 import dsfml.graphics; 6 7 /// Represents a collider composed of multiple rectangles 8 class Collider : Drawable { 9 private static draw_colliders = false; 10 11 private FloatRect[] rects; 12 private FloatRect bounds; 13 private float initial_tex_width = 0; 14 private auto delayed_translation = optional.no!Vector2f; 15 16 private void ensure_translation() { 17 if(delayed_translation == optional.none) { 18 return; 19 } 20 immutable vec = delayed_translation.or(Vector2f(0, 0)); 21 22 foreach(ref r; rects) { 23 r.top += vec.y; 24 r.left += vec.x; 25 } 26 delayed_translation = optional.no!Vector2f; 27 } 28 29 /// Initializes a collider from a texture, all pixels with alpha > 127 collide 30 this(const Texture tex) { 31 bounds = FloatRect(0, 0, tex.getSize.x, tex.getSize.y); 32 auto image = tex.copyToImage; 33 foreach(x; 0..image.getSize.x) { 34 auto fr = FloatRect(x, 0, 1, 0); 35 36 foreach(y; 0..image.getSize.y) { 37 immutable color = image.getPixel(x, y); 38 immutable ok = color.a > 127 && color != Color(247, 247, 247); 39 40 if(ok) { 41 fr.height += 1; 42 } else { 43 if(fr.height != 0) { 44 rects ~= fr; 45 } 46 fr = FloatRect(x, y, 1, 0); 47 } 48 } 49 if(fr.height != 0) { 50 rects ~= fr; 51 } 52 } 53 54 initial_tex_width = tex.getSize.x; 55 } 56 57 override void draw(RenderTarget target, RenderStates states) { 58 if(!draw_colliders) { 59 return; 60 } 61 62 ensure_translation(); 63 auto rs = new RectangleShape; 64 foreach(r; rects) { 65 rs.position(Vector2f(r.left, r.top)); 66 rs.size(Vector2f(r.width, r.height)); 67 rs.fillColor(Color(0, 255, 0, 127)); 68 target.draw(rs, states); 69 } 70 71 rs.position(Vector2f(bounds.left, bounds.top)); 72 rs.size(Vector2f(bounds.width, bounds.height)); 73 rs.fillColor(Color(255, 0, 0, 127)); 74 target.draw(rs); 75 } 76 77 /// Returns true if two colliders intersect 78 bool intersects(Collider other) { 79 if(!bounds.intersects(other.bounds)) { 80 return false; 81 } 82 83 ensure_translation(); 84 other.ensure_translation(); 85 foreach(r1; rects) 86 foreach(r2; other.rects) { 87 if(r1.intersects(r2)) { 88 return true; 89 } 90 } 91 92 return false; 93 } 94 95 /// Initializes an empty collider 96 private this() { } 97 98 /// Returns a collider translated by the given vector 99 Collider translate(Vector2f vec) const { 100 auto c = new Collider; 101 c.rects = rects.dup; 102 if(delayed_translation == optional.none) { 103 c.delayed_translation = optional.some(vec); 104 } else { 105 c.delayed_translation = vec + delayed_translation; 106 } 107 108 c.bounds = bounds; 109 c.bounds.top += vec.y; 110 c.bounds.left += vec.x; 111 c.initial_tex_width = initial_tex_width; 112 113 return c; 114 } 115 116 /// Merges colliders 117 void merge(Collider other) { 118 ensure_translation(); 119 other.ensure_translation(); 120 121 foreach(rect; other.rects) { 122 rects ~= rect; 123 } 124 if(bounds.left > other.bounds.left) { 125 bounds.left = other.bounds.left; 126 } 127 if(bounds.top > other.bounds.top) { 128 bounds.top = other.bounds.top; 129 } 130 if(bounds.left + bounds.width < other.bounds.left + other.bounds.width) { 131 bounds.width = other.bounds.left - bounds.left + other.bounds.width; 132 } 133 if(bounds.top + bounds.height < other.bounds.top + other.bounds.height) { 134 bounds.height = other.bounds.top - bounds.top + other.bounds.height; 135 } 136 137 initial_tex_width = max(initial_tex_width, other.initial_tex_width); 138 } 139 140 /// Replicates a collider horizontally 141 Collider replicate(uint times) const { 142 Collider c = new Collider; 143 144 foreach(i; 0..times) { 145 c.merge(translate(Vector2f(i * initial_tex_width, 0))); 146 } 147 148 c.initial_tex_width = initial_tex_width * times; 149 150 return c; 151 } 152 }