Bird.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. //-----------------------------------------------------------------------------
  2. // Bird.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Text;
  10. using Microsoft.Xna.Framework;
  11. using Microsoft.Xna.Framework.Audio;
  12. using Microsoft.Xna.Framework.Content;
  13. using Microsoft.Xna.Framework.Graphics;
  14. using Microsoft.Xna.Framework.Input;
  15. namespace Flocking
  16. {
  17. class Bird : Animal
  18. {
  19. protected Random random;
  20. Vector2 aiNewDir;
  21. int aiNumSeen;
  22. /// <summary>
  23. /// Bird constructor
  24. /// </summary>
  25. /// <param name="tex"></param>
  26. /// <param name="dir">movement direction</param>
  27. /// <param name="loc">spawn location</param>
  28. /// <param name="screenSize">screen size</param>
  29. public Bird(Texture2D tex, Vector2 dir, Vector2 loc,
  30. int screenWidth, int screenHeight)
  31. : base(tex, screenWidth, screenHeight)
  32. {
  33. direction = dir;
  34. direction.Normalize();
  35. location = loc;
  36. moveSpeed = 125.0f;
  37. fleeing = false;
  38. random = new Random((int)loc.X + (int)loc.Y);
  39. animaltype = AnimalType.Bird;
  40. BuildBehaviors();
  41. }
  42. /// <summary>
  43. /// update bird position, wrapping around the screen edges
  44. /// </summary>
  45. /// <param name="gameTime"></param>
  46. public void Update(GameTime gameTime, ref AIParameters aiParams)
  47. {
  48. float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
  49. Vector2 randomDir = Vector2.Zero;
  50. randomDir.X = (float)random.NextDouble() - 0.5f;
  51. randomDir.Y = (float)random.NextDouble() - 0.5f;
  52. Vector2.Normalize(ref randomDir, out randomDir);
  53. if (aiNumSeen > 0)
  54. {
  55. aiNewDir = (direction * aiParams.MoveInOldDirectionInfluence) +
  56. (aiNewDir * (aiParams.MoveInFlockDirectionInfluence /
  57. (float)aiNumSeen));
  58. }
  59. else
  60. {
  61. aiNewDir = direction * aiParams.MoveInOldDirectionInfluence;
  62. }
  63. aiNewDir += (randomDir * aiParams.MoveInRandomDirectionInfluence);
  64. Vector2.Normalize(ref aiNewDir, out aiNewDir);
  65. aiNewDir = ChangeDirection(direction, aiNewDir,
  66. aiParams.MaxTurnRadians * elapsedTime);
  67. direction = aiNewDir;
  68. if (direction.LengthSquared() > .01f)
  69. {
  70. Vector2 moveAmount = direction * moveSpeed * elapsedTime;
  71. location = location + moveAmount;
  72. //wrap bird to the other side of the screen if needed
  73. if (location.X < 0.0f)
  74. {
  75. location.X = boundryWidth + location.X;
  76. }
  77. else if (location.X > boundryWidth)
  78. {
  79. location.X = location.X - boundryWidth;
  80. }
  81. location.Y += direction.Y * moveSpeed * elapsedTime;
  82. if (location.Y < 0.0f)
  83. {
  84. location.Y = boundryHeight + location.Y;
  85. }
  86. else if (location.Y > boundryHeight)
  87. {
  88. location.Y = location.Y - boundryHeight;
  89. }
  90. }
  91. }
  92. /// <summary>
  93. /// Draw the bird, tinting it if it's currently fleeing
  94. /// </summary>
  95. /// <param name="spriteBatch"></param>
  96. /// <param name="gameTime"></param>
  97. public override void Draw(SpriteBatch spriteBatch, GameTime gameTime)
  98. {
  99. Color tintColor = color;
  100. float rotation = 0.0f;
  101. rotation = (float)Math.Atan2(direction.Y, direction.X);
  102. // if the entity is highlighted, we want to make it pulse with a red tint.
  103. if (fleeing)
  104. {
  105. // to do this, we'll first generate a value t, which we'll use to
  106. // determine how much tint to have.
  107. float t = (float)Math.Sin(10 * gameTime.TotalGameTime.TotalSeconds);
  108. // Sin varies from -1 to 1, and we want t to go from 0 to 1, so we'll
  109. // scale it now.
  110. t = .5f + .5f * t;
  111. // finally, we'll calculate our tint color by using Lerp to generate
  112. // a color in between Red and White.
  113. tintColor = new Color(Vector4.Lerp(
  114. Color.Red.ToVector4(), Color.White.ToVector4(), t));
  115. }
  116. // Draw the animal, centered around its position, and using the
  117. //orientation and tint color.
  118. spriteBatch.Draw(texture, location, null, tintColor,
  119. rotation, textureCenter, 1.0f, SpriteEffects.None, 0.0f);
  120. }
  121. /// <summary>
  122. /// Instantiates all the behaviors that this Bird knows about
  123. /// </summary>
  124. public void BuildBehaviors()
  125. {
  126. Behaviors catReactions = new Behaviors();
  127. catReactions.Add(new FleeBehavior(this));
  128. behaviors.Add(AnimalType.Cat, catReactions);
  129. Behaviors birdReactions = new Behaviors();
  130. birdReactions.Add(new AlignBehavior(this));
  131. birdReactions.Add(new CohesionBehavior(this));
  132. birdReactions.Add(new SeparationBehavior(this));
  133. behaviors.Add(AnimalType.Bird, birdReactions);
  134. }
  135. /// <summary>
  136. /// Setup the bird to figure out it's new movement direction
  137. /// </summary>
  138. /// <param name="AIparams">flock AI parameters</param>
  139. public void ResetThink()
  140. {
  141. Fleeing = false;
  142. aiNewDir = Vector2.Zero;
  143. aiNumSeen = 0;
  144. reactionDistance = 0f;
  145. reactionLocation = Vector2.Zero;
  146. }
  147. /// <summary>
  148. /// Since we're wrapping movement around the screen, two point at extreme
  149. /// sides of the screen are actually very close together, this function
  150. /// figures out if destLocation is closer the srcLocation if you wrap around
  151. /// the screen
  152. /// </summary>
  153. /// <param name="srcLocation">screen location of src</param>
  154. /// <param name="destLocation">screen location of dest</param>
  155. /// <param name="outVector">relative location of dest to src</param>
  156. private void ClosestLocation(ref Vector2 srcLocation,
  157. ref Vector2 destLocation, out Vector2 outLocation)
  158. {
  159. outLocation = new Vector2();
  160. float x = destLocation.X;
  161. float y = destLocation.Y;
  162. float dX = Math.Abs(destLocation.X - srcLocation.X);
  163. float dY = Math.Abs(destLocation.Y - srcLocation.Y);
  164. // now see if the distance between birds is closer if going off one
  165. // side of the map and onto the other.
  166. if (Math.Abs(boundryWidth - destLocation.X + srcLocation.X) < dX)
  167. {
  168. dX = boundryWidth - destLocation.X + srcLocation.X;
  169. x = destLocation.X - boundryWidth;
  170. }
  171. if (Math.Abs(boundryWidth - srcLocation.X + destLocation.X) < dX)
  172. {
  173. dX = boundryWidth - srcLocation.X + destLocation.X;
  174. x = destLocation.X + boundryWidth;
  175. }
  176. if (Math.Abs(boundryHeight - destLocation.Y + srcLocation.Y) < dY)
  177. {
  178. dY = boundryHeight - destLocation.Y + srcLocation.Y;
  179. y = destLocation.Y - boundryHeight;
  180. }
  181. if (Math.Abs(boundryHeight - srcLocation.Y + destLocation.Y) < dY)
  182. {
  183. dY = boundryHeight - srcLocation.Y + destLocation.Y;
  184. y = destLocation.Y + boundryHeight;
  185. }
  186. outLocation.X = x;
  187. outLocation.Y = y;
  188. }
  189. /// <summary>
  190. /// React to an Animal based on it's type
  191. /// </summary>
  192. /// <param name="animal"></param>
  193. public void ReactTo(Animal animal, ref AIParameters AIparams)
  194. {
  195. if (animal != null)
  196. {
  197. //setting the the reactionLocation and reactionDistance here is
  198. //an optimization, many of the possible reactions use the distance
  199. //and location of theAnimal, so we might as well figure them out
  200. //only once !
  201. Vector2 otherLocation = animal.Location;
  202. ClosestLocation(ref location, ref otherLocation,
  203. out reactionLocation);
  204. reactionDistance = Vector2.Distance(location, reactionLocation);
  205. //we only react if theAnimal is close enough that we can see it
  206. if (reactionDistance < AIparams.DetectionDistance)
  207. {
  208. Behaviors reactions = behaviors[animal.AnimalType];
  209. foreach (Behavior reaction in reactions)
  210. {
  211. reaction.Update(animal, AIparams);
  212. if (reaction.Reacted)
  213. {
  214. aiNewDir += reaction.Reaction;
  215. aiNumSeen++;
  216. }
  217. }
  218. }
  219. }
  220. }
  221. /// <summary>
  222. /// This function clamps turn rates to no more than maxTurnRadians
  223. /// </summary>
  224. /// <param name="oldDir">current movement direction</param>
  225. /// <param name="newDir">desired movement direction</param>
  226. /// <param name="maxTurnRadians">max turn in radians</param>
  227. /// <returns></returns>
  228. private static Vector2 ChangeDirection(
  229. Vector2 oldDir, Vector2 newDir, float maxTurnRadians)
  230. {
  231. float oldAngle = (float)Math.Atan2(oldDir.Y, oldDir.X);
  232. float desiredAngle = (float)Math.Atan2(newDir.Y, newDir.X);
  233. float newAngle = MathHelper.Clamp(desiredAngle, WrapAngle(
  234. oldAngle - maxTurnRadians), WrapAngle(oldAngle + maxTurnRadians));
  235. return new Vector2((float)Math.Cos(newAngle), (float)Math.Sin(newAngle));
  236. }
  237. /// <summary>
  238. /// clamps the angle in radians between -Pi and Pi.
  239. /// </summary>
  240. /// <param name="radians"></param>
  241. /// <returns></returns>
  242. private static float WrapAngle(float radians)
  243. {
  244. while (radians < -MathHelper.Pi)
  245. {
  246. radians += MathHelper.TwoPi;
  247. }
  248. while (radians > MathHelper.Pi)
  249. {
  250. radians -= MathHelper.TwoPi;
  251. }
  252. return radians;
  253. }
  254. }
  255. }