PeerToPeerGame.cs 16 KB

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