InvitesGame.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // InvitesGame.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.GamerServices;
  13. using Microsoft.Xna.Framework.Graphics;
  14. using Microsoft.Xna.Framework.Input;
  15. using Microsoft.Xna.Framework.Net;
  16. #endregion
  17. namespace Invites
  18. {
  19. /// <summary>
  20. /// Sample showing how to add invite support to a networked XNA Framework
  21. /// game. This builds on the Peer-to-Peer sample, extending it by adding
  22. /// an event handler which will respond to invite notifications.
  23. /// </summary>
  24. public class InvitesGame : Microsoft.Xna.Framework.Game
  25. {
  26. #region Fields
  27. const int screenWidth = 1067;
  28. const int screenHeight = 600;
  29. const int maxGamers = 16;
  30. const int maxLocalGamers = 4;
  31. GraphicsDeviceManager graphics;
  32. SpriteBatch spriteBatch;
  33. SpriteFont font;
  34. KeyboardState currentKeyboardState;
  35. GamePadState currentGamePadState;
  36. NetworkSession networkSession;
  37. PacketWriter packetWriter = new PacketWriter();
  38. PacketReader packetReader = new PacketReader();
  39. string errorMessage;
  40. #endregion
  41. #region Initialization
  42. public InvitesGame()
  43. {
  44. graphics = new GraphicsDeviceManager(this);
  45. graphics.PreferredBackBufferWidth = screenWidth;
  46. graphics.PreferredBackBufferHeight = screenHeight;
  47. Content.RootDirectory = "Content";
  48. Components.Add(new GamerServicesComponent(this));
  49. // Listen for invite notification events. This handler will be called
  50. // whenever the user accepts an invite message, or if they select the
  51. // "Join Session In Progress" option from their Guide friends screen.
  52. NetworkSession.InviteAccepted += InviteAcceptedEventHandler;
  53. }
  54. /// <summary>
  55. /// Load your content.
  56. /// </summary>
  57. protected override void LoadContent()
  58. {
  59. spriteBatch = new SpriteBatch(GraphicsDevice);
  60. font = Content.Load<SpriteFont>("Font");
  61. }
  62. #endregion
  63. #region Update
  64. /// <summary>
  65. /// Allows the game to run logic.
  66. /// </summary>
  67. protected override void Update(GameTime gameTime)
  68. {
  69. HandleInput();
  70. if (networkSession == null)
  71. {
  72. // If we are not in a network session, update the
  73. // menu screen that will let us create or join one.
  74. UpdateMenuScreen();
  75. }
  76. else
  77. {
  78. // If we are in a network session, update it.
  79. UpdateNetworkSession();
  80. }
  81. base.Update(gameTime);
  82. }
  83. /// <summary>
  84. /// Menu screen provides options to create or join network sessions.
  85. /// </summary>
  86. void UpdateMenuScreen()
  87. {
  88. if (IsActive)
  89. {
  90. if (Gamer.SignedInGamers.Count == 0)
  91. {
  92. // If there are no profiles signed in, we cannot proceed.
  93. // Show the Guide so the user can sign in.
  94. Guide.ShowSignIn(maxLocalGamers, false);
  95. }
  96. else if (IsPressed(Keys.A, Buttons.A))
  97. {
  98. // Create a new session?
  99. CreateSession();
  100. }
  101. else if (IsPressed(Keys.B, Buttons.B))
  102. {
  103. // Join an existing session?
  104. JoinSession();
  105. }
  106. }
  107. }
  108. /// <summary>
  109. /// Starts hosting a new network session.
  110. /// </summary>
  111. void CreateSession()
  112. {
  113. DrawMessage("Creating session...");
  114. try
  115. {
  116. networkSession = NetworkSession.Create(NetworkSessionType.PlayerMatch,
  117. maxLocalGamers, maxGamers);
  118. HookSessionEvents();
  119. }
  120. catch (Exception error)
  121. {
  122. errorMessage = error.Message;
  123. }
  124. }
  125. /// <summary>
  126. /// Joins an existing network session.
  127. /// </summary>
  128. void JoinSession()
  129. {
  130. DrawMessage("Joining session...");
  131. try
  132. {
  133. // Search for sessions.
  134. using (AvailableNetworkSessionCollection availableSessions =
  135. NetworkSession.Find(NetworkSessionType.PlayerMatch,
  136. maxLocalGamers, null))
  137. {
  138. if (availableSessions.Count == 0)
  139. {
  140. errorMessage = "No network sessions found.";
  141. return;
  142. }
  143. // Join the first session we found.
  144. networkSession = NetworkSession.Join(availableSessions[0]);
  145. HookSessionEvents();
  146. }
  147. }
  148. catch (Exception error)
  149. {
  150. errorMessage = error.Message;
  151. }
  152. }
  153. /// <summary>
  154. /// This event handler will be called whenever the game recieves an invite
  155. /// notification. This can occur when the user accepts an invite that was
  156. /// sent to them by a friend (pull mode), or if they choose the "Join
  157. /// Session In Progress" option in their friends screen (push mode).
  158. /// The handler should leave the current session (if any), then join the
  159. /// session referred to by the invite. It is not necessary to prompt the
  160. /// user before doing this, as the Guide will already have taken care of
  161. /// the necessary confirmations before the invite was delivered to you.
  162. /// </summary>
  163. void InviteAcceptedEventHandler(object sender, InviteAcceptedEventArgs e)
  164. {
  165. DrawMessage("Joining session from invite...");
  166. // Leave the current network session.
  167. if (networkSession != null)
  168. {
  169. networkSession.Dispose();
  170. networkSession = null;
  171. }
  172. try
  173. {
  174. // Join a new session in response to the invite.
  175. networkSession = NetworkSession.JoinInvited(maxLocalGamers);
  176. HookSessionEvents();
  177. }
  178. catch (Exception error)
  179. {
  180. errorMessage = error.Message;
  181. }
  182. }
  183. /// <summary>
  184. /// After creating or joining a network session, we must subscribe to
  185. /// some events so we will be notified when the session changes state.
  186. /// </summary>
  187. void HookSessionEvents()
  188. {
  189. networkSession.GamerJoined += GamerJoinedEventHandler;
  190. networkSession.SessionEnded += SessionEndedEventHandler;
  191. }
  192. /// <summary>
  193. /// This event handler will be called whenever a new gamer joins the session.
  194. /// We use it to allocate a Tank object, and associate it with the new gamer.
  195. /// </summary>
  196. void GamerJoinedEventHandler(object sender, GamerJoinedEventArgs e)
  197. {
  198. int gamerIndex = networkSession.AllGamers.IndexOf(e.Gamer);
  199. e.Gamer.Tag = new Tank(gamerIndex, Content, screenWidth, screenHeight);
  200. }
  201. /// <summary>
  202. /// Event handler notifies us when the network session has ended.
  203. /// </summary>
  204. void SessionEndedEventHandler(object sender, NetworkSessionEndedEventArgs e)
  205. {
  206. errorMessage = e.EndReason.ToString();
  207. networkSession.Dispose();
  208. networkSession = null;
  209. }
  210. /// <summary>
  211. /// Updates the state of the network session, moving the tanks
  212. /// around and synchronizing their state over the network.
  213. /// </summary>
  214. void UpdateNetworkSession()
  215. {
  216. // Update our locally controlled tanks, and send their
  217. // latest position data to everyone in the session.
  218. foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
  219. {
  220. UpdateLocalGamer(gamer);
  221. }
  222. // Pump the underlying session object.
  223. networkSession.Update();
  224. // Make sure the session has not ended.
  225. if (networkSession == null)
  226. return;
  227. // Read any packets telling us the positions of remotely controlled tanks.
  228. foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
  229. {
  230. ReadIncomingPackets(gamer);
  231. }
  232. }
  233. /// <summary>
  234. /// Helper for updating a locally controlled gamer.
  235. /// </summary>
  236. void UpdateLocalGamer(LocalNetworkGamer gamer)
  237. {
  238. // Look up what tank is associated with this local player.
  239. Tank localTank = gamer.Tag as Tank;
  240. // Update the tank.
  241. ReadTankInputs(localTank, gamer.SignedInGamer.PlayerIndex);
  242. localTank.Update();
  243. // Write the tank state into a network packet.
  244. packetWriter.Write(localTank.Position);
  245. packetWriter.Write(localTank.TankRotation);
  246. packetWriter.Write(localTank.TurretRotation);
  247. // Send the data to everyone in the session.
  248. gamer.SendData(packetWriter, SendDataOptions.InOrder);
  249. }
  250. /// <summary>
  251. /// Helper for reading incoming network packets.
  252. /// </summary>
  253. void ReadIncomingPackets(LocalNetworkGamer gamer)
  254. {
  255. // Keep reading as long as incoming packets are available.
  256. while (gamer.IsDataAvailable)
  257. {
  258. NetworkGamer sender;
  259. // Read a single packet from the network.
  260. gamer.ReceiveData(packetReader, out sender);
  261. // Discard packets sent by local gamers: we already know their state!
  262. if (sender.IsLocal)
  263. continue;
  264. // Look up the tank associated with whoever sent this packet.
  265. Tank remoteTank = sender.Tag as Tank;
  266. // Read the state of this tank from the network packet.
  267. remoteTank.Position = packetReader.ReadVector2();
  268. remoteTank.TankRotation = packetReader.ReadSingle();
  269. remoteTank.TurretRotation = packetReader.ReadSingle();
  270. }
  271. }
  272. #endregion
  273. #region Draw
  274. /// <summary>
  275. /// This is called when the game should draw itself.
  276. /// </summary>
  277. protected override void Draw(GameTime gameTime)
  278. {
  279. GraphicsDevice.Clear(Color.CornflowerBlue);
  280. if (networkSession == null)
  281. {
  282. // If we are not in a network session, draw the
  283. // menu screen that will let us create or join one.
  284. DrawMenuScreen();
  285. }
  286. else
  287. {
  288. // If we are in a network session, draw it.
  289. DrawNetworkSession();
  290. }
  291. base.Draw(gameTime);
  292. }
  293. /// <summary>
  294. /// Draws the startup screen used to create and join network sessions.
  295. /// </summary>
  296. void DrawMenuScreen()
  297. {
  298. string message = string.Empty;
  299. if (!string.IsNullOrEmpty(errorMessage))
  300. message += "Error:\n" + errorMessage.Replace(". ", ".\n") + "\n\n";
  301. message += "A = create session\n" +
  302. "B = join session";
  303. spriteBatch.Begin();
  304. spriteBatch.DrawString(font, message, new Vector2(161, 161), Color.Black);
  305. spriteBatch.DrawString(font, message, new Vector2(160, 160), Color.White);
  306. spriteBatch.End();
  307. }
  308. /// <summary>
  309. /// Draws the state of an active network session.
  310. /// </summary>
  311. void DrawNetworkSession()
  312. {
  313. spriteBatch.Begin();
  314. // For each person in the session...
  315. foreach (NetworkGamer gamer in networkSession.AllGamers)
  316. {
  317. // Look up the tank object belonging to this network gamer.
  318. Tank tank = gamer.Tag as Tank;
  319. // Draw the tank.
  320. tank.Draw(spriteBatch);
  321. // Draw a gamertag label.
  322. string label = gamer.Gamertag;
  323. Color labelColor = Color.Black;
  324. Vector2 labelOffset = new Vector2(100, 150);
  325. if (gamer.IsHost)
  326. label += " (host)";
  327. // Flash the gamertag to yellow when the player is talking.
  328. if (gamer.IsTalking)
  329. labelColor = Color.Yellow;
  330. spriteBatch.DrawString(font, label, tank.Position, labelColor, 0,
  331. labelOffset, 0.6f, SpriteEffects.None, 0);
  332. }
  333. spriteBatch.End();
  334. }
  335. /// <summary>
  336. /// Helper draws notification messages before calling blocking network methods.
  337. /// </summary>
  338. void DrawMessage(string message)
  339. {
  340. if (!BeginDraw())
  341. return;
  342. GraphicsDevice.Clear(Color.CornflowerBlue);
  343. spriteBatch.Begin();
  344. spriteBatch.DrawString(font, message, new Vector2(161, 161), Color.Black);
  345. spriteBatch.DrawString(font, message, new Vector2(160, 160), Color.White);
  346. spriteBatch.End();
  347. EndDraw();
  348. }
  349. #endregion
  350. #region Handle Input
  351. /// <summary>
  352. /// Handles input.
  353. /// </summary>
  354. private void HandleInput()
  355. {
  356. currentKeyboardState = Keyboard.GetState();
  357. currentGamePadState = GamePad.GetState(PlayerIndex.One);
  358. // Check for exit.
  359. if (IsActive && IsPressed(Keys.Escape, Buttons.Back))
  360. {
  361. Exit();
  362. }
  363. }
  364. /// <summary>
  365. /// Checks if the specified button is pressed on either keyboard or gamepad.
  366. /// </summary>
  367. bool IsPressed(Keys key, Buttons button)
  368. {
  369. return (currentKeyboardState.IsKeyDown(key) ||
  370. currentGamePadState.IsButtonDown(button));
  371. }
  372. /// <summary>
  373. /// Reads input data from keyboard and gamepad, and stores
  374. /// it into the specified tank object.
  375. /// </summary>
  376. void ReadTankInputs(Tank tank, PlayerIndex playerIndex)
  377. {
  378. // Read the gamepad.
  379. GamePadState gamePad = GamePad.GetState(playerIndex);
  380. Vector2 tankInput = gamePad.ThumbSticks.Left;
  381. Vector2 turretInput = gamePad.ThumbSticks.Right;
  382. // Read the keyboard.
  383. KeyboardState keyboard = Keyboard.GetState(playerIndex);
  384. if (keyboard.IsKeyDown(Keys.Left))
  385. tankInput.X = -1;
  386. else if (keyboard.IsKeyDown(Keys.Right))
  387. tankInput.X = 1;
  388. if (keyboard.IsKeyDown(Keys.Up))
  389. tankInput.Y = 1;
  390. else if (keyboard.IsKeyDown(Keys.Down))
  391. tankInput.Y = -1;
  392. if (keyboard.IsKeyDown(Keys.A))
  393. turretInput.X = -1;
  394. else if (keyboard.IsKeyDown(Keys.D))
  395. turretInput.X = 1;
  396. if (keyboard.IsKeyDown(Keys.W))
  397. turretInput.Y = 1;
  398. else if (keyboard.IsKeyDown(Keys.S))
  399. turretInput.Y = -1;
  400. // Normalize the input vectors.
  401. if (tankInput.Length() > 1)
  402. tankInput.Normalize();
  403. if (turretInput.Length() > 1)
  404. turretInput.Normalize();
  405. // Store these input values into the tank object.
  406. tank.TankInput = tankInput;
  407. tank.TurretInput = turretInput;
  408. }
  409. #endregion
  410. }
  411. #region Entry Point
  412. /// <summary>
  413. /// The main entry point for the application.
  414. /// </summary>
  415. static class Program
  416. {
  417. static void Main()
  418. {
  419. using (InvitesGame game = new InvitesGame())
  420. {
  421. game.Run();
  422. }
  423. }
  424. }
  425. #endregion
  426. }