GameplayScreen.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  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. // create and add the bloom effect
  115. /* TODO bloomComponent = new BloomComponent(ScreenManager.Game);
  116. bloomComponent.Settings = BloomSettings.PresetSettings[0];
  117. ScreenManager.Game.Components.Add(bloomComponent);
  118. bloomComponent.Initialize();
  119. bloomComponent.Visible = false; // we want to control when bloom component is drawn*/
  120. // create the starfield
  121. starfield = new Starfield(Vector2.Zero, ScreenManager.GraphicsDevice,
  122. ScreenManager.Content);
  123. starfield.LoadContent();
  124. // start the background soundtrack
  125. AudioManager.PlaySoundTrack();
  126. base.LoadContent();
  127. }
  128. /// <summary>
  129. /// Release graphics data.
  130. /// </summary>
  131. public override void UnloadContent()
  132. {
  133. if (starfield != null)
  134. {
  135. starfield.UnloadContent();
  136. }
  137. base.UnloadContent();
  138. }
  139. /// <summary>
  140. /// Updates the state of the game. This method checks the GameScreen.IsActive
  141. /// property, so the game will stop updating when the pause menu is active,
  142. /// or if you tab away to a different application.
  143. /// </summary>
  144. public override void Update(GameTime gameTime, bool otherScreenHasFocus,
  145. bool coveredByOtherScreen)
  146. {
  147. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  148. // if something else has canceled our game, then exit
  149. if ((networkSession == null) || (world == null))
  150. {
  151. if (!IsExiting)
  152. {
  153. ExitScreen();
  154. }
  155. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  156. return;
  157. }
  158. // update the world
  159. if (world != null)
  160. {
  161. if (otherScreenHasFocus || coveredByOtherScreen)
  162. {
  163. world.Update((float)gameTime.ElapsedGameTime.TotalSeconds, true);
  164. }
  165. else if (world.GameExited)
  166. {
  167. if (!IsExiting)
  168. {
  169. ExitScreen();
  170. }
  171. networkSession = null;
  172. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  173. return;
  174. }
  175. else
  176. {
  177. world.Update((float)gameTime.ElapsedGameTime.TotalSeconds, false);
  178. // if the game was just won, then build the winner string
  179. if (world.GameWon && String.IsNullOrEmpty(winnerString) &&
  180. (world.WinnerIndex >= 0) &&
  181. (world.WinnerIndex < networkSession.AllGamers.Count))
  182. {
  183. winnerString =
  184. networkSession.AllGamers[world.WinnerIndex].Gamertag;
  185. winnerString +=
  186. " has won the game!\nPress A to return to the lobby.";
  187. Vector2 winnerStringSize =
  188. world.PlayerFont.MeasureString(winnerString);
  189. winnerStringPosition = new Vector2(
  190. ScreenManager.BASE_BUFFER_WIDTH / 2 -
  191. (float)Math.Floor(winnerStringSize.X / 2),
  192. ScreenManager.BASE_BUFFER_HEIGHT / 2 -
  193. (float)Math.Floor(winnerStringSize.Y / 2));
  194. }
  195. }
  196. }
  197. }
  198. /// <summary>
  199. /// Lets the game respond to player input. Unlike the Update method,
  200. /// this will only be called when the gameplay screen is active.
  201. /// </summary>
  202. public override void HandleInput(InputState input)
  203. {
  204. if (input == null)
  205. throw new ArgumentNullException("input");
  206. if (!IsExiting)
  207. {
  208. if ((world != null) && !world.GameExited)
  209. {
  210. if (input.PauseGame && !world.GameWon)
  211. {
  212. // If they pressed pause, bring up the pause menu screen.
  213. const string message = "Exit the game?";
  214. MessageBoxScreen messageBox = new MessageBoxScreen(message,
  215. false);
  216. messageBox.Accepted += ExitMessageBoxAccepted;
  217. ScreenManager.AddScreen(messageBox);
  218. }
  219. if (input.MenuSelect && world.GameWon)
  220. {
  221. world.GameExited = true;
  222. world = null;
  223. if (!IsExiting)
  224. {
  225. ExitScreen();
  226. }
  227. networkSession = null;
  228. }
  229. }
  230. }
  231. }
  232. /// <summary>
  233. /// Event handler for when the user selects "yes" on the "are you sure
  234. /// you want to exit" message box.
  235. /// </summary>
  236. private void ExitMessageBoxAccepted(object sender, EventArgs e)
  237. {
  238. world.GameExited = true;
  239. world = null;
  240. }
  241. /// <summary>
  242. /// Force the end of a network session so that a new one can be joined.
  243. /// </summary>
  244. public void EndSession()
  245. {
  246. if (networkSession != null)
  247. {
  248. networkSession.Dispose();
  249. networkSession = null;
  250. }
  251. }
  252. /// <summary>
  253. /// Exit this screen.
  254. /// </summary>
  255. public override void ExitScreen()
  256. {
  257. if (bloomComponent != null)
  258. {
  259. bloomComponent.Visible = false;
  260. ScreenManager.Game.Components.Remove(bloomComponent);
  261. bloomComponent = null;
  262. }
  263. if (!IsExiting && (networkSession != null))
  264. {
  265. networkSession.SessionEnded -= sessionEndedHandler;
  266. networkSession.GameEnded -= gameEndedHandler;
  267. networkSession.GamerLeft -= gamerLeftHandler;
  268. }
  269. MediaPlayer.Stop();
  270. base.ExitScreen();
  271. }
  272. /// <summary>
  273. /// Screen-specific update to gamer rich presence.
  274. /// </summary>
  275. public override void UpdatePresence()
  276. {
  277. if (!IsExiting && (networkSession != null))
  278. {
  279. bool isTied = (world.HighScorers.Count > 1);
  280. for (int i = 0; i < networkSession.AllGamers.Count; ++i)
  281. {
  282. NetworkGamer networkGamer = networkSession.AllGamers[i];
  283. if (networkGamer.IsLocal)
  284. {
  285. SignedInGamer signedInGamer = (networkGamer as LocalNetworkGamer).SignedInGamer;
  286. if (signedInGamer.IsSignedInToLive)
  287. {
  288. if (world.HighScorers.Contains(i))
  289. {
  290. if (isTied)
  291. signedInGamer.Presence.PresenceMode = GamerPresenceMode.ScoreIsTied;
  292. else
  293. signedInGamer.Presence.PresenceMode = GamerPresenceMode.Winning;
  294. }
  295. else
  296. {
  297. signedInGamer.Presence.PresenceMode = GamerPresenceMode.Losing;
  298. }
  299. }
  300. }
  301. }
  302. }
  303. }
  304. /// <summary>
  305. /// Draws the gameplay screen.
  306. /// </summary>
  307. public override void Draw(GameTime gameTime)
  308. {
  309. float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
  310. if (networkSession != null)
  311. {
  312. // make sure we know what the local ship is
  313. if ((localShip == null) && (networkSession.LocalGamers.Count > 0))
  314. {
  315. PlayerData playerData = networkSession.LocalGamers[0].Tag
  316. as PlayerData;
  317. if (playerData.Ship != null)
  318. {
  319. localShip = playerData.Ship;
  320. starfield.Reset(localShip.Position);
  321. }
  322. }
  323. if (bloomComponent != null)
  324. {
  325. bloomComponent.BeginDraw();
  326. }
  327. // draw the world
  328. if ((world != null) && (localShip != null) && !IsExiting)
  329. {
  330. Vector2 center = new Vector2(
  331. localShip.Position.X + ScreenManager.BASE_BUFFER_WIDTH / 2,
  332. localShip.Position.Y + ScreenManager.BASE_BUFFER_HEIGHT / 2);
  333. starfield.Draw(center);
  334. world.Draw(elapsedTime, center);
  335. if (bloomComponent != null)
  336. {
  337. bloomComponent.Draw(gameTime);
  338. }
  339. }
  340. // draw the user-interface elements of the game (scores, etc.)
  341. DrawHud((float)gameTime.TotalGameTime.TotalSeconds);
  342. }
  343. // If the game is transitioning on or off, fade it out to black.
  344. if (ScreenState == ScreenState.TransitionOn && (TransitionPosition > 0))
  345. ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);
  346. }
  347. /// <summary>
  348. /// Draw the user interface elements of the game (scores, etc.).
  349. /// </summary>
  350. /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
  351. private void DrawHud(float totalTime)
  352. {
  353. if ((networkSession != null) && (world != null))
  354. {
  355. ScreenManager.SpriteBatch.Begin();
  356. // draw players 0 - 3 at the top of the screen
  357. Vector2 position = new Vector2(
  358. ScreenManager.BASE_BUFFER_WIDTH * 0.2f,
  359. ScreenManager.BASE_BUFFER_HEIGHT * 0.1f);
  360. for (int i = 0; i < Math.Min(4, networkSession.AllGamers.Count); i++)
  361. {
  362. world.DrawPlayerData(totalTime, networkSession.AllGamers[i],
  363. position, ScreenManager.SpriteBatch, false);
  364. position.X += ScreenManager.BASE_BUFFER_WIDTH * 0.2f;
  365. }
  366. // draw players 4 - 7 at the bottom of the screen
  367. position = new Vector2(
  368. ScreenManager.BASE_BUFFER_WIDTH * 0.2f,
  369. ScreenManager.BASE_BUFFER_HEIGHT * 0.9f);
  370. for (int i = 4; i < Math.Min(8, networkSession.AllGamers.Count); i++)
  371. {
  372. world.DrawPlayerData(totalTime, networkSession.AllGamers[i],
  373. position, ScreenManager.SpriteBatch, false);
  374. position.X += ScreenManager.BASE_BUFFER_WIDTH * 0.2f;
  375. }
  376. // draw players 8 - 11 at the left of the screen
  377. position = new Vector2(
  378. ScreenManager.BASE_BUFFER_WIDTH * 0.13f,
  379. ScreenManager.BASE_BUFFER_HEIGHT * 0.2f);
  380. for (int i = 8; i < Math.Min(12, networkSession.AllGamers.Count); i++)
  381. {
  382. world.DrawPlayerData(totalTime, networkSession.AllGamers[i],
  383. position, ScreenManager.SpriteBatch, false);
  384. position.Y += ScreenManager.BASE_BUFFER_HEIGHT * 0.2f;
  385. }
  386. // draw players 12 - 15 at the right of the screen
  387. position = new Vector2(
  388. ScreenManager.BASE_BUFFER_WIDTH * 0.9f,
  389. ScreenManager.BASE_BUFFER_HEIGHT * 0.2f);
  390. for (int i = 12; i < Math.Min(16, networkSession.AllGamers.Count); i++)
  391. {
  392. world.DrawPlayerData(totalTime, networkSession.AllGamers[i],
  393. position, ScreenManager.SpriteBatch, false);
  394. position.Y += ScreenManager.BASE_BUFFER_HEIGHT * 0.2f;
  395. }
  396. // if the game is over, draw the winner text
  397. if (world.GameWon && !String.IsNullOrEmpty(winnerString))
  398. {
  399. ScreenManager.SpriteBatch.DrawString(world.PlayerFont, winnerString,
  400. winnerStringPosition, Color.White, 0f, Vector2.Zero, 1.3f,
  401. SpriteEffects.None, 0f);
  402. }
  403. ScreenManager.SpriteBatch.End();
  404. }
  405. }
  406. /// <summary>
  407. /// Handle the end of the game session.
  408. /// </summary>
  409. void networkSession_GameEnded(object sender, GameEndedEventArgs e)
  410. {
  411. if ((world != null) && !world.GameWon && !world.GameExited)
  412. {
  413. world.GameExited = true;
  414. }
  415. if (!IsExiting && ((world == null) || world.GameExited))
  416. {
  417. world = null;
  418. ExitScreen();
  419. networkSession = null;
  420. }
  421. }
  422. /// <summary>
  423. /// Handle the end of the session.
  424. /// </summary>
  425. void networkSession_SessionEnded(object sender, NetworkSessionEndedEventArgs e)
  426. {
  427. if ((world != null) && !world.GameExited)
  428. {
  429. world.GameExited = true;
  430. world = null;
  431. }
  432. if (!IsExiting)
  433. {
  434. ExitScreen();
  435. }
  436. networkSession = null;
  437. }
  438. /// <summary>
  439. /// Handle a player leaving the game.
  440. /// </summary>
  441. void networkSession_GamerLeft(object sender, GamerLeftEventArgs e)
  442. {
  443. PlayerData playerData = e.Gamer.Tag as PlayerData;
  444. if ((playerData != null) && (playerData.Ship != null))
  445. {
  446. playerData.Ship.Die(null, true);
  447. }
  448. }
  449. /// <summary>
  450. /// Finalizes the GameplayScreen object, calls Dispose(false)
  451. /// </summary>
  452. ~GameplayScreen()
  453. {
  454. Dispose(false);
  455. }
  456. /// <summary>
  457. /// Disposes the GameplayScreen object.
  458. /// </summary>
  459. public void Dispose()
  460. {
  461. Dispose(true);
  462. GC.SuppressFinalize(this);
  463. }
  464. /// <summary>
  465. /// Disposes this object.
  466. /// </summary>
  467. /// <param name="disposing">
  468. /// True if this method was called as part of the Dispose method.
  469. /// </param>
  470. protected virtual void Dispose(bool disposing)
  471. {
  472. if (disposing)
  473. {
  474. lock (this)
  475. {
  476. if (bloomComponent != null)
  477. {
  478. bloomComponent.Dispose();
  479. bloomComponent = null;
  480. }
  481. if (starfield != null)
  482. {
  483. starfield.Dispose();
  484. starfield = null;
  485. }
  486. }
  487. }
  488. }
  489. }
  490. }