Starfield.cs 12 KB

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