DemoGame.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // DemoGame.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.Linq;
  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. using Microsoft.Xna.Framework.Input.Touch;
  20. using Microsoft.Xna.Framework.Media;
  21. #endregion
  22. namespace XnaGraphicsDemo
  23. {
  24. /// <summary>
  25. /// The main game class.
  26. /// </summary>
  27. public class DemoGame : Microsoft.Xna.Framework.Game
  28. {
  29. // Constants.
  30. const float TransitionSpeed = 1.5f;
  31. const float ZoomyTextLifespan = 0.75f;
  32. // Properties.
  33. public GraphicsDeviceManager Graphics { get; private set; }
  34. public SpriteBatch SpriteBatch { get; private set; }
  35. public SpriteFont Font { get; private set; }
  36. public SpriteFont BigFont { get; private set; }
  37. public Texture2D BlankTexture { get; private set; }
  38. public Matrix ScaleMatrix { get; private set; }
  39. // Fields.
  40. List<MenuComponent> menuComponents = new List<MenuComponent>();
  41. GameTime currentGameTime;
  42. // Transition effects provide swooshy crossfades when moving from one screen to another.
  43. float transitionTimer = float.MaxValue;
  44. int transitionMode;
  45. RenderTarget2D transitionRenderTarget;
  46. // Zoomy text provides visual feedback when selecting menu items.
  47. // This is implemented by the main game, rather than any individual menu
  48. // screen, because the zoomy effect from selecting a menu item needs to
  49. // display across the transition while that menu makes way for a new one.
  50. class ZoomyText
  51. {
  52. public string Text;
  53. public Vector2 Position;
  54. public float Age;
  55. }
  56. static List<ZoomyText> zoomyTexts = new List<ZoomyText>();
  57. /// <summary>
  58. /// Constructor.
  59. /// </summary>
  60. public DemoGame()
  61. {
  62. Content.RootDirectory = "Content";
  63. Graphics = new GraphicsDeviceManager(this);
  64. Graphics.PreferredBackBufferWidth = 480;
  65. Graphics.PreferredBackBufferHeight = 800;
  66. #if WINDOWS_PHONE
  67. Graphics.IsFullScreen = true;
  68. #else
  69. IsMouseVisible = true;
  70. #endif
  71. TargetElapsedTime = TimeSpan.FromSeconds(1 / 30.0);
  72. // Create all the different menu screens.
  73. menuComponents.Add(new TitleMenu(this));
  74. menuComponents.Add(new BasicDemo(this));
  75. menuComponents.Add(new DualDemo(this));
  76. menuComponents.Add(new AlphaDemo(this));
  77. menuComponents.Add(new SkinnedDemo(this));
  78. menuComponents.Add(new EnvmapDemo(this));
  79. menuComponents.Add(new ParticleDemo(this));
  80. // Set all the menu screens except the first to hidden and inactive.
  81. foreach (MenuComponent component in menuComponents)
  82. {
  83. component.Enabled = component.Visible = false;
  84. Components.Add(component);
  85. }
  86. // Make the title menu active and visible.
  87. menuComponents[0].Enabled = menuComponents[0].Visible = true;
  88. }
  89. /// <summary>
  90. /// Changes which menu screen is currently active.
  91. /// </summary>
  92. public void SetActiveMenu(int index)
  93. {
  94. // Trigger the transition effect.
  95. for (int i = 0; i < menuComponents.Count; i++)
  96. {
  97. if (menuComponents[i].Visible)
  98. {
  99. BeginTransition(i, index);
  100. break;
  101. }
  102. }
  103. // Mark the previous menu as inactive, and the new one as active.
  104. for (int i = 0; i < menuComponents.Count; i++)
  105. {
  106. menuComponents[i].Enabled = menuComponents[i].Visible = (i == index);
  107. menuComponents[i].Reset();
  108. }
  109. }
  110. /// <summary>
  111. /// Loads content and creates graphics resources.
  112. /// </summary>
  113. protected override void LoadContent()
  114. {
  115. SpriteBatch = new SpriteBatch(GraphicsDevice);
  116. Font = Content.Load<SpriteFont>("font");
  117. BigFont = Content.Load<SpriteFont>("BigFont");
  118. BlankTexture = new Texture2D(GraphicsDevice, 1, 1);
  119. BlankTexture.SetData(new Color[] { Color.White });
  120. transitionRenderTarget = new RenderTarget2D(GraphicsDevice, 480, 800, false, SurfaceFormat.Color, DepthFormat.Depth24, 0, 0);
  121. }
  122. /// <summary>
  123. /// Updates the transition effect and zoomy text animations.
  124. /// </summary>
  125. protected override void Update(GameTime gameTime)
  126. {
  127. currentGameTime = gameTime;
  128. UpdateZoomyText(gameTime);
  129. if (transitionTimer < float.MaxValue)
  130. transitionTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
  131. // This updates game components, including the currently active menu screen.
  132. base.Update(gameTime);
  133. }
  134. /// <summary>
  135. /// Draws the game.
  136. /// </summary>
  137. protected override void Draw(GameTime gameTime)
  138. {
  139. ScaleMatrix = Matrix.CreateScale(Graphics.PreferredBackBufferWidth / 480f, Graphics.PreferredBackBufferHeight / 800f, 1);
  140. // This draws game components, including the currently active menu screen.
  141. base.Draw(gameTime);
  142. DrawTransitionEffect();
  143. DrawZoomyText();
  144. }
  145. /// <summary>
  146. /// Begins a transition effect, capturing a copy of the current screen into the transitionRenderTarget.
  147. /// </summary>
  148. void BeginTransition(int oldMenuIndex, int newMenuIndex)
  149. {
  150. ScaleMatrix = Matrix.Identity;
  151. GraphicsDevice.SetRenderTarget(transitionRenderTarget);
  152. // Draw the old menu screen into the rendertarget.
  153. menuComponents[oldMenuIndex].Draw(currentGameTime);
  154. // Force the rendertarget alpha channel to fully opaque.
  155. SpriteBatch.Begin(0, BlendState.Additive);
  156. SpriteBatch.Draw(BlankTexture, new Rectangle(0, 0, 480, 800), new Color(0, 0, 0, 255));
  157. SpriteBatch.End();
  158. GraphicsDevice.SetRenderTarget(null);
  159. // Initialize the transition state.
  160. transitionTimer = (float)TargetElapsedTime.TotalSeconds;
  161. transitionMode = newMenuIndex;
  162. }
  163. /// <summary>
  164. /// Draws the transition effect, displaying various animating pieces of the rendertarget
  165. /// which contains the previous scene image over the top of the new scene. There are
  166. /// various different effects which animate these pieces in different ways.
  167. /// </summary>
  168. void DrawTransitionEffect()
  169. {
  170. if (transitionTimer >= TransitionSpeed)
  171. return;
  172. SpriteBatch.Begin();
  173. float mu = transitionTimer / TransitionSpeed;
  174. float alpha = 1 - mu;
  175. switch (transitionMode)
  176. {
  177. case 1:
  178. // BasicEffect
  179. DrawOpenCurtainsTransition(alpha);
  180. break;
  181. case 2:
  182. case 5:
  183. // DualTexture
  184. // EnvironmentMap
  185. DrawSpinningSquaresTransition(mu, alpha);
  186. break;
  187. case 3:
  188. case 4:
  189. // AlphaTest and Skinning
  190. DrawChequeredAppearTransition(mu);
  191. break;
  192. case 6:
  193. // Particles
  194. DrawFallingLinesTransition(mu);
  195. break;
  196. default:
  197. // Returning to menu.
  198. DrawShrinkAndSpinTransition(mu, alpha);
  199. break;
  200. }
  201. SpriteBatch.End();
  202. }
  203. /// <summary>
  204. /// Transition effect where the screen splits in half, opening down the middle.
  205. /// </summary>
  206. void DrawOpenCurtainsTransition(float alpha)
  207. {
  208. int w = (int)(240 * alpha * alpha);
  209. SpriteBatch.Draw(transitionRenderTarget, new Rectangle(0, 0, w, 800), new Rectangle(0, 0, 240, 800), Color.White * alpha);
  210. SpriteBatch.Draw(transitionRenderTarget, new Rectangle(480 - w, 0, w, 800), new Rectangle(240, 0, 240, 800), Color.White * alpha);
  211. }
  212. /// <summary>
  213. /// Transition effect where the screen splits into pieces, each spinning off in a different direction.
  214. /// </summary>
  215. void DrawSpinningSquaresTransition(float mu, float alpha)
  216. {
  217. Random random = new Random(23);
  218. for (int x = 0; x < 4; x++)
  219. {
  220. for (int y = 0; y < 8; y++)
  221. {
  222. Rectangle rect = new Rectangle(480 * x / 4, 800 * y / 8, 480 / 4, 800 / 8);
  223. Vector2 origin = new Vector2(rect.Width, rect.Height) / 2;
  224. float rotation = (float)(random.NextDouble() - 0.5) * mu * mu * 2;
  225. float scale = 1 + (float)(random.NextDouble() - 0.5f) * mu * mu;
  226. Vector2 pos = new Vector2(rect.Center.X, rect.Center.Y);
  227. pos.X += (float)(random.NextDouble() - 0.5) * mu * mu * 400;
  228. pos.Y += (float)(random.NextDouble() - 0.5) * mu * mu * 400;
  229. SpriteBatch.Draw(transitionRenderTarget, pos, rect, Color.White * alpha, rotation, origin, scale, 0, 0);
  230. }
  231. }
  232. }
  233. /// <summary>
  234. /// Transition effect where each square of the image appears at a different time.
  235. /// </summary>
  236. void DrawChequeredAppearTransition(float mu)
  237. {
  238. Random random = new Random(23);
  239. for (int x = 0; x < 8; x++)
  240. {
  241. for (int y = 0; y < 16; y++)
  242. {
  243. Rectangle rect = new Rectangle(480 * x / 8, 800 * y / 16, 480 / 8, 800 / 16);
  244. if (random.NextDouble() > mu * mu)
  245. SpriteBatch.Draw(transitionRenderTarget, rect, rect, Color.White);
  246. }
  247. }
  248. // The zoomy text effect doesn't look so good with this
  249. // particular transition effect, so we temporarily disable it.
  250. zoomyTexts.Clear();
  251. }
  252. /// <summary>
  253. /// Transition effect where the image dissolves into a sequence of vertically falling lines.
  254. /// </summary>
  255. void DrawFallingLinesTransition(float mu)
  256. {
  257. Random random = new Random(23);
  258. const int segments = 60;
  259. for (int x = 0; x < segments; x++)
  260. {
  261. Rectangle rect = new Rectangle(480 * x / segments, 0, 480 / segments, 800);
  262. Vector2 pos = new Vector2(rect.X, 0);
  263. pos.Y += 800 * (float)Math.Pow(mu, random.NextDouble() * 10);
  264. SpriteBatch.Draw(transitionRenderTarget, pos, rect, Color.White);
  265. }
  266. }
  267. /// <summary>
  268. /// Transition effect where the image spins off toward the bottom left of the screen.
  269. /// </summary>
  270. void DrawShrinkAndSpinTransition(float mu, float alpha)
  271. {
  272. Vector2 origin = new Vector2(240, 400);
  273. Vector2 translate = (new Vector2(32, 800 - 32) - origin) * mu * mu;
  274. float rotation = mu * mu * -4;
  275. float scale = alpha * alpha;
  276. Color tint = Color.White * (float)Math.Sqrt(alpha);
  277. SpriteBatch.Draw(transitionRenderTarget, origin + translate, null, tint, rotation, origin, scale, 0, 0);
  278. }
  279. /// <summary>
  280. /// Creates a new zoomy text menu item selection effect.
  281. /// </summary>
  282. public static void SpawnZoomyText(string text, Vector2 position)
  283. {
  284. zoomyTexts.Add(new ZoomyText { Text = text, Position = position });
  285. }
  286. /// <summary>
  287. /// Updates the zoomy text animations.
  288. /// </summary>
  289. static void UpdateZoomyText(GameTime gameTime)
  290. {
  291. int i = 0;
  292. while (i < zoomyTexts.Count)
  293. {
  294. zoomyTexts[i].Age += (float)gameTime.ElapsedGameTime.TotalSeconds;
  295. if (zoomyTexts[i].Age >= ZoomyTextLifespan)
  296. zoomyTexts.RemoveAt(i);
  297. else
  298. i++;
  299. }
  300. }
  301. /// <summary>
  302. /// Draws the zoomy text animations.
  303. /// </summary>
  304. void DrawZoomyText()
  305. {
  306. if (zoomyTexts.Count <= 0)
  307. return;
  308. SpriteBatch.Begin(0, null, null, null, null, null, ScaleMatrix);
  309. foreach (ZoomyText zoomyText in zoomyTexts)
  310. {
  311. Vector2 pos = zoomyText.Position + Font.MeasureString(zoomyText.Text) / 2;
  312. float age = zoomyText.Age / ZoomyTextLifespan;
  313. float sqrtAge = (float)Math.Sqrt(age);
  314. float scale = 0.333f + sqrtAge * 2f;
  315. float alpha = 1 - age;
  316. SpriteFont font = BigFont;
  317. // Our BigFont only contains characters a-z, so if the text
  318. // contains any numbers, we have to use the other font instead.
  319. foreach (char ch in zoomyText.Text)
  320. {
  321. if (char.IsDigit(ch))
  322. {
  323. font = Font;
  324. scale *= 2;
  325. break;
  326. }
  327. }
  328. Vector2 origin = font.MeasureString(zoomyText.Text) / 2;
  329. SpriteBatch.DrawString(font, zoomyText.Text, pos, Color.Lerp(new Color(64, 64, 255), Color.White, sqrtAge) * alpha, 0, origin, scale, 0, 0);
  330. }
  331. SpriteBatch.End();
  332. }
  333. }
  334. }