Grid.cs 9.8 KB

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