Starfield.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. //-----------------------------------------------------------------------------
  2. // Starfield.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using Microsoft.Xna.Framework;
  9. using Microsoft.Xna.Framework.Graphics;
  10. using Microsoft.Xna.Framework.Content;
  11. namespace NetRumble
  12. {
  13. /// <summary>
  14. /// The starfield that renders behind the game, including a parallax effect.
  15. /// </summary>
  16. public class Starfield : IDisposable
  17. {
  18. /// <summary>
  19. /// The number of stars in the starfield.
  20. /// </summary>
  21. const int numberOfStars = 256;
  22. /// <summary>
  23. /// The number of layers in the starfield.
  24. /// </summary>
  25. const int numberOfLayers = 8;
  26. /// <summary>
  27. /// The colors for each layer of stars.
  28. /// </summary>
  29. static readonly Color[] layerColors = new Color[numberOfLayers]
  30. {
  31. new Color(255, 255, 255, 255),
  32. new Color(255, 255, 255, 216),
  33. new Color(255, 255, 255, 192),
  34. new Color(255, 255, 255, 160),
  35. new Color(255, 255, 255, 128),
  36. new Color(255, 255, 255, 96),
  37. new Color(255, 255, 255, 64),
  38. new Color(255, 255, 255, 32)
  39. };
  40. /// <summary>
  41. /// The movement factor for each layer of stars, used in the parallax effect.
  42. /// </summary>
  43. static readonly float[] movementFactors = new float[numberOfLayers]
  44. {
  45. 0.9f, 0.8f, 0.7f, 0.6f, 0.5f, 0.4f, 0.3f, 0.2f
  46. };
  47. /// <summary>
  48. /// The maximum amount of movement allowed per update.
  49. /// </summary>
  50. /// <remarks>
  51. /// Any per-update movement values that exceed this will trigger a
  52. /// starfield reset.
  53. /// </remarks>
  54. const float maximumMovementPerUpdate = 128f;
  55. /// <summary>
  56. /// The background color of the starfield.
  57. /// </summary>
  58. static readonly Color backgroundColor = new Color(0, 0, 32);
  59. /// <summary>
  60. /// The size of each star, in pixels.
  61. /// </summary>
  62. const int starSize = 2;
  63. /// <summary>
  64. /// The last position, used for the parallax effect.
  65. /// </summary>
  66. private Vector2 lastPosition;
  67. /// <summary>
  68. /// The current position, used for the parallax effect.
  69. /// </summary>
  70. private Vector2 position;
  71. /// <summary>
  72. /// The stars in the starfield.
  73. /// </summary>
  74. private Vector2[] stars;
  75. /// <summary>
  76. /// The graphics device used to render the starfield.
  77. /// </summary>
  78. private GraphicsDevice graphicsDevice;
  79. /// <summary>
  80. /// The content manager used to manage the textures in the starfield.
  81. /// </summary>
  82. private ContentManager contentManager;
  83. /// <summary>
  84. /// The SpriteBatch used to render the starfield.
  85. /// </summary>
  86. private SpriteBatch spriteBatch;
  87. /// <summary>
  88. /// The texture used for each star, typically one white pixel.
  89. /// </summary>
  90. private Texture2D starTexture;
  91. /// <summary>
  92. /// The cloud texture that appears behind the stars.
  93. /// </summary>
  94. private Texture2D cloudTexture;
  95. /// <summary>
  96. /// The effect used to draw the clouds.
  97. /// </summary>
  98. private Effect cloudEffect;
  99. /// <summary>
  100. /// The parameter on the cloud effect that receives the current position
  101. /// </summary>
  102. private EffectParameter cloudEffectPosition;
  103. /// <summary>
  104. /// Create a new Starfield object.
  105. /// </summary>
  106. /// <param name="position"></param>
  107. /// <param name="graphicsDevice">The graphics device used to render.</param>
  108. /// <param name="contentManager">The content manager for this object.</param>
  109. public Starfield(Vector2 position, GraphicsDevice graphicsDevice,
  110. ContentManager contentManager)
  111. {
  112. // safety-check the parameters, as they must be valid
  113. if (graphicsDevice == null)
  114. {
  115. throw new ArgumentNullException("graphicsDevice");
  116. }
  117. if (contentManager == null)
  118. {
  119. throw new ArgumentNullException("contentManager");
  120. }
  121. // assign the parameters
  122. this.graphicsDevice = graphicsDevice;
  123. this.contentManager = contentManager;
  124. // initialize the stars
  125. stars = new Vector2[numberOfStars];
  126. Reset(position);
  127. }
  128. /// <summary>
  129. /// Load graphics data from the system.
  130. /// </summary>
  131. public void LoadContent()
  132. {
  133. // load the cloud texture
  134. cloudTexture = contentManager.Load<Texture2D>("Textures/clouds");
  135. // load the cloud effect
  136. cloudEffect = contentManager.Load<Effect>("Effects/Clouds");
  137. //cloudEffect = new CloudsEffect(graphicsDevice);
  138. cloudEffectPosition = cloudEffect.Parameters["Position"];
  139. // create the star texture
  140. starTexture = new Texture2D(graphicsDevice, 1, 1, false, SurfaceFormat.Color);
  141. starTexture.SetData<Color>(new Color[] { Color.White });
  142. // create the SpriteBatch object
  143. spriteBatch = new SpriteBatch(graphicsDevice);
  144. }
  145. /// <summary>
  146. /// Release graphics data.
  147. /// </summary>
  148. public void UnloadContent()
  149. {
  150. cloudTexture = null;
  151. cloudEffect = null;
  152. cloudEffectPosition = null;
  153. if (starTexture != null)
  154. {
  155. starTexture.Dispose();
  156. starTexture = null;
  157. }
  158. if (spriteBatch != null)
  159. {
  160. spriteBatch.Dispose();
  161. spriteBatch = null;
  162. }
  163. }
  164. /// <summary>
  165. /// Reset the stars and the parallax effect.
  166. /// </summary>
  167. /// <param name="position">The new origin point for the parallax effect.</param>
  168. public void Reset(Vector2 position)
  169. {
  170. // recreate the stars
  171. int viewportWidth = graphicsDevice.Viewport.Width;
  172. int viewportHeight = graphicsDevice.Viewport.Height;
  173. for (int i = 0; i < stars.Length; ++i)
  174. {
  175. stars[i] = new Vector2(RandomMath.Random.Next(0, viewportWidth),
  176. RandomMath.Random.Next(0, viewportHeight));
  177. }
  178. // reset the position
  179. this.lastPosition = this.position = position;
  180. }
  181. /// <summary>
  182. /// Update and draw the starfield.
  183. /// </summary>
  184. /// <remarks>
  185. /// This function updates and draws the starfield,
  186. /// so that the per-star loop is only run once per frame.
  187. /// </remarks>
  188. /// <param name="position">The new position for the parallax effect.</param>
  189. public void Draw(Vector2 position)
  190. {
  191. // update the current position
  192. this.lastPosition = this.position;
  193. this.position = position;
  194. // determine the movement vector of the stars
  195. // -- for the purposes of the parallax effect,
  196. // this is the opposite direction as the position movement.
  197. Vector2 movement = -1.0f * (position - lastPosition);
  198. // create a rectangle representing the screen dimensions of the starfield
  199. Rectangle starfieldRectangle = new Rectangle(0, 0,
  200. graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height);
  201. // draw a background color for the starfield
  202. spriteBatch.Begin();
  203. spriteBatch.Draw(starTexture, starfieldRectangle, backgroundColor);
  204. spriteBatch.End();
  205. // draw the cloud texture
  206. cloudEffectPosition.SetValue(this.position);
  207. spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied,
  208. null, null, null, cloudEffect);
  209. spriteBatch.Draw(cloudTexture, starfieldRectangle, null, Color.White, 0.0f,
  210. Vector2.Zero, SpriteEffects.None, 1.0f);
  211. spriteBatch.End();
  212. // if we've moved too far, then reset, as the stars will be moving too fast
  213. if (movement.Length() > maximumMovementPerUpdate)
  214. {
  215. Reset(position);
  216. return;
  217. }
  218. // draw all of the stars
  219. spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
  220. for (int i = 0; i < stars.Length; i++)
  221. {
  222. // move the star based on the depth
  223. int depth = i % movementFactors.Length;
  224. stars[i] += movement * movementFactors[depth];
  225. // wrap the stars around
  226. if (stars[i].X < starfieldRectangle.X)
  227. {
  228. stars[i].X = starfieldRectangle.X + starfieldRectangle.Width;
  229. stars[i].Y = starfieldRectangle.Y +
  230. RandomMath.Random.Next(starfieldRectangle.Height);
  231. }
  232. if (stars[i].X > (starfieldRectangle.X + starfieldRectangle.Width))
  233. {
  234. stars[i].X = starfieldRectangle.X;
  235. stars[i].Y = starfieldRectangle.Y +
  236. RandomMath.Random.Next(starfieldRectangle.Height);
  237. }
  238. if (stars[i].Y < starfieldRectangle.Y)
  239. {
  240. stars[i].X = starfieldRectangle.X +
  241. RandomMath.Random.Next(starfieldRectangle.Width);
  242. stars[i].Y = starfieldRectangle.Y + starfieldRectangle.Height;
  243. }
  244. if (stars[i].Y >
  245. (starfieldRectangle.Y + graphicsDevice.Viewport.Height))
  246. {
  247. stars[i].X = starfieldRectangle.X +
  248. RandomMath.Random.Next(starfieldRectangle.Width);
  249. stars[i].Y = starfieldRectangle.Y;
  250. }
  251. // draw the star
  252. spriteBatch.Draw(starTexture,
  253. new Rectangle((int)stars[i].X, (int)stars[i].Y, starSize, starSize),
  254. null, layerColors[depth]);
  255. }
  256. spriteBatch.End();
  257. }
  258. /// <summary>
  259. /// Finalizes the Starfield object, calls Dispose(false)
  260. /// </summary>
  261. ~Starfield()
  262. {
  263. Dispose(false);
  264. }
  265. /// <summary>
  266. /// Disposes the Starfield object.
  267. /// </summary>
  268. public void Dispose()
  269. {
  270. Dispose(true);
  271. GC.SuppressFinalize(this);
  272. }
  273. /// <summary>
  274. /// Disposes this object.
  275. /// </summary>
  276. /// <param name="disposing">
  277. /// True if this method was called as part of the Dispose method.
  278. /// </param>
  279. protected virtual void Dispose(bool disposing)
  280. {
  281. if (disposing)
  282. {
  283. lock (this)
  284. {
  285. if (starTexture != null)
  286. {
  287. starTexture.Dispose();
  288. starTexture = null;
  289. }
  290. if (spriteBatch != null)
  291. {
  292. spriteBatch.Dispose();
  293. spriteBatch = null;
  294. }
  295. }
  296. }
  297. }
  298. }
  299. }