Bird.cs 11 KB

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