GameplayScreen.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // GameplayScreen.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.Threading;
  12. using Microsoft.Xna.Framework;
  13. using Microsoft.Xna.Framework.Content;
  14. using Microsoft.Xna.Framework.GamerServices;
  15. using Microsoft.Xna.Framework.Graphics;
  16. using Microsoft.Xna.Framework.Input;
  17. using Microsoft.Xna.Framework.Net;
  18. using Microsoft.Xna.Framework.Media;
  19. #endregion
  20. namespace NetRumble
  21. {
  22. /// <summary>
  23. /// This screen implements the actual game logic.
  24. /// </summary>
  25. /// <remarks>
  26. /// This public class is somewhat similar to one of the same name in the
  27. /// GameStateManagement sample.
  28. /// </remarks>
  29. public class GameplayScreen : GameScreen, IDisposable
  30. {
  31. #region Gameplay Data
  32. /// <summary>
  33. /// The primary gameplay object.
  34. /// </summary>
  35. private World world;
  36. /// <summary>
  37. /// The ship for the local player.
  38. /// </summary>
  39. private Ship localShip;
  40. /// <summary>
  41. /// The game-winner text.
  42. /// </summary>
  43. private string winnerString = String.Empty;
  44. /// <summary>
  45. /// The position of the game-winner text.
  46. /// </summary>
  47. private Vector2 winnerStringPosition;
  48. #endregion
  49. #region Graphics Data
  50. /// <summary>
  51. /// The bloom component, applied to part of the game world.
  52. /// </summary>
  53. private BloomComponent bloomComponent;
  54. /// <summary>
  55. /// The starfield, rendering behind the game.
  56. /// </summary>
  57. private Starfield starfield;
  58. #endregion
  59. #region Networking Data
  60. /// <summary>
  61. /// The network session used in this game.
  62. /// </summary>
  63. private NetworkSession networkSession;
  64. /// <summary>
  65. /// Event handler for the session-ended event.
  66. /// </summary>
  67. EventHandler<NetworkSessionEndedEventArgs> sessionEndedHandler;
  68. /// <summary>
  69. /// Event handler for the game-ended event.
  70. /// </summary>
  71. EventHandler<GameEndedEventArgs> gameEndedHandler;
  72. /// <summary>
  73. /// Event handler for the gamer-left event.
  74. /// </summary>
  75. EventHandler<GamerLeftEventArgs> gamerLeftHandler;
  76. #endregion
  77. #region Initialization Methods
  78. /// <summary>
  79. /// Construct a new GameplayScreen object.
  80. /// </summary>
  81. /// <param name="networkSession">The network session for this game.</param>
  82. /// <param name="world">The primary gameplay object.</param>
  83. public GameplayScreen(NetworkSession networkSession, World world)
  84. {
  85. // safety-check the parameters
  86. if (networkSession == null)
  87. {
  88. throw new ArgumentNullException("networkSession");
  89. }
  90. if (world == null)
  91. {
  92. throw new ArgumentNullException("world");
  93. }
  94. // apply the parameters
  95. this.networkSession = networkSession;
  96. this.world = world;
  97. // set up the network events
  98. sessionEndedHandler = new EventHandler<NetworkSessionEndedEventArgs>(
  99. networkSession_SessionEnded);
  100. networkSession.SessionEnded += sessionEndedHandler;
  101. gameEndedHandler = new EventHandler<GameEndedEventArgs>(
  102. networkSession_GameEnded);
  103. networkSession.GameEnded += gameEndedHandler;
  104. gamerLeftHandler = new EventHandler<GamerLeftEventArgs>(
  105. networkSession_GamerLeft);
  106. networkSession.GamerLeft += gamerLeftHandler;
  107. // cache the local player's ship object
  108. if (networkSession.LocalGamers.Count > 0)
  109. {
  110. PlayerData playerData = networkSession.LocalGamers[0].Tag as PlayerData;
  111. if (playerData != null)
  112. {
  113. localShip = playerData.Ship;
  114. }
  115. }
  116. // set the transition times
  117. TransitionOnTime = TimeSpan.FromSeconds(1.0);
  118. TransitionOffTime = TimeSpan.FromSeconds(1.0);
  119. }
  120. /// <summary>
  121. /// Load graphics content for the game.
  122. /// </summary>
  123. public override void LoadContent()
  124. {
  125. // Developers Comment or uncomment the bloomComponent to run with effects or not
  126. // ***************************
  127. // Comment or uncomment from here
  128. // ***************************
  129. // create and add the bloom effect
  130. bloomComponent = new BloomComponent(ScreenManager.Game);
  131. bloomComponent.Settings = BloomSettings.PresetSettings[0];
  132. ScreenManager.Game.Components.Add(bloomComponent);
  133. bloomComponent.Initialize();
  134. bloomComponent.Visible = false; // we want to control when bloom component is drawn
  135. // ***************************
  136. // Comment or uncomment to here
  137. // ***************************
  138. // create the starfield
  139. starfield = new Starfield(Vector2.Zero, ScreenManager.GraphicsDevice,
  140. ScreenManager.Content);
  141. starfield.LoadContent();
  142. // start the background soundtrack
  143. AudioManager.PlaySoundTrack();
  144. base.LoadContent();
  145. }
  146. /// <summary>
  147. /// Release graphics data.
  148. /// </summary>
  149. public override void UnloadContent()
  150. {
  151. if (starfield != null)
  152. {
  153. starfield.UnloadContent();
  154. }
  155. base.UnloadContent();
  156. }
  157. #endregion
  158. #region Updating Methods
  159. /// <summary>
  160. /// Updates the state of the game. This method checks the GameScreen.IsActive
  161. /// property, so the game will stop updating when the pause menu is active,
  162. /// or if you tab away to a different application.
  163. /// </summary>
  164. public override void Update(GameTime gameTime, bool otherScreenHasFocus,
  165. bool coveredByOtherScreen)
  166. {
  167. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  168. // if something else has canceled our game, then exit
  169. if ((networkSession == null) || (world == null))
  170. {
  171. if (!IsExiting)
  172. {
  173. ExitScreen();
  174. }
  175. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  176. return;
  177. }
  178. // update the world
  179. if (world != null)
  180. {
  181. if (otherScreenHasFocus || coveredByOtherScreen)
  182. {
  183. world.Update((float)gameTime.ElapsedGameTime.TotalSeconds, true);
  184. }
  185. else if (world.GameExited)
  186. {
  187. if (!IsExiting)
  188. {
  189. ExitScreen();
  190. }
  191. networkSession = null;
  192. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  193. return;
  194. }
  195. else
  196. {
  197. world.Update((float)gameTime.ElapsedGameTime.TotalSeconds, false);
  198. // if the game was just won, then build the winner string
  199. if (world.GameWon && String.IsNullOrEmpty(winnerString) &&
  200. (world.WinnerIndex >= 0) &&
  201. (world.WinnerIndex < networkSession.AllGamers.Count))
  202. {
  203. winnerString =
  204. networkSession.AllGamers[world.WinnerIndex].Gamertag;
  205. winnerString +=
  206. " has won the game!\nPress A to return to the lobby.";
  207. Vector2 winnerStringSize =
  208. world.PlayerFont.MeasureString(winnerString);
  209. winnerStringPosition = new Vector2(
  210. ScreenManager.GraphicsDevice.Viewport.X +
  211. ScreenManager.GraphicsDevice.Viewport.Width / 2 -
  212. (float)Math.Floor(winnerStringSize.X / 2),
  213. ScreenManager.GraphicsDevice.Viewport.Y +
  214. ScreenManager.GraphicsDevice.Viewport.Height / 2 -
  215. (float)Math.Floor(winnerStringSize.Y / 2));
  216. }
  217. }
  218. }
  219. }
  220. /// <summary>
  221. /// Lets the game respond to player input. Unlike the Update method,
  222. /// this will only be called when the gameplay screen is active.
  223. /// </summary>
  224. public override void HandleInput(InputState input)
  225. {
  226. if (input == null)
  227. throw new ArgumentNullException("input");
  228. if (!IsExiting)
  229. {
  230. if ((world != null) && !world.GameExited)
  231. {
  232. if (input.PauseGame && !world.GameWon)
  233. {
  234. // If they pressed pause, bring up the pause menu screen.
  235. const string message = "Exit the game?";
  236. MessageBoxScreen messageBox = new MessageBoxScreen(message,
  237. false);
  238. messageBox.Accepted += ExitMessageBoxAccepted;
  239. ScreenManager.AddScreen(messageBox);
  240. }
  241. if (input.MenuSelect && world.GameWon)
  242. {
  243. world.GameExited = true;
  244. world = null;
  245. if (!IsExiting)
  246. {
  247. ExitScreen();
  248. }
  249. networkSession = null;
  250. }
  251. }
  252. }
  253. }
  254. /// <summary>
  255. /// Event handler for when the user selects "yes" on the "are you sure
  256. /// you want to exit" message box.
  257. /// </summary>
  258. private void ExitMessageBoxAccepted(object sender, EventArgs e)
  259. {
  260. world.GameExited = true;
  261. world = null;
  262. }
  263. /// <summary>
  264. /// Force the end of a network session so that a new one can be joined.
  265. /// </summary>
  266. public void EndSession()
  267. {
  268. if (networkSession != null)
  269. {
  270. networkSession.Dispose();
  271. networkSession = null;
  272. }
  273. }
  274. /// <summary>
  275. /// Exit this screen.
  276. /// </summary>
  277. public override void ExitScreen()
  278. {
  279. if (bloomComponent != null)
  280. {
  281. bloomComponent.Visible = false;
  282. ScreenManager.Game.Components.Remove(bloomComponent);
  283. bloomComponent = null;
  284. }
  285. if (!IsExiting && (networkSession != null))
  286. {
  287. networkSession.SessionEnded -= sessionEndedHandler;
  288. networkSession.GameEnded -= gameEndedHandler;
  289. networkSession.GamerLeft -= gamerLeftHandler;
  290. }
  291. MediaPlayer.Stop();
  292. base.ExitScreen();
  293. }
  294. /// <summary>
  295. /// Screen-specific update to gamer rich presence.
  296. /// </summary>
  297. public override void UpdatePresence()
  298. {
  299. if (!IsExiting && (networkSession != null))
  300. {
  301. bool isTied = (world.HighScorers.Count > 1);
  302. for (int i = 0; i < networkSession.AllGamers.Count; ++i)
  303. {
  304. NetworkGamer networkGamer = networkSession.AllGamers[i];
  305. if (networkGamer.IsLocal)
  306. {
  307. SignedInGamer signedInGamer = (networkGamer as LocalNetworkGamer).SignedInGamer;
  308. if (signedInGamer.IsSignedInToLive)
  309. {
  310. if (world.HighScorers.Contains(i))
  311. {
  312. if (isTied)
  313. signedInGamer.Presence.PresenceMode = GamerPresenceMode.ScoreIsTied;
  314. else
  315. signedInGamer.Presence.PresenceMode = GamerPresenceMode.Winning;
  316. }
  317. else
  318. {
  319. signedInGamer.Presence.PresenceMode = GamerPresenceMode.Losing;
  320. }
  321. }
  322. }
  323. }
  324. }
  325. }
  326. #endregion
  327. #region Drawing Methods
  328. /// <summary>
  329. /// Draws the gameplay screen.
  330. /// </summary>
  331. public override void Draw(GameTime gameTime)
  332. {
  333. float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
  334. if (networkSession != null)
  335. {
  336. // make sure we know what the local ship is
  337. if ((localShip == null) && (networkSession.LocalGamers.Count > 0))
  338. {
  339. PlayerData playerData = networkSession.LocalGamers[0].Tag
  340. as PlayerData;
  341. if (playerData.Ship != null)
  342. {
  343. localShip = playerData.Ship;
  344. starfield.Reset(localShip.Position);
  345. }
  346. }
  347. if (bloomComponent != null)
  348. {
  349. bloomComponent.BeginDraw();
  350. }
  351. // draw the world
  352. if ((world != null) && (localShip != null) && !IsExiting)
  353. {
  354. Vector2 center = new Vector2(
  355. localShip.Position.X + ScreenManager.GraphicsDevice.Viewport.X -
  356. ScreenManager.GraphicsDevice.Viewport.Width / 2,
  357. localShip.Position.Y + ScreenManager.GraphicsDevice.Viewport.Y -
  358. ScreenManager.GraphicsDevice.Viewport.Height / 2);
  359. starfield.Draw(center);
  360. world.Draw(elapsedTime, center);
  361. if (bloomComponent != null)
  362. {
  363. bloomComponent.Draw(gameTime);
  364. }
  365. }
  366. // draw the user-interface elements of the game (scores, etc.)
  367. DrawHud((float)gameTime.TotalGameTime.TotalSeconds);
  368. }
  369. // If the game is transitioning on or off, fade it out to black.
  370. if (ScreenState == ScreenState.TransitionOn && (TransitionPosition > 0))
  371. ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);
  372. }
  373. /// <summary>
  374. /// Draw the user interface elements of the game (scores, etc.).
  375. /// </summary>
  376. /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
  377. private void DrawHud(float totalTime)
  378. {
  379. if ((networkSession != null) && (world != null))
  380. {
  381. ScreenManager.SpriteBatch.Begin();
  382. // draw players 0 - 3 at the top of the screen
  383. Vector2 position = new Vector2(
  384. ScreenManager.GraphicsDevice.Viewport.Width * 0.2f,
  385. ScreenManager.GraphicsDevice.Viewport.Height * 0.1f);
  386. for (int i = 0; i < Math.Min(4, networkSession.AllGamers.Count); i++)
  387. {
  388. world.DrawPlayerData(totalTime, networkSession.AllGamers[i],
  389. position, ScreenManager.SpriteBatch, false);
  390. position.X += ScreenManager.GraphicsDevice.Viewport.Width * 0.2f;
  391. }
  392. // draw players 4 - 7 at the bottom of the screen
  393. position = new Vector2(
  394. ScreenManager.GraphicsDevice.Viewport.Width * 0.2f,
  395. ScreenManager.GraphicsDevice.Viewport.Height * 0.9f);
  396. for (int i = 4; i < Math.Min(8, networkSession.AllGamers.Count); i++)
  397. {
  398. world.DrawPlayerData(totalTime, networkSession.AllGamers[i],
  399. position, ScreenManager.SpriteBatch, false);
  400. position.X += ScreenManager.GraphicsDevice.Viewport.Width * 0.2f;
  401. }
  402. // draw players 8 - 11 at the left of the screen
  403. position = new Vector2(
  404. ScreenManager.GraphicsDevice.Viewport.Width * 0.13f,
  405. ScreenManager.GraphicsDevice.Viewport.Height * 0.2f);
  406. for (int i = 8; i < Math.Min(12, networkSession.AllGamers.Count); i++)
  407. {
  408. world.DrawPlayerData(totalTime, networkSession.AllGamers[i],
  409. position, ScreenManager.SpriteBatch, false);
  410. position.Y += ScreenManager.GraphicsDevice.Viewport.Height * 0.2f;
  411. }
  412. // draw players 12 - 15 at the right of the screen
  413. position = new Vector2(
  414. ScreenManager.GraphicsDevice.Viewport.Width * 0.9f,
  415. ScreenManager.GraphicsDevice.Viewport.Height * 0.2f);
  416. for (int i = 12; i < Math.Min(16, networkSession.AllGamers.Count); i++)
  417. {
  418. world.DrawPlayerData(totalTime, networkSession.AllGamers[i],
  419. position, ScreenManager.SpriteBatch, false);
  420. position.Y += ScreenManager.GraphicsDevice.Viewport.Height * 0.2f;
  421. }
  422. // if the game is over, draw the winner text
  423. if (world.GameWon && !String.IsNullOrEmpty(winnerString))
  424. {
  425. ScreenManager.SpriteBatch.DrawString(world.PlayerFont, winnerString,
  426. winnerStringPosition, Color.White, 0f, Vector2.Zero, 1.3f,
  427. SpriteEffects.None, 0f);
  428. }
  429. ScreenManager.SpriteBatch.End();
  430. }
  431. }
  432. #endregion
  433. #region Networking Event Handlers
  434. /// <summary>
  435. /// Handle the end of the game session.
  436. /// </summary>
  437. void networkSession_GameEnded(object sender, GameEndedEventArgs e)
  438. {
  439. if ((world != null) && !world.GameWon && !world.GameExited)
  440. {
  441. world.GameExited = true;
  442. }
  443. if (!IsExiting && ((world == null) || world.GameExited))
  444. {
  445. world = null;
  446. ExitScreen();
  447. networkSession = null;
  448. }
  449. }
  450. /// <summary>
  451. /// Handle the end of the session.
  452. /// </summary>
  453. void networkSession_SessionEnded(object sender, NetworkSessionEndedEventArgs e)
  454. {
  455. if ((world != null) && !world.GameExited)
  456. {
  457. world.GameExited = true;
  458. world = null;
  459. }
  460. if (!IsExiting)
  461. {
  462. ExitScreen();
  463. }
  464. networkSession = null;
  465. }
  466. /// <summary>
  467. /// Handle a player leaving the game.
  468. /// </summary>
  469. void networkSession_GamerLeft(object sender, GamerLeftEventArgs e)
  470. {
  471. PlayerData playerData = e.Gamer.Tag as PlayerData;
  472. if ((playerData != null) && (playerData.Ship != null))
  473. {
  474. playerData.Ship.Die(null, true);
  475. }
  476. }
  477. #endregion
  478. #region IDisposable Implementation
  479. /// <summary>
  480. /// Finalizes the GameplayScreen object, calls Dispose(false)
  481. /// </summary>
  482. ~GameplayScreen()
  483. {
  484. Dispose(false);
  485. }
  486. /// <summary>
  487. /// Disposes the GameplayScreen object.
  488. /// </summary>
  489. public void Dispose()
  490. {
  491. Dispose(true);
  492. GC.SuppressFinalize(this);
  493. }
  494. /// <summary>
  495. /// Disposes this object.
  496. /// </summary>
  497. /// <param name="disposing">
  498. /// True if this method was called as part of the Dispose method.
  499. /// </param>
  500. protected virtual void Dispose(bool disposing)
  501. {
  502. if (disposing)
  503. {
  504. lock (this)
  505. {
  506. if (bloomComponent != null)
  507. {
  508. bloomComponent.Dispose();
  509. bloomComponent = null;
  510. }
  511. if (starfield != null)
  512. {
  513. starfield.Dispose();
  514. starfield = null;
  515. }
  516. }
  517. }
  518. }
  519. #endregion
  520. }
  521. }