GameplayScreen.cs 20 KB

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