#region File Description //----------------------------------------------------------------------------- // PeerToPeerGame.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; #if ANDROID using Android.App; #endif using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input.Touch; using Microsoft.Xna.Framework.Net; #endregion namespace PeerToPeer { /// /// Sample showing how to implement a simple multiplayer /// network session, using a peer-to-peer network topology. /// public class PeerToPeerGame : Microsoft.Xna.Framework.Game { #region Fields const int screenWidth = 1067; const int screenHeight = 600; const int maxGamers = 16; const int maxLocalGamers = 4; GraphicsDeviceManager graphics; SpriteBatch spriteBatch; SpriteFont font; KeyboardState currentKeyboardState; GamePadState currentGamePadState; TouchCollection currentTouchState; NetworkSession networkSession; PacketWriter packetWriter = new PacketWriter (); PacketReader packetReader = new PacketReader (); string errorMessage; Texture2D gamePadTexture; #endregion #region Initialization public PeerToPeerGame () { graphics = new GraphicsDeviceManager (this); #if ANDROID || IOS graphics.IsFullScreen = true; #else graphics.PreferredBackBufferWidth = screenWidth; graphics.PreferredBackBufferHeight = screenHeight; graphics.IsFullScreen = false; #endif Content.RootDirectory = "Content"; Components.Add (new GamerServicesComponent (this)); } /// /// Load your content. /// protected override void LoadContent () { spriteBatch = new SpriteBatch (GraphicsDevice); font = Content.Load ("Font"); #if ANDROID || IOS gamePadTexture = Content.Load("gamepad.png"); ThumbStickDefinition thumbStickLeft = new ThumbStickDefinition(); thumbStickLeft.Position = new Vector2(10,400); thumbStickLeft.Texture = gamePadTexture; thumbStickLeft.TextureRect = new Rectangle(2,2,68,68); GamePad.LeftThumbStickDefinition = thumbStickLeft; ThumbStickDefinition thumbStickRight = new ThumbStickDefinition(); thumbStickRight.Position = new Vector2(240,400); thumbStickRight.Texture = gamePadTexture; thumbStickRight.TextureRect = new Rectangle(2,2,68,68); GamePad.RightThumbStickDefinition = thumbStickRight; #endif } #endregion #region Update /// /// Allows the game to run logic. /// protected override void Update (GameTime gameTime) { HandleInput (); if (networkSession == null) { // If we are not in a network session, update the // menu screen that will let us create or join one. UpdateMenuScreen (); } else { // If we are in a network session, update it. UpdateNetworkSession (); } base.Update (gameTime); } /// /// Menu screen provides options to create or join network sessions. /// void UpdateMenuScreen () { if (IsActive) { if (Gamer.SignedInGamers.Count == 0) { // If there are no profiles signed in, we cannot proceed. // Show the Guide so the user can sign in. Guide.ShowSignIn (maxLocalGamers, false); } else if (IsPressed (Keys.A, Buttons.A)) { // Create a new session? CreateSession (); } else if (IsPressed(Keys.X, Buttons.X)) { CreateLiveSession(); } else if (IsPressed(Keys.Y, Buttons.Y)) { JoinSession(NetworkSessionType.PlayerMatch); } else if (IsPressed (Keys.B, Buttons.B)) { // Join an existing session? JoinSession (NetworkSessionType.SystemLink); } } } private void JoinLiveSession() { throw new NotImplementedException(); } private void CreateLiveSession() { DrawMessage("Creating Live session..."); try { networkSession = NetworkSession.Create(NetworkSessionType.PlayerMatch, maxLocalGamers, maxGamers); HookSessionEvents(); //networkSession.AddLocalGamer(); } catch (Exception e) { errorMessage = e.Message; } } /// /// Starts hosting a new network session. /// void CreateSession () { DrawMessage ("Creating session..."); try { networkSession = NetworkSession.Create (NetworkSessionType.SystemLink, maxLocalGamers, maxGamers); HookSessionEvents (); //networkSession.AddLocalGamer(); } catch (Exception e) { errorMessage = e.Message; } } /// /// Joins an existing network session. /// void JoinSession (NetworkSessionType type) { DrawMessage ("Joining session..."); try { // Search for sessions. using (AvailableNetworkSessionCollection availableSessions = NetworkSession.Find (type, maxLocalGamers, null)) { if (availableSessions.Count == 0) { errorMessage = "No network sessions found."; return; } // Join the first session we found. networkSession = NetworkSession.Join (availableSessions [0]); HookSessionEvents (); } } catch (Exception e) { errorMessage = e.Message; } } /// /// After creating or joining a network session, we must subscribe to /// some events so we will be notified when the session changes state. /// void HookSessionEvents () { networkSession.GamerJoined += GamerJoinedEventHandler; networkSession.SessionEnded += SessionEndedEventHandler; } /// /// This event handler will be called whenever a new gamer joins the session. /// We use it to allocate a Tank object, and associate it with the new gamer. /// void GamerJoinedEventHandler (object sender, GamerJoinedEventArgs e) { int gamerIndex = networkSession.AllGamers.IndexOf (e.Gamer); e.Gamer.Tag = new Tank (gamerIndex, Content, screenWidth, screenHeight); } /// /// Event handler notifies us when the network session has ended. /// void SessionEndedEventHandler (object sender, NetworkSessionEndedEventArgs e) { errorMessage = e.EndReason.ToString (); networkSession.Dispose (); networkSession = null; } /// /// Updates the state of the network session, moving the tanks /// around and synchronizing their state over the network. /// void UpdateNetworkSession () { // Update our locally controlled tanks, and send their // latest position data to everyone in the session. foreach (LocalNetworkGamer gamer in networkSession.LocalGamers) { UpdateLocalGamer (gamer); } // Pump the underlying session object. networkSession.Update (); // Make sure the session has not ended. if (networkSession == null) return; // Read any packets telling us the positions of remotely controlled tanks. foreach (LocalNetworkGamer gamer in networkSession.LocalGamers) { ReadIncomingPackets (gamer); } } /// /// Helper for updating a locally controlled gamer. /// void UpdateLocalGamer (LocalNetworkGamer gamer) { // Look up what tank is associated with this local player. Tank localTank = gamer.Tag as Tank; if (localTank != null) { // Update the tank. ReadTankInputs(localTank, gamer.SignedInGamer.PlayerIndex); localTank.Update(); // Write the tank state into a network packet. packetWriter.Write(localTank.Position); packetWriter.Write(localTank.TankRotation); packetWriter.Write(localTank.TurretRotation); // Send the data to everyone in the session. gamer.SendData(packetWriter, SendDataOptions.InOrder); } } /// /// Helper for reading incoming network packets. /// void ReadIncomingPackets (LocalNetworkGamer gamer) { // Keep reading as long as incoming packets are available. while (gamer.IsDataAvailable) { NetworkGamer sender; // Read a single packet from the network. gamer.ReceiveData (packetReader, out sender); // Discard packets sent by local gamers: we already know their state! if (sender.IsLocal) continue; // Look up the tank associated with whoever sent this packet. Tank remoteTank = sender.Tag as Tank; if (remoteTank != null) { // Read the state of this tank from the network packet. remoteTank.Position = packetReader.ReadVector2(); remoteTank.TankRotation = packetReader.ReadSingle(); remoteTank.TurretRotation = packetReader.ReadSingle(); } } } #endregion #region Draw /// /// This is called when the game should draw itself. /// protected override void Draw (GameTime gameTime) { GraphicsDevice.Clear (Color.CornflowerBlue); if (networkSession == null) { // If we are not in a network session, draw the // menu screen that will let us create or join one. DrawMenuScreen (); } else { // If we are in a network session, draw it. DrawNetworkSession (gameTime); } base.Draw (gameTime); } /// /// Draws the startup screen used to create and join network sessions. /// void DrawMenuScreen () { string message = string.Empty; if (!string.IsNullOrEmpty (errorMessage)) message += "Error:\n" + errorMessage.Replace (". ", ".\n") + "\n\n"; message += "A = create local session\n" + "X = create live session\n" + "Y = join live session\n" + "B = join session"; spriteBatch.Begin (); spriteBatch.DrawString (font, message, new Vector2 (61, 161), Color.Black); spriteBatch.DrawString (font, message, new Vector2 (60, 160), Color.White); spriteBatch.End (); } /// /// Draws the state of an active network session. /// void DrawNetworkSession (GameTime gameTime) { spriteBatch.Begin (); // For each person in the session... foreach (NetworkGamer gamer in networkSession.AllGamers) { // Look up the tank object belonging to this network gamer. Tank tank = gamer.Tag as Tank; if (tank != null) { // Draw the tank. tank.Draw(spriteBatch); // Draw a gamertag label. string label = gamer.Gamertag; Color labelColor = Color.Black; Vector2 labelOffset = new Vector2(100, 150); if (gamer.IsHost) label += " (host)"; // Flash the gamertag to yellow when the player is talking. if (gamer.IsTalking) labelColor = Color.Yellow; spriteBatch.DrawString(font, label, tank.Position, labelColor, 0, labelOffset, 0.6f, SpriteEffects.None, 0); } } #if ANDROID || IOS GamePad.Draw(gameTime, spriteBatch); #endif spriteBatch.End (); } /// /// Helper draws notification messages before calling blocking network methods. /// void DrawMessage (string message) { if (!BeginDraw ()) return; GraphicsDevice.Clear (Color.CornflowerBlue); spriteBatch.Begin (); spriteBatch.DrawString (font, message, new Vector2 (161, 161), Color.Black); spriteBatch.DrawString (font, message, new Vector2 (160, 160), Color.White); spriteBatch.End (); EndDraw (); } #endregion #region Handle Input /// /// Handles input. /// private void HandleInput () { currentKeyboardState = Keyboard.GetState (); currentGamePadState = GamePad.GetState (PlayerIndex.One); currentTouchState = TouchPanel.GetState(); // Check for exit. if (IsActive && IsPressed (Keys.Escape, Buttons.Back)) { Exit (); } // Only test of Menu touches when networkSession is null if (networkSession == null) { // Doing very very basic touch detection for menu if (currentTouchState.Count > 0) { Console.WriteLine( string.Format("X:{0}, Y:{1}", currentTouchState[0].Position.X, currentTouchState[0].Position.Y ) ); if ((currentTouchState[0].Position.X > 60 ) && (currentTouchState[0].Position.Y > 160 ) && (currentTouchState[0].Position.X < 220 ) && (currentTouchState[0].Position.Y < 190 ) ) { CreateSession (); } if ((currentTouchState[0].Position.X > 60 ) && (currentTouchState[0].Position.Y > 200 ) && (currentTouchState[0].Position.X < 220 ) && (currentTouchState[0].Position.Y < 230 ) ) { CreateLiveSession(); } if ((currentTouchState[0].Position.X > 60 ) && (currentTouchState[0].Position.Y > 240 ) && (currentTouchState[0].Position.X < 220 ) && (currentTouchState[0].Position.Y < 270 ) ) { JoinSession(NetworkSessionType.PlayerMatch); } if ((currentTouchState[0].Position.X > 60 ) && (currentTouchState[0].Position.Y > 280 ) && (currentTouchState[0].Position.X < 220 ) && (currentTouchState[0].Position.Y < 310 ) ) { JoinSession(NetworkSessionType.SystemLink); } } } } /// /// Checks if the specified button is pressed on either keyboard or gamepad. /// bool IsPressed (Keys key, Buttons button) { return (currentKeyboardState.IsKeyDown (key) || currentGamePadState.IsButtonDown (button)); } /// /// Reads input data from keyboard and gamepad, and stores /// it into the specified tank object. /// void ReadTankInputs (Tank tank, PlayerIndex playerIndex) { // Read the gamepad. GamePadState gamePad = GamePad.GetState (playerIndex); Vector2 tankInput = gamePad.ThumbSticks.Left; Vector2 turretInput = gamePad.ThumbSticks.Right; // Read the keyboard. KeyboardState keyboard = Keyboard.GetState (playerIndex); if (keyboard.IsKeyDown (Keys.Left)) tankInput.X = -1; else if (keyboard.IsKeyDown (Keys.Right)) tankInput.X = 1; if (keyboard.IsKeyDown (Keys.Up)) tankInput.Y = 1; else if (keyboard.IsKeyDown (Keys.Down)) tankInput.Y = -1; if (keyboard.IsKeyDown (Keys.A)) turretInput.X = -1; else if (keyboard.IsKeyDown (Keys.D)) turretInput.X = 1; if (keyboard.IsKeyDown (Keys.W)) turretInput.Y = 1; else if (keyboard.IsKeyDown (Keys.S)) turretInput.Y = -1; // Normalize the input vectors. if (tankInput.Length () > 1) tankInput.Normalize (); if (turretInput.Length () > 1) turretInput.Normalize (); // Store these input values into the tank object. tank.TankInput = tankInput; tank.TurretInput = turretInput; } #endregion } }