AbstractForceController.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. using System;
  2. using FarseerPhysics.Dynamics;
  3. using Microsoft.Xna.Framework;
  4. namespace FarseerPhysics.Controllers
  5. {
  6. public abstract class AbstractForceController : Controller
  7. {
  8. /// <summary>
  9. /// Modes for Decay. Actual Decay must be implemented in inheriting
  10. /// classes
  11. /// </summary>
  12. public enum DecayModes
  13. {
  14. None,
  15. Step,
  16. Linear,
  17. InverseSquare,
  18. Curve
  19. }
  20. /// <summary>
  21. /// Forcetypes are used in the decay math to properly get the distance.
  22. /// They are also used to draw a representation in DebugView
  23. /// </summary>
  24. public enum ForceTypes
  25. {
  26. Point,
  27. Line,
  28. Area
  29. }
  30. /// <summary>
  31. /// Timing Modes
  32. /// Switched: Standard on/off mode using the baseclass enabled property
  33. /// Triggered: When the Trigger() method is called the force is active
  34. /// for a specified Impulse Length
  35. /// Curve: Still to be defined. The basic idea is having a Trigger
  36. /// combined with a curve for the strength
  37. /// </summary>
  38. public enum TimingModes
  39. {
  40. Switched,
  41. Triggered,
  42. Curve
  43. }
  44. /// <summary>
  45. /// Curve to be used for Decay in Curve mode
  46. /// </summary>
  47. public Curve DecayCurve;
  48. /// <summary>
  49. /// The Forcetype of the instance
  50. /// </summary>
  51. public ForceTypes ForceType;
  52. /// <summary>
  53. /// Provided for reuse to provide Variation functionality in
  54. /// inheriting classes
  55. /// </summary>
  56. protected Random Randomize;
  57. /// <summary>
  58. /// Curve used by Curve Mode as an animated multiplier for the force
  59. /// strength.
  60. /// Only positions between 0 and 1 are considered as that range is
  61. /// stretched to have ImpulseLength.
  62. /// </summary>
  63. public Curve StrengthCurve;
  64. /// <summary>
  65. /// Constructor
  66. /// </summary>
  67. public AbstractForceController()
  68. : base(ControllerType.AbstractForceController)
  69. {
  70. Enabled = true;
  71. Strength = 1.0f;
  72. Position = new Vector2(0, 0);
  73. MaximumSpeed = 100.0f;
  74. TimingMode = TimingModes.Switched;
  75. ImpulseTime = 0.0f;
  76. ImpulseLength = 1.0f;
  77. Triggered = false;
  78. StrengthCurve = new Curve();
  79. Variation = 0.0f;
  80. Randomize = new Random(1234);
  81. DecayMode = DecayModes.None;
  82. DecayCurve = new Curve();
  83. DecayStart = 0.0f;
  84. DecayEnd = 0.0f;
  85. StrengthCurve.Keys.Add(new CurveKey(0, 5));
  86. StrengthCurve.Keys.Add(new CurveKey(0.1f, 5));
  87. StrengthCurve.Keys.Add(new CurveKey(0.2f, -4));
  88. StrengthCurve.Keys.Add(new CurveKey(1f, 0));
  89. }
  90. /// <summary>
  91. /// Overloaded Contstructor with supplying Timing Mode
  92. /// </summary>
  93. /// <param name="mode"></param>
  94. public AbstractForceController(TimingModes mode)
  95. : base(ControllerType.AbstractForceController)
  96. {
  97. TimingMode = mode;
  98. switch (mode)
  99. {
  100. case TimingModes.Switched:
  101. Enabled = true;
  102. break;
  103. case TimingModes.Triggered:
  104. Enabled = false;
  105. break;
  106. case TimingModes.Curve:
  107. Enabled = false;
  108. break;
  109. }
  110. }
  111. /// <summary>
  112. /// Global Strength of the force to be applied
  113. /// </summary>
  114. public float Strength { get; set; }
  115. /// <summary>
  116. /// Position of the Force. Can be ignored (left at (0,0) for forces
  117. /// that are not position-dependent
  118. /// </summary>
  119. public Vector2 Position { get; set; }
  120. /// <summary>
  121. /// Maximum speed of the bodies. Bodies that are travelling faster are
  122. /// supposed to be ignored
  123. /// </summary>
  124. public float MaximumSpeed { get; set; }
  125. /// <summary>
  126. /// Maximum Force to be applied. As opposed to Maximum Speed this is
  127. /// independent of the velocity of
  128. /// the affected body
  129. /// </summary>
  130. public float MaximumForce { get; set; }
  131. /// <summary>
  132. /// Timing Mode of the force instance
  133. /// </summary>
  134. public TimingModes TimingMode { get; set; }
  135. /// <summary>
  136. /// Time of the current impulse. Incremented in update till
  137. /// ImpulseLength is reached
  138. /// </summary>
  139. public float ImpulseTime { get; private set; }
  140. /// <summary>
  141. /// Length of a triggered impulse. Used in both Triggered and Curve Mode
  142. /// </summary>
  143. public float ImpulseLength { get; set; }
  144. /// <summary>
  145. /// Indicating if we are currently during an Impulse
  146. /// (Triggered and Curve Mode)
  147. /// </summary>
  148. public bool Triggered { get; private set; }
  149. /// <summary>
  150. /// Variation of the force applied to each body affected
  151. /// !! Must be used in inheriting classes properly !!
  152. /// </summary>
  153. public float Variation { get; set; }
  154. /// <summary>
  155. /// See DecayModes
  156. /// </summary>
  157. public DecayModes DecayMode { get; set; }
  158. /// <summary>
  159. /// Start of the distance based Decay. To set a non decaying area
  160. /// </summary>
  161. public float DecayStart { get; set; }
  162. /// <summary>
  163. /// Maximum distance a force should be applied
  164. /// </summary>
  165. public float DecayEnd { get; set; }
  166. /// <summary>
  167. /// Calculate the Decay for a given body. Meant to ease force
  168. /// development and stick to the DRY principle and provide unified and
  169. /// predictable decay math.
  170. /// </summary>
  171. /// <param name="body">The body to calculate decay for</param>
  172. /// <returns>A multiplier to multiply the force with to add decay
  173. /// support in inheriting classes</returns>
  174. protected float GetDecayMultiplier(Body body)
  175. {
  176. //TODO: Consider ForceType in distance calculation!
  177. float distance = (body.Position - Position).Length();
  178. switch (DecayMode)
  179. {
  180. case DecayModes.None:
  181. {
  182. return 1.0f;
  183. }
  184. case DecayModes.Step:
  185. {
  186. if (distance < DecayEnd)
  187. return 1.0f;
  188. else
  189. return 0.0f;
  190. }
  191. case DecayModes.Linear:
  192. {
  193. if (distance < DecayStart)
  194. return 1.0f;
  195. if (distance > DecayEnd)
  196. return 0.0f;
  197. return (DecayEnd - DecayStart / distance - DecayStart);
  198. }
  199. case DecayModes.InverseSquare:
  200. {
  201. if (distance < DecayStart)
  202. return 1.0f;
  203. else
  204. return 1.0f / ((distance - DecayStart) * (distance - DecayStart));
  205. }
  206. case DecayModes.Curve:
  207. {
  208. if (distance < DecayStart)
  209. return 1.0f;
  210. else
  211. return DecayCurve.Evaluate(distance - DecayStart);
  212. }
  213. default:
  214. return 1.0f;
  215. }
  216. }
  217. /// <summary>
  218. /// Triggers the trigger modes (Trigger and Curve)
  219. /// </summary>
  220. public void Trigger()
  221. {
  222. Triggered = true;
  223. ImpulseTime = 0;
  224. }
  225. /// <summary>
  226. /// Inherited from Controller
  227. /// Depending on the TimingMode perform timing logic and call ApplyForce()
  228. /// </summary>
  229. /// <param name="dt"></param>
  230. public override void Update(float dt)
  231. {
  232. switch (TimingMode)
  233. {
  234. case TimingModes.Switched:
  235. {
  236. if (Enabled)
  237. {
  238. ApplyForce(dt, Strength);
  239. }
  240. break;
  241. }
  242. case TimingModes.Triggered:
  243. {
  244. if (Enabled && Triggered)
  245. {
  246. if (ImpulseTime < ImpulseLength)
  247. {
  248. ApplyForce(dt, Strength);
  249. ImpulseTime += dt;
  250. }
  251. else
  252. {
  253. Triggered = false;
  254. }
  255. }
  256. break;
  257. }
  258. case TimingModes.Curve:
  259. {
  260. if (Enabled && Triggered)
  261. {
  262. if (ImpulseTime < ImpulseLength)
  263. {
  264. ApplyForce(dt, Strength * StrengthCurve.Evaluate(ImpulseTime));
  265. ImpulseTime += dt;
  266. }
  267. else
  268. {
  269. Triggered = false;
  270. }
  271. }
  272. break;
  273. }
  274. }
  275. }
  276. /// <summary>
  277. /// Apply the force supplying strength (wich is modified in Update()
  278. /// according to the TimingMode
  279. /// </summary>
  280. /// <param name="dt"></param>
  281. /// <param name="strength">The strength</param>
  282. public abstract void ApplyForce(float dt, float strength);
  283. }
  284. }