Grid.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. //---------------------------------------------------------------------------------
  2. // Ported to the Atomic Game Engine
  3. // Originally written for XNA by Michael Hoffman
  4. // Find the full tutorial at: http://gamedev.tutsplus.com/series/vector-shooter-xna/
  5. //----------------------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Generic;
  8. using AtomicEngine;
  9. namespace AtomicBlaster
  10. {
  11. public class Grid
  12. {
  13. class PointMass
  14. {
  15. public Vector3 Position;
  16. public Vector3 Velocity;
  17. public float InverseMass;
  18. private Vector3 acceleration;
  19. private float damping = 0.98f;
  20. public PointMass(Vector3 position, float invMass)
  21. {
  22. Position = position;
  23. InverseMass = invMass;
  24. }
  25. public void ApplyForce(Vector3 force)
  26. {
  27. acceleration += force * InverseMass;
  28. }
  29. public void IncreaseDamping(float factor)
  30. {
  31. damping *= factor;
  32. }
  33. public void Update()
  34. {
  35. Velocity += acceleration;
  36. Position += Velocity;
  37. acceleration = Vector3.Zero;
  38. if (Velocity.LengthSquared < 0.001f * 0.001f)
  39. Velocity = Vector3.Zero;
  40. Velocity *= damping;
  41. damping = 0.98f;
  42. }
  43. }
  44. struct Spring
  45. {
  46. public PointMass End1;
  47. public PointMass End2;
  48. public float TargetLength;
  49. public float Stiffness;
  50. public float Damping;
  51. public Spring(PointMass end1, PointMass end2, float stiffness, float damping)
  52. {
  53. End1 = end1;
  54. End2 = end2;
  55. Stiffness = stiffness;
  56. Damping = damping;
  57. TargetLength = Vector3.Distance(end1.Position, end2.Position) * 0.95f;
  58. }
  59. public void Update()
  60. {
  61. var x = End1.Position - End2.Position;
  62. float length = x.Length;
  63. // these springs can only pull, not push
  64. if (length <= TargetLength)
  65. return;
  66. x = (x / length) * (length - TargetLength);
  67. var dv = End2.Velocity - End1.Velocity;
  68. var force = Stiffness * x - dv * Damping;
  69. End1.ApplyForce(-force);
  70. End2.ApplyForce(force);
  71. }
  72. }
  73. Spring[] springs;
  74. PointMass[,] points;
  75. Vector2 screenSize;
  76. public Grid(IntRect size, Vector2 spacing)
  77. {
  78. var springList = new List<Spring>();
  79. int numColumns = (int)(size.Width / spacing.X) + 1;
  80. int numRows = (int)(size.Height / spacing.Y) + 1;
  81. points = new PointMass[numColumns, numRows];
  82. // these fixed points will be used to anchor the grid to fixed positions on the screen
  83. PointMass[,] fixedPoints = new PointMass[numColumns, numRows];
  84. // create the point masses
  85. int column = 0, row = 0;
  86. for (float y = size.Top; y <= size.Bottom; y += spacing.Y)
  87. {
  88. for (float x = size.Left; x <= size.Right; x += spacing.X)
  89. {
  90. points[column, row] = new PointMass(new Vector3(x, y, 0), 1);
  91. fixedPoints[column, row] = new PointMass(new Vector3(x, y, 0), 0);
  92. column++;
  93. }
  94. row++;
  95. column = 0;
  96. }
  97. // link the point masses with springs
  98. for (int y = 0; y < numRows; y++)
  99. for (int x = 0; x < numColumns; x++)
  100. {
  101. if (x == 0 || y == 0 || x == numColumns - 1 || y == numRows - 1) // anchor the border of the grid
  102. springList.Add(new Spring(fixedPoints[x, y], points[x, y], 0.1f, 0.1f));
  103. else if (x % 3 == 0 && y % 3 == 0) // loosely anchor 1/9th of the point masses
  104. springList.Add(new Spring(fixedPoints[x, y], points[x, y], 0.002f, 0.02f));
  105. const float stiffness = 0.28f;
  106. const float damping = 0.06f;
  107. if (x > 0)
  108. springList.Add(new Spring(points[x - 1, y], points[x, y], stiffness, damping));
  109. if (y > 0)
  110. springList.Add(new Spring(points[x, y - 1], points[x, y], stiffness, damping));
  111. }
  112. springs = springList.ToArray();
  113. }
  114. public void ApplyDirectedForce(Vector2 force, Vector2 position, float radius)
  115. {
  116. ApplyDirectedForce(new Vector3(force, 0), new Vector3(position, 0), radius);
  117. }
  118. public void ApplyDirectedForce(Vector3 force, Vector3 position, float radius)
  119. {
  120. foreach (var mass in points)
  121. if (Vector3.DistanceSquared(position, mass.Position) < radius * radius)
  122. mass.ApplyForce(10 * force / (10 + Vector3.Distance(position, mass.Position)));
  123. }
  124. public void ApplyImplosiveForce(float force, Vector2 position, float radius)
  125. {
  126. ApplyImplosiveForce(force, new Vector3(position, 0), radius);
  127. }
  128. public void ApplyImplosiveForce(float force, Vector3 position, float radius)
  129. {
  130. foreach (var mass in points)
  131. {
  132. float dist2 = Vector3.DistanceSquared(position, mass.Position);
  133. if (dist2 < radius * radius)
  134. {
  135. mass.ApplyForce(10 * force * (position - mass.Position) / (100 + dist2));
  136. mass.IncreaseDamping(0.6f);
  137. }
  138. }
  139. }
  140. public void ApplyExplosiveForce(float force, Vector2 position, float radius)
  141. {
  142. ApplyExplosiveForce(force, new Vector3(position, 0), radius);
  143. }
  144. public void ApplyExplosiveForce(float force, Vector3 position, float radius)
  145. {
  146. foreach (var mass in points)
  147. {
  148. float dist2 = Vector3.DistanceSquared(position, mass.Position);
  149. if (dist2 < radius * radius)
  150. {
  151. mass.ApplyForce(100 * force * (mass.Position - position) / (10000 + dist2));
  152. mass.IncreaseDamping(0.6f);
  153. }
  154. }
  155. }
  156. public void Update()
  157. {
  158. foreach (var spring in springs)
  159. spring.Update();
  160. foreach (var mass in points)
  161. mass.Update();
  162. }
  163. public void Draw(/*SpriteBatch spriteBatch*/)
  164. {
  165. screenSize = GameRoot.ScreenSize;
  166. int width = points.GetLength(0);
  167. int height = points.GetLength(1);
  168. Color color = new Color(30/255.0f, 30/255.0f, 139/255.0f, 100/255.0f); // dark blue
  169. for (int y = 1; y < height; y++)
  170. {
  171. for (int x = 1; x < width; x++)
  172. {
  173. Vector2 left = new Vector2(), up = new Vector2();
  174. Vector2 p = ToVec2(points[x, y].Position);
  175. if (x > 1)
  176. {
  177. left = ToVec2(points[x - 1, y].Position);
  178. float thickness = y % 3 == 1 ? 3f : 1f;
  179. // use Catmull-Rom interpolation to help smooth bends in the grid
  180. int clampedX = Math.Min(x + 1, width - 1);
  181. Vector2 mid = Vector2.CatmullRom(ToVec2(points[x - 2, y].Position), left, p, ToVec2(points[clampedX, y].Position), 0.5f);
  182. // If the grid is very straight here, draw a single straight line. Otherwise, draw lines to our
  183. // new interpolated midpoint
  184. if (Vector2.DistanceSquared(mid, (left + p) / 2) > 1)
  185. {
  186. CustomRenderer.DrawLine(left, mid, color, thickness);
  187. CustomRenderer.DrawLine(mid, p, color, thickness);
  188. }
  189. else
  190. CustomRenderer.DrawLine(left, p, color, thickness);
  191. }
  192. if (y > 1)
  193. {
  194. up = ToVec2(points[x, y - 1].Position);
  195. float thickness = x % 3 == 1 ? 3f : 1f;
  196. int clampedY = Math.Min(y + 1, height - 1);
  197. Vector2 mid = Vector2.CatmullRom(ToVec2(points[x, y - 2].Position), up, p, ToVec2(points[x, clampedY].Position), 0.5f);
  198. if (Vector2.DistanceSquared(mid, (up + p) / 2) > 1)
  199. {
  200. CustomRenderer.DrawLine(up, mid, color, thickness);
  201. CustomRenderer.DrawLine(mid, p, color, thickness);
  202. }
  203. else
  204. CustomRenderer.DrawLine(up, p, color, thickness);
  205. }
  206. // Add interpolated lines halfway between our point masses. This makes the grid look
  207. // denser without the cost of simulating more springs and point masses.
  208. if (x > 1 && y > 1)
  209. {
  210. Vector2 upLeft = ToVec2(points[x - 1, y - 1].Position);
  211. CustomRenderer.DrawLine(0.5f * (upLeft + up), 0.5f * (left + p), color, 1f); // vertical line
  212. CustomRenderer.DrawLine(0.5f * (upLeft + left), 0.5f * (up + p), color, 1f); // horizontal line
  213. }
  214. }
  215. }
  216. }
  217. public Vector2 ToVec2(Vector3 v)
  218. {
  219. // do a perspective projection
  220. float factor = (v.Z + 2000) / 2000;
  221. return (new Vector2(v.X, v.Y) - screenSize / 2f) * factor + screenSize / 2;
  222. }
  223. }
  224. }