SlideShare a Scribd company logo
Developing Cross-Platform Networked Game with XNA 2.0 Game Studio 2008. 5. 24. 김의열  (eykim@microsoft.com) Xbox & HW STE Microsoft Korea EDD R&D
Agenda XNA & Cross-Platform Game Development Preview: Net Rumble Initializing Game Services Creating a Session Finding and Joining a Session Managing Players Joining and Leaving Managing Players from Lobby to Gameplay Sending & Receiving Data Ending the Game DEMO TIPS Q & A
XNA & Cross-Platform Game Development (1) What is the cross-platform game? LIVE Server PC Xbox360
XNA & Cross-Platform Game Development (2) .NET Framework for Windows, & .NET Compact Framework for Xbox 360 공통된  XNA Framework –  하나의  source code 로 각  platform 에서 컴파일 및 실행 가능 추가적인  source code 나  architecture  없이  cross-platform networking  지원 Framework Your Game
Preview: Net Rumble (DEMO) Download from XNA Creators’ Club ( http://creators.xna.com/ ) > Education > Starter kits > Net Rumble DEMO
Preview: Net Rumble (FSM) GamerJoined GamerLeft GameStarted SessionEnded Create(…) StartGame() EndGame() Dispose() GameEnded HostChanged
Initiating Gamer Services Networking API call  이전에  Gamer Services 가 반드시  initialize 되어야 함 Components.Add(new GamerServicesComponent(this)); Gamer 들의  LIVE sign in/out  관리 Gamer 의  Profile  정보 제공 Gamerscore, Gamerzone, Gamer Picture, Region, Motto, Reputation, … SignedInGamer 의  GameDefaults  정보 제공  (preferred settings) Controller sensitivity, Game difficulty, Auto-aim, Axis inversion, … SignedInGamer 의  Privileges  정보 제공 AllowCommunication, AllowOnlineSessions, AllowProfileViewing, LIVE-enabled or guest 자동으로  GamerServicesDispatcher.Update()  호출 , Gamer.SignedInGamers collection 을 업데이트
Initiating Game Services: Code Initializing 에 시간이 많이 소요되므로  constructor 에서 수행 Guide 를 통해  LIVE sign in/out 을 수행 Gamer.SignedInGamers 를 통해  local 에  sign-in 한  Gamer 의  collection 을 가져옴 using Microsoft.Xna.Framework.GamerServices …  public NetRumbleGame() { … Components.Add(new GamerServicesComponent(this)); … } if (!Guide.IsVisible) { Guide.ShowSignIn(1, false); } bool signedIntoLive = false; if (Gamer.SignedInGamers.Count > 0) { foreach (SignedInGamer signedInGamer in Gamer.SignedInGamers) { if (signedInGamer.IsSignedInToLive)  { signedIntoLive = true; break; } } State = signedIntoLive ? MainMenuState.SignedInLive : MainMenuState.SignedInLocal; } else { State = MainMenuState.SignedOut; }
Creating a Session Network session 은  gamer 의 집합과 속성으로 이루어짐 . AllGamers, LocalGamers, RemoteGamers, Host, … SessionType, SessionState, SessionProperties, … IsEveryoneReady, IsHost, … NetworkSession.Create(…) 를 통해  session  생성 .  public static NetworkSession Create(NetworkSessionType sessionType, int maxLocalGamers, int maxGamers); public static NetworkSession Create(NetworkSessionType sessionType, int maxLocalGamers, int maxGamers, int privateGamerSlots, NetworkSessionProperties sessionProperties); .BeginCreate(…) ~ .EndCreate(…) 를 통한 비동기식 방식 가능 . NetworkSession 을 생성한  player 는  host 가 됨 . NetworkException, GamerPrivilegeException 관심 있는 이벤트를  subscribe.
Creating a Session – Network Requirements Xbox 360 Console Windows-Based Computer Run an XNA Framework Game LIVE Silver membership +  Creators Club membership No memberships Required Use System Link LIVE Silver membership +  Creators Club membership No memberships Required Sign-on to Xbox Live and  Games for Windows - LIVE Servers LIVE Silver membership +  Creators Club membership LIVE Silver membership +  Creators Club membership Use LIVE Matchmaking LIVE Gold membership +  Creators Club membership LIVE Gold membership +  Creators Club membership
Creating a Session: Code (1) NetworkSession.Create(…) 은  NetworkSession 을 반환한다 . enum  을 활용하여  NetworkSessionProperties  이용 . BeginCreate(…) ~ EndCreate(…) 를 이용하여 비동기적으로  session 을 생성할 수 있다 . AllowHostMigration? AllowJoinInProgress? using Microsoft.Xna.Framework.Net; … enum SessionProperties { GameMode, ScoreToWin } enum GameMode { FreeForAll, CaptureTheFlag } … NetworkSessionProperties sessionProperties  = new NetworkSessionProperties(); sessionProperties[(int)SessionProperties.GameMode]  = (int)GameMode.FreeForAll; sessionProperties[(int)SessionProperties.ScoreToWin] = 1000; … NetworkSession session; int maxLocalGamers = 1; int maxGamers = 8; int privateGamerSlots =2; session = NetworkSession.Create(NetworkSessionType.SystemLink,  maxLocalGamers, maxGamers, privateGamerSlots, sessionProperties); NetworkSession networkSession = null; … IAsyncResult asyncResult = NetworkSession.BeginCreate(sessionType, 1, World.MaximumPlayers, null, null); … if ((asyncResult != null) && asyncResult.IsCompleted) { networkSession = NetworkSession.EndCreate(asyncResult); … networkSession.AllowHostMigration = true; networkSession.AllowJoinInProgress = false; … }
Creating a Session: Code (2) Session 을 생성한 후에는  NetworkSession 에서 발생하는 이벤트를  subscribe 하여 적절하게 반응한다 . GameEnded  이벤트 GameStarted  이벤트 GamerJoined  이벤트 GamerLeft  이벤트 HostChanged  이벤트 SessionEnded  이벤트 EventHandler<GameStartedEventArgs> gameStartedHandler; EventHandler<GamerJoinedEventArgs> gamerJoinedHandler; EventHandler<NetworkSessionEndedEventArgs> sessionEndedHandler; … public override void LoadContent() { … networkSession.GamerJoined += gamerJoinedHandler; networkSession.GameStarted += gameStartedHandler; networkSession.SessionEnded += sessionEndedHandler; … } … void networkSession_GamerJoined(object sender, GamerJoinedEventArgs e) { … } …
Finding and Joining a Session NetworkSession.Find( … ) 를 통해 원하는  session 의 집합  AvailableNetworkSessionCollection 을 가져옴 .  public static AvailableNetworkSessionCollection Find(NetworkSessionType sessionType, int maxLocalGamers, NetworkSessionProperties searchProperties); .BeginFind( … ) ~ .EndFind( … ) 를 통한 비동기식 방식 가능 . AvailableNetworkSession 에서 다음과 같은 정보를 가져올 수 있다 . HostGamertag, CurrentGamerCount, OpenPrivateGamerSlots, OpenPublicGamerSlots, … 원하는  AvailableNetworkSession 을  NetworkSession.Join( … ) 에 인자로 넘겨주면 해당  session 을 반환하고  Join 이 일어난다 . session = NetworkSession.Join(availableSessions[selectedSessionIndex]; NetworkException, GamerPrivilegeException 관심 있는 이벤트를  subscribe.
Finding and Joining a Session: Code (1) NetworkSession.Create(…) 에 사용하였던 것과 같은 방식으로  NetworkSessionProperties  구��� . availableSessions 로부터  HostGamertag, CurrentGamerCount, OpenPrivateGamerSlots,  등등의 정보를 가져옴 . Quick Match 의 경우는 바로  availableSessions[0] 을 통해  join. using Microsoft.Xna.Framework.Net; … enum SearchProperties { GameMode, ScoreToWin } enum GameMode { FreeForAll, CaptureTheFlag } … NetworkSessionProperties searchProperties  = new NetworkSessionProperties(); searchProperties[(int)SearchProperties.GameMode]  = (int)GameMode.FreeForAll; searchProperties[(int)SearchProperties.ScoreToWin] = 1000; searchProperties[2] = 3; … AvailableNetworkSessionCollection availableSessions; int maxLocalGamers = 1; availableSessions = NetworkSession.Find(NetworkSessionType.PlayerMatch,  maxLocalGamers, searchProperties); if (availableSessions != null) { foreach (AvailableNetworkSession availableSession in availableSessions) { if (availableSession.CurrentGamerCount < World.MaximumPlayers) { MenuEntries.Add(availableSession.HostGamertag + &quot; (&quot; + availableSession.CurrentGamerCount.ToString() + &quot;/&quot; + World.MaximumPlayers.ToString() + &quot;)&quot;); } } }
Finding and Joining a Session: Code (2) NetworkSession.Join 에 참여를 원하는  availableSession 을 넘겨주면 바로  join  완료 . Session 에  join 한 후에는  NetworkSession 에서 발생하는 이벤트를 적절하게  subscribe 한다 . Session 이 종료될 때는  subscribe 했던 이벤트를  unsubscribe. NetworkSession session; … session = NetworkSession.Join(availableSessions[selectedSessionIndex]); EventHandler<GameStartedEventArgs> gameStartedHandler; EventHandler<GamerJoinedEventArgs> gamerJoinedHandler; EventHandler<NetworkSessionEndedEventArgs> sessionEndedHandler; … public override void LoadContent() { … networkSession.GamerJoined += gamerJoinedHandler; networkSession.GameStarted += gameStartedHandler; networkSession.SessionEnded += sessionEndedHandler; … } … void networkSession_GamerJoined(object sender, GamerJoinedEventArgs e) { … } …
Managing Players Joining and Leaving NetworkSession 은  Gamer 들의  list 를 보유  public GamerCollection<NetworkGamer> AllGamers {get;} public GamerCollection<LocalNetworkGamer> LocalGamers {get;} public GamerCollection<NetworkGamer> RemoteGamers {get;}  public NetworkGamer Host {get;} Session 의  gamer list 는  machine  간에 서로 같음이 보장 . Local gamer / Remote gamer Host gamer / guest gamer session 에  gamer 가  joined  할 때  session.GamerJoined  이벤트 발생 . session 에  gamer 가  left  할 때  session.GamerLeft  이벤트 발생 . Gamer.Tag object 를 이용해  player 의 상태와 연결하는 데 사용한다 .
Managing Players Joining and Leaving: Code NetworkSession.GamerJoined, NetworkSession.GamerLeft  이벤트는  session  생성 당시  subscribe 하기로 등록된 상태 GamerJoined  이벤트에 따라  joined 한  player 에  data  할당 Object type 인  Gamer.Tag 를 이용하여  game 에 사용되는  player class 를 연결 GamerLeft  이벤트에 따라  left 한  player 의  data cleanup void networkSession_GamerJoined(object sender, GamerJoinedEventArgs e) { for (int i = 0; i < networkSession.AllGamers.Count; i++) { if (networkSession.AllGamers[i] == e.Gamer) { PlayerData playerData = new PlayerData(); e.Gamer.Tag = playerData; … } } // Code for broadcasting the new player information … } void networkSession_GamerLeft(object sender, GamerLeftEventArgs e) { PlayerData playerData = e.Gamer.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { playerData.Ship.Die(null, true); // Remove the player } }
Managing Players from Lobby to Gameplay 각  Gamer 는  IsReady  속성을 가지고 있음 . 생성된  NetworkSession 은  IsEveryoneReady  속성을 가지고 있음 . 각  Gamer 의  .IsReady  값을 초기화하기 위한  .ResetReady()  메소드 . NetworkSession 은  NetworkSessionState 를 가진다 . NetworkSessionState.Lobby NetworkSessionState.Playing NetworkSessionState.Ended Lobby 에서  Playing 으로  state 를  transit 하기 위해  Host 가  NetworkSession.StartGame()  호출 .
Managing Players from Lobby to Gameplay: Code LocalGamer 가  IsReady 가 아니라면  Ready 하라는 메시지 IsEveryoneReady 가 아니라면 모두  Ready 하기를 기다리라는 메시지 모두  Ready  상태가 되면  Host 가 아닌 경우는 대기 , Host 라면  .StartGame() 와 호출 ,  게임 시작 . NetworkSession.GameStarted 이벤트의  EventHandler 에서  world 를 생성 및 초기화 . if (!networkSession.LocalGamers[0].IsReady) { MenuEntries[0] = &quot;Press X to Mark as Ready&quot;; } else if (!networkSession.IsEveryoneReady) { MenuEntries[0] = &quot;Waiting for all players to mark as ready...&quot;; } else if (!networkSession.IsHost) { MenuEntries[0] = &quot;Waiting for the host to start game...&quot;; } else { MenuEntries[0] = &quot;Starting the game...&quot;; networkSession.StartGame(); } void networkSession_GameStarted(object sender, GameStartedEventArgs e) { if ((networkSession != null) && networkSession.IsHost && (world != null)) { world.GenerateWorld(); } }
Sending & Receiving Data: Network Topology Server-Client model vs. Peer-to-Peer model NetworkSession 의  host 가  game logic 상  server  역할을 의미하지는 않음 . Host 의 역할은  session 의  description 을  own 하고 , session 의  property 와  state 를  change/update 하고 , player 를  session 에서 제거하는 것에만 국한 . HostMigration 이 허용되는 경우 자동으로 새  host 가 선출됨  (NetworkSession.HostChanged  이벤트 발생 ) Server-Client model 의 경우  server gamer 가 갑자기  disconnect 되는 상황 등 고려
Sending & Receiving Data Reliable UDP 에 기반한  packet  전송 LocalNetworkGamer 의  SendData(…) 함수 호출 public void SendData(PacketWriter data, SendDataOptions options); public void SendData(byte[] data, SendDataOptions options); public void SendData(byte[] data, int offset, int count, SendDataOptions options, NetworkGamer recipient); PacketWriter 를 이용하거나  byte 들의  array 를 전달 특정  NetworkGamer 에게 전달하거나  broadcast 상황에 맞는  SendDataOptions .None: Unreliable, out-of-order .InOrder: Unreliable, in-order .Reliable: Reliable, out-of-order .ReliableInOrder: Reliable, in-order LocalNetworkGamer 가  .IsDataAvailable 일 때  ReceiveData(…)  호출 public int ReceiveData(PacketReader data, out NetworkGamer sender); public int ReceiveData(byte[] data, int offset, out NetworkGamer sender);
Sending & Receiving Data: Code (1) PacketWriter  혹은  byte[]  이용 . PacketWriter 는  SendData  수행 후 자동으로  clear 되므로 하나의  instance  생성 후 재사용 . Gamer state data 는  SendDataOptions.InOrder  사용 . GamerJoined, GamerLeft  등 중요한 이벤트에 대해서는  .ReliableInOrder  활용 . 꼭 정보가 필요한 사람에게만 전달하여  traffic 을 최소화 . PacketWriter packetWriter = new PacketWriter(); … PlayerData playerData = networkSession.LocalGamers[0].Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { packetWriter.Write((int)World.PacketTypes.ShipData); packetWriter.Write(playerData.Ship.Position); packetWriter.Write(playerData.Ship.Velocity); packetWriter.Write(playerData.Ship.Rotation); packetWriter.Write(playerData.Ship.Life); packetWriter.Write(playerData.Ship.Shield); packetWriter.Write(playerData.Ship.Score); networkSession.LocalGamers[0].SendData(packetWriter, SendDataOptions.InOrder); } void networkSession_GamerJoined(object sender, GamerJoinedEventArgs e) { … if ((networkSession.LocalGamers.Count > 0) && !e.Gamer.IsLocal) { PlayerData playerData=networkSession.LocalGamers[0].Tag as PlayerData; if (playerData != null) { packetWriter.Write((int)World.PacketTypes.PlayerData); … networkSession.LocalGamers[0].SendData(packetWriter, SendDataOptions.ReliableInOrder, e.Gamer); } } }
Sending & Receiving Data: Code (2) PacketReader  혹은  byte[]  이용 . .IsDataAvailable 로 받은  packet 이 있는지 확인 .ReceiveData 에  packetReader 와  out sender 를 인자로 전달하여 누가 보냈는지 추적 . Packet 의 구조에 따라 적절하게  PacketRead  함수 사용 . PacketReader packetReader = new PacketReader(); … while (networkSession.LocalGamers[0].IsDataAvailable) { NetworkGamer sender; networkSession.LocalGamers[0].ReceiveData(packetReader, out sender); PacketTypes packetType = (PacketTypes)packetReader.ReadInt32(); switch (packetType) { … case PacketTypes.ShipData: if ((sender != null) && !sender.IsLocal) { UpdateShipData(sender); } break; case PacketTypes.WorldData: if (!networkSession.IsHost && Initialized) { UpdateWorldData(); } break; … } }
Latency & Packet Loss Simulation SystemLink 에 비해 전송이 원활하지 않은 상황을  simulation NeetworkSession  인스턴스의 속성 TimeSpan SimulatedLatency Float SimulatedPacketLoss SendDataOptions 들은  simulation  상황에 관계 없이 존중됨 . 일반적인  internet 을 통한  network game play Latency up to 200ms Packet loss up to 0.1 (10%)
Ending the Game NetworkSession 은  NetworkSessionState 를 가진다 . NetworkSessionState.Lobby NetworkSessionState.Playing NetworkSessionState.Ended Playing 에서  Lobby 로  state 를  transit 하기 위해  NetworkSession.EndGame()  호출 . Lobby 로 돌아갈 수도 있고  NetworkSession.Dispose() 를 통해  session 을 종료할 수도 있음 . 다른  player 에 의해  session 이 종료된 경우  NetworkSessionState.Ended state 로 이동 . NetworkSession.SessionEnded  이벤트의  argument 로  NetworkSessionEndReason 이 제공 . .ClientSignedOut, .HostEndedSession, .RemovedByHost, .Disconnected
Ending the Game: Code 조건에 따라서  Host 인  gamer 가  .EndGame()  호출 . NetworkSession.GameEnded 의  EventHandler 에서  world 의 제거 및 후처리 . EndGame()  후  lobby 로 돌아갈 것인가  session 을 종료할 것인가는  design 하기 나름 . Session 이 종료될 때  subscribe 했던 이벤트를  unsubscribe. void networkSession_GameEnded(object sender, GameEndedEventArgs e) { if ((world != null) && !world.GameWon && !world.GameExited) { world.GameExited = true; } if (!IsExiting && ((world == null) || world.GameExited)) {  …  } } … if (world.GameExited) { … if (world != null) { world.Dispose(); world = null; } if (networkSession != null) { networkSession.Dispose(); networkSession = null; } … } switch (packetType) { … case PacketTypes.GameWon: … if (networkSession.IsHost && (networkSession.SessionState == NetworkSessionState.Playing)) {  networkSession.EndGame();  } break; }
DEMO Xbox360  연결하기 Xbox360 용으로 컴파일 및 게임 실행 Cross-platform  매치 : Net Rumble
TIP 1: Control & Balance Input 을  xbox360 과  keyboard/mouse 로부터 동시에 받도록 코드 구성 게임의 성격에 따라 컨트롤의 밸���스에 주의 GamePadState gamePad; KeyboardState keyboard; … protected override void Update(GameTime gameTime) { gamePad = GamePad.GetState(PlayerIndex.One); keyboard = Keyboard.GetState(); gamePadUp = gamePad.DPad.Up == ButtonState.Pressed || gamePad.ThumbSticks.Left.Y > 0.5f; gamePadDown = gamePad.DPad.Down == ButtonState.Pressed || gamePad.ThumbSticks.Left.Y < -0.5f; float moveFactorPerSecond = 0.5f * (float)gameTime.ElapsedRealTime.TotalMilliseconds/1000.0f; if (gamePadUp || keyboard.IsKeyDown(Keys.Up)) rightPaddlePosition -= moveFactorPerSecond; if (gamePadDown || keyboard.IsKeyDown(Keys.Down)) rightPaddlePosition += moveFactorPerSecond; … } ?
TIP 2: SafeArea 화면의 가장자리에 중요한  UI  정보를 출력하지 않는다 . VGA  케이블로 연결된  PC  모니터 : 100%  혹은 이하 SCART  케이블로 연결된 구형 모니터 :  약  92% 컴포넌트 케이블로 연결된  HDTV  모니터 : 93~95% 일부 구형 모니터 : 80~90% SafeArea
TIP 3: No Foreign References XNA Framework 에서 제공하는  dll 외의  dll 들을 함부로 호출하면  Xbox360 에서 정상적으로 컴파일 / 실행되지 않을 수 있음 . mscorlib.~ System.~ Microsoft.Xna.Framework.~ Microsoft.Xna.Framework.dll (XNA Graphic Engine) Microsoft.Xna.Framework.Game.dll (XNA Game Application Model) Microsoft.Xna.Framework.Conponent.Pipeline.dll (XNA Content Pipeline)
Q & A Any questions? Refer to Creators Club ( http://creators.xna.com ) & MSDN!! E-mail: eykim@microsoft.com

More Related Content

XNA2.0 Network Programming

  • 1. Developing Cross-Platform Networked Game with XNA 2.0 Game Studio 2008. 5. 24. 김의열 (eykim@microsoft.com) Xbox & HW STE Microsoft Korea EDD R&D
  • 2. Agenda XNA & Cross-Platform Game Development Preview: Net Rumble Initializing Game Services Creating a Session Finding and Joining a Session Managing Players Joining and Leaving Managing Players from Lobby to Gameplay Sending & Receiving Data Ending the Game DEMO TIPS Q & A
  • 3. XNA & Cross-Platform Game Development (1) What is the cross-platform game? LIVE Server PC Xbox360
  • 4. XNA & Cross-Platform Game Development (2) .NET Framework for Windows, & .NET Compact Framework for Xbox 360 공통된 XNA Framework – 하나의 source code 로 각 platform 에서 컴파일 및 실행 가능 추가적인 source code 나 architecture 없이 cross-platform networking 지원 Framework Your Game
  • 5. Preview: Net Rumble (DEMO) Download from XNA Creators’ Club ( http://creators.xna.com/ ) > Education > Starter kits > Net Rumble DEMO
  • 6. Preview: Net Rumble (FSM) GamerJoined GamerLeft GameStarted SessionEnded Create(…) StartGame() EndGame() Dispose() GameEnded HostChanged
  • 7. Initiating Gamer Services Networking API call 이전에 Gamer Services 가 반드시 initialize 되어야 함 Components.Add(new GamerServicesComponent(this)); Gamer 들의 LIVE sign in/out 관리 Gamer 의 Profile 정보 제공 Gamerscore, Gamerzone, Gamer Picture, Region, Motto, Reputation, … SignedInGamer 의 GameDefaults 정보 제공 (preferred settings) Controller sensitivity, Game difficulty, Auto-aim, Axis inversion, … SignedInGamer 의 Privileges 정보 제공 AllowCommunication, AllowOnlineSessions, AllowProfileViewing, LIVE-enabled or guest 자동으로 GamerServicesDispatcher.Update() 호출 , Gamer.SignedInGamers collection 을 업데이트
  • 8. Initiating Game Services: Code Initializing 에 시간이 많이 소요되므로 constructor 에서 수행 Guide 를 통해 LIVE sign in/out 을 수행 Gamer.SignedInGamers 를 통해 local 에 sign-in 한 Gamer 의 collection 을 가져옴 using Microsoft.Xna.Framework.GamerServices … public NetRumbleGame() { … Components.Add(new GamerServicesComponent(this)); … } if (!Guide.IsVisible) { Guide.ShowSignIn(1, false); } bool signedIntoLive = false; if (Gamer.SignedInGamers.Count > 0) { foreach (SignedInGamer signedInGamer in Gamer.SignedInGamers) { if (signedInGamer.IsSignedInToLive) { signedIntoLive = true; break; } } State = signedIntoLive ? MainMenuState.SignedInLive : MainMenuState.SignedInLocal; } else { State = MainMenuState.SignedOut; }
  • 9. Creating a Session Network session 은 gamer 의 집합과 속성으로 이루어짐 . AllGamers, LocalGamers, RemoteGamers, Host, … SessionType, SessionState, SessionProperties, … IsEveryoneReady, IsHost, … NetworkSession.Create(…) 를 통해 session 생성 . public static NetworkSession Create(NetworkSessionType sessionType, int maxLocalGamers, int maxGamers); public static NetworkSession Create(NetworkSessionType sessionType, int maxLocalGamers, int maxGamers, int privateGamerSlots, NetworkSessionProperties sessionProperties); .BeginCreate(…) ~ .EndCreate(…) 를 통한 비동기식 방식 가능 . NetworkSession 을 생성한 player 는 host 가 됨 . NetworkException, GamerPrivilegeException 관심 있는 이벤트를 subscribe.
  • 10. Creating a Session – Network Requirements Xbox 360 Console Windows-Based Computer Run an XNA Framework Game LIVE Silver membership + Creators Club membership No memberships Required Use System Link LIVE Silver membership + Creators Club membership No memberships Required Sign-on to Xbox Live and Games for Windows - LIVE Servers LIVE Silver membership + Creators Club membership LIVE Silver membership + Creators Club membership Use LIVE Matchmaking LIVE Gold membership + Creators Club membership LIVE Gold membership + Creators Club membership
  • 11. Creating a Session: Code (1) NetworkSession.Create(…) 은 NetworkSession 을 반환한다 . enum 을 활용하여 NetworkSessionProperties 이용 . BeginCreate(…) ~ EndCreate(…) 를 이용하여 비동기적으로 session 을 생성할 수 있다 . AllowHostMigration? AllowJoinInProgress? using Microsoft.Xna.Framework.Net; … enum SessionProperties { GameMode, ScoreToWin } enum GameMode { FreeForAll, CaptureTheFlag } … NetworkSessionProperties sessionProperties = new NetworkSessionProperties(); sessionProperties[(int)SessionProperties.GameMode] = (int)GameMode.FreeForAll; sessionProperties[(int)SessionProperties.ScoreToWin] = 1000; … NetworkSession session; int maxLocalGamers = 1; int maxGamers = 8; int privateGamerSlots =2; session = NetworkSession.Create(NetworkSessionType.SystemLink, maxLocalGamers, maxGamers, privateGamerSlots, sessionProperties); NetworkSession networkSession = null; … IAsyncResult asyncResult = NetworkSession.BeginCreate(sessionType, 1, World.MaximumPlayers, null, null); … if ((asyncResult != null) && asyncResult.IsCompleted) { networkSession = NetworkSession.EndCreate(asyncResult); … networkSession.AllowHostMigration = true; networkSession.AllowJoinInProgress = false; … }
  • 12. Creating a Session: Code (2) Session 을 생성한 후에는 NetworkSession 에서 발생하는 이벤트를 subscribe 하여 적절하게 반응한다 . GameEnded 이벤트 GameStarted 이벤트 GamerJoined 이벤트 GamerLeft 이벤트 HostChanged 이벤트 SessionEnded 이벤트 EventHandler<GameStartedEventArgs> gameStartedHandler; EventHandler<GamerJoinedEventArgs> gamerJoinedHandler; EventHandler<NetworkSessionEndedEventArgs> sessionEndedHandler; … public override void LoadContent() { … networkSession.GamerJoined += gamerJoinedHandler; networkSession.GameStarted += gameStartedHandler; networkSession.SessionEnded += sessionEndedHandler; … } … void networkSession_GamerJoined(object sender, GamerJoinedEventArgs e) { … } …
  • 13. Finding and Joining a Session NetworkSession.Find( … ) 를 통해 원하는 session 의 집합 AvailableNetworkSessionCollection 을 가져옴 . public static AvailableNetworkSessionCollection Find(NetworkSessionType sessionType, int maxLocalGamers, NetworkSessionProperties searchProperties); .BeginFind( … ) ~ .EndFind( … ) 를 통한 비동기식 방식 가능 . AvailableNetworkSession 에서 다음과 같은 정보를 가져올 수 있다 . HostGamertag, CurrentGamerCount, OpenPrivateGamerSlots, OpenPublicGamerSlots, … 원하는 AvailableNetworkSession 을 NetworkSession.Join( … ) 에 인자로 넘겨주면 해당 session 을 반환하고 Join 이 일어난다 . session = NetworkSession.Join(availableSessions[selectedSessionIndex]; NetworkException, GamerPrivilegeException 관심 있는 이벤트를 subscribe.
  • 14. Finding and Joining a Session: Code (1) NetworkSession.Create(…) 에 사용하였던 것과 같은 방식으로 NetworkSessionProperties 구성 . availableSessions 로부터 HostGamertag, CurrentGamerCount, OpenPrivateGamerSlots, 등등의 정보를 가져옴 . Quick Match 의 경우는 바로 availableSessions[0] 을 통해 join. using Microsoft.Xna.Framework.Net; … enum SearchProperties { GameMode, ScoreToWin } enum GameMode { FreeForAll, CaptureTheFlag } … NetworkSessionProperties searchProperties = new NetworkSessionProperties(); searchProperties[(int)SearchProperties.GameMode] = (int)GameMode.FreeForAll; searchProperties[(int)SearchProperties.ScoreToWin] = 1000; searchProperties[2] = 3; … AvailableNetworkSessionCollection availableSessions; int maxLocalGamers = 1; availableSessions = NetworkSession.Find(NetworkSessionType.PlayerMatch, maxLocalGamers, searchProperties); if (availableSessions != null) { foreach (AvailableNetworkSession availableSession in availableSessions) { if (availableSession.CurrentGamerCount < World.MaximumPlayers) { MenuEntries.Add(availableSession.HostGamertag + &quot; (&quot; + availableSession.CurrentGamerCount.ToString() + &quot;/&quot; + World.MaximumPlayers.ToString() + &quot;)&quot;); } } }
  • 15. Finding and Joining a Session: Code (2) NetworkSession.Join 에 참여를 원하는 availableSession 을 넘겨주면 바로 join 완료 . Session 에 join 한 후에는 NetworkSession 에서 발생하는 이벤트를 적절하게 subscribe 한다 . Session 이 종료될 때는 subscribe 했던 이벤트를 unsubscribe. NetworkSession session; … session = NetworkSession.Join(availableSessions[selectedSessionIndex]); EventHandler<GameStartedEventArgs> gameStartedHandler; EventHandler<GamerJoinedEventArgs> gamerJoinedHandler; EventHandler<NetworkSessionEndedEventArgs> sessionEndedHandler; … public override void LoadContent() { … networkSession.GamerJoined += gamerJoinedHandler; networkSession.GameStarted += gameStartedHandler; networkSession.SessionEnded += sessionEndedHandler; … } … void networkSession_GamerJoined(object sender, GamerJoinedEventArgs e) { … } …
  • 16. Managing Players Joining and Leaving NetworkSession 은 Gamer 들의 list 를 보유 public GamerCollection<NetworkGamer> AllGamers {get;} public GamerCollection<LocalNetworkGamer> LocalGamers {get;} public GamerCollection<NetworkGamer> RemoteGamers {get;} public NetworkGamer Host {get;} Session 의 gamer list 는 machine 간에 서로 같음이 보장 . Local gamer / Remote gamer Host gamer / guest gamer session 에 gamer 가 joined 할 때 session.GamerJoined 이벤트 발생 . session 에 gamer 가 left 할 때 session.GamerLeft 이벤트 발생 . Gamer.Tag object 를 이용해 player 의 상태와 연결하는 데 사용한다 .
  • 17. Managing Players Joining and Leaving: Code NetworkSession.GamerJoined, NetworkSession.GamerLeft 이벤트는 session 생성 당시 subscribe 하기로 등록된 상태 GamerJoined 이벤트에 따라 joined 한 player 에 data 할당 Object type 인 Gamer.Tag 를 이용하여 game 에 사용되는 player class 를 연결 GamerLeft 이벤트에 따라 left 한 player 의 data cleanup void networkSession_GamerJoined(object sender, GamerJoinedEventArgs e) { for (int i = 0; i < networkSession.AllGamers.Count; i++) { if (networkSession.AllGamers[i] == e.Gamer) { PlayerData playerData = new PlayerData(); e.Gamer.Tag = playerData; … } } // Code for broadcasting the new player information … } void networkSession_GamerLeft(object sender, GamerLeftEventArgs e) { PlayerData playerData = e.Gamer.Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { playerData.Ship.Die(null, true); // Remove the player } }
  • 18. Managing Players from Lobby to Gameplay 각 Gamer 는 IsReady 속성을 가지고 있음 . 생성된 NetworkSession 은 IsEveryoneReady 속성을 가지고 있음 . 각 Gamer 의 .IsReady 값을 초기화하기 위한 .ResetReady() 메소드 . NetworkSession 은 NetworkSessionState 를 가진다 . NetworkSessionState.Lobby NetworkSessionState.Playing NetworkSessionState.Ended Lobby 에서 Playing 으로 state 를 transit 하기 위해 Host 가 NetworkSession.StartGame() 호출 .
  • 19. Managing Players from Lobby to Gameplay: Code LocalGamer 가 IsReady 가 아니라면 Ready 하라는 메시지 IsEveryoneReady 가 아니라면 모두 Ready 하기��� 기다리라는 메시지 모두 Ready 상태가 되면 Host 가 아닌 경우는 대기 , Host 라면 .StartGame() 와 호출 , 게임 시작 . NetworkSession.GameStarted 이벤트의 EventHandler 에서 world 를 생성 및 초기화 . if (!networkSession.LocalGamers[0].IsReady) { MenuEntries[0] = &quot;Press X to Mark as Ready&quot;; } else if (!networkSession.IsEveryoneReady) { MenuEntries[0] = &quot;Waiting for all players to mark as ready...&quot;; } else if (!networkSession.IsHost) { MenuEntries[0] = &quot;Waiting for the host to start game...&quot;; } else { MenuEntries[0] = &quot;Starting the game...&quot;; networkSession.StartGame(); } void networkSession_GameStarted(object sender, GameStartedEventArgs e) { if ((networkSession != null) && networkSession.IsHost && (world != null)) { world.GenerateWorld(); } }
  • 20. Sending & Receiving Data: Network Topology Server-Client model vs. Peer-to-Peer model NetworkSession 의 host 가 game logic 상 server 역할을 의미하지는 않음 . Host 의 역할은 session 의 description 을 own 하고 , session 의 property 와 state 를 change/update 하고 , player 를 session 에서 제거하는 것에만 국한 . HostMigration 이 허용되는 경우 자동으로 새 host 가 선출됨 (NetworkSession.HostChanged 이벤트 발생 ) Server-Client model 의 경우 server gamer 가 갑자기 disconnect 되는 상황 등 고려
  • 21. Sending & Receiving Data Reliable UDP 에 기반한 packet 전송 LocalNetworkGamer 의 SendData(…) 함수 호출 public void SendData(PacketWriter data, SendDataOptions options); public void SendData(byte[] data, SendDataOptions options); public void SendData(byte[] data, int offset, int count, SendDataOptions options, NetworkGamer recipient); PacketWriter 를 이용하거나 byte 들의 array 를 전달 특정 NetworkGamer 에게 전달하거나 broadcast 상황에 맞는 SendDataOptions .None: Unreliable, out-of-order .InOrder: Unreliable, in-order .Reliable: Reliable, out-of-order .ReliableInOrder: Reliable, in-order LocalNetworkGamer 가 .IsDataAvailable 일 때 ReceiveData(…) 호출 public int ReceiveData(PacketReader data, out NetworkGamer sender); public int ReceiveData(byte[] data, int offset, out NetworkGamer sender);
  • 22. Sending & Receiving Data: Code (1) PacketWriter 혹은 byte[] 이용 . PacketWriter 는 SendData 수행 후 자동으로 clear 되므로 하나의 instance 생성 후 재사용 . Gamer state data 는 SendDataOptions.InOrder 사용 . GamerJoined, GamerLeft 등 중요한 이벤트에 대해서는 .ReliableInOrder 활용 . 꼭 정보가 필요한 사람에게만 전달하여 traffic 을 최소화 . PacketWriter packetWriter = new PacketWriter(); … PlayerData playerData = networkSession.LocalGamers[0].Tag as PlayerData; if ((playerData != null) && (playerData.Ship != null)) { packetWriter.Write((int)World.PacketTypes.ShipData); packetWriter.Write(playerData.Ship.Position); packetWriter.Write(playerData.Ship.Velocity); packetWriter.Write(playerData.Ship.Rotation); packetWriter.Write(playerData.Ship.Life); packetWriter.Write(playerData.Ship.Shield); packetWriter.Write(playerData.Ship.Score); networkSession.LocalGamers[0].SendData(packetWriter, SendDataOptions.InOrder); } void networkSession_GamerJoined(object sender, GamerJoinedEventArgs e) { … if ((networkSession.LocalGamers.Count > 0) && !e.Gamer.IsLocal) { PlayerData playerData=networkSession.LocalGamers[0].Tag as PlayerData; if (playerData != null) { packetWriter.Write((int)World.PacketTypes.PlayerData); … networkSession.LocalGamers[0].SendData(packetWriter, SendDataOptions.ReliableInOrder, e.Gamer); } } }
  • 23. Sending & Receiving Data: Code (2) PacketReader 혹은 byte[] 이용 . .IsDataAvailable 로 받은 packet 이 있는지 확인 .ReceiveData 에 packetReader 와 out sender 를 인자로 전달하여 누가 보냈는지 추적 . Packet 의 구조에 따라 적절하게 PacketRead 함수 사용 . PacketReader packetReader = new PacketReader(); … while (networkSession.LocalGamers[0].IsDataAvailable) { NetworkGamer sender; networkSession.LocalGamers[0].ReceiveData(packetReader, out sender); PacketTypes packetType = (PacketTypes)packetReader.ReadInt32(); switch (packetType) { … case PacketTypes.ShipData: if ((sender != null) && !sender.IsLocal) { UpdateShipData(sender); } break; case PacketTypes.WorldData: if (!networkSession.IsHost && Initialized) { UpdateWorldData(); } break; … } }
  • 24. Latency & Packet Loss Simulation SystemLink 에 비해 전송이 원활하지 않은 상황을 simulation NeetworkSession 인스턴스의 속성 TimeSpan SimulatedLatency Float SimulatedPacketLoss SendDataOptions 들은 simulation 상황에 관계 없이 존중됨 . 일반적인 internet 을 통한 network game play Latency up to 200ms Packet loss up to 0.1 (10%)
  • 25. Ending the Game NetworkSession 은 NetworkSessionState 를 가진다 . NetworkSessionState.Lobby NetworkSessionState.Playing NetworkSessionState.Ended Playing 에서 Lobby 로 state 를 transit 하기 위해 NetworkSession.EndGame() 호출 . Lobby 로 돌아갈 수도 있고 NetworkSession.Dispose() 를 통해 session 을 종료할 수도 있음 . 다른 player 에 의해 session 이 종료된 경우 NetworkSessionState.Ended state 로 이동 . NetworkSession.SessionEnded 이벤트의 argument 로 NetworkSessionEndReason 이 제공 . .ClientSignedOut, .HostEndedSession, .RemovedByHost, .Disconnected
  • 26. Ending the Game: Code 조건에 따라서 Host 인 gamer 가 .EndGame() 호출 . NetworkSession.GameEnded 의 EventHandler 에서 world 의 제거 및 후처리 . EndGame() 후 lobby 로 돌아갈 것인가 session 을 종료할 것인가는 design 하기 나름 . Session 이 종료될 때 subscribe 했던 이벤트를 unsubscribe. void networkSession_GameEnded(object sender, GameEndedEventArgs e) { if ((world != null) && !world.GameWon && !world.GameExited) { world.GameExited = true; } if (!IsExiting && ((world == null) || world.GameExited)) { … } } … if (world.GameExited) { … if (world != null) { world.Dispose(); world = null; } if (networkSession != null) { networkSession.Dispose(); networkSession = null; } … } switch (packetType) { … case PacketTypes.GameWon: … if (networkSession.IsHost && (networkSession.SessionState == NetworkSessionState.Playing)) { networkSession.EndGame(); } break; }
  • 27. DEMO Xbox360 연결하기 Xbox360 용으로 컴파일 및 게임 실행 Cross-platform 매치 : Net Rumble
  • 28. TIP 1: Control & Balance Input 을 xbox360 과 keyboard/mouse 로부터 동시에 받도록 코드 구성 게임의 성격에 따라 컨트롤의 밸런스에 주의 GamePadState gamePad; KeyboardState keyboard; … protected override void Update(GameTime gameTime) { gamePad = GamePad.GetState(PlayerIndex.One); keyboard = Keyboard.GetState(); gamePadUp = gamePad.DPad.Up == ButtonState.Pressed || gamePad.ThumbSticks.Left.Y > 0.5f; gamePadDown = gamePad.DPad.Down == ButtonState.Pressed || gamePad.ThumbSticks.Left.Y < -0.5f; float moveFactorPerSecond = 0.5f * (float)gameTime.ElapsedRealTime.TotalMilliseconds/1000.0f; if (gamePadUp || keyboard.IsKeyDown(Keys.Up)) rightPaddlePosition -= moveFactorPerSecond; if (gamePadDown || keyboard.IsKeyDown(Keys.Down)) rightPaddlePosition += moveFactorPerSecond; … } ?
  • 29. TIP 2: SafeArea 화면의 가장자리에 중요한 UI 정보를 출력하지 않는다 . VGA 케이블로 연결된 PC 모니터 : 100% 혹은 이하 SCART 케이블로 연결된 구형 모니터 : 약 92% 컴포넌트 케이블로 연결된 HDTV 모니터 : 93~95% 일부 구형 모니터 : 80~90% SafeArea
  • 30. TIP 3: No Foreign References XNA Framework 에서 제공하는 dll 외의 dll 들을 함부로 호출하면 Xbox360 에서 정상적으로 컴파일 / 실행되지 않을 수 있음 . mscorlib.~ System.~ Microsoft.Xna.Framework.~ Microsoft.Xna.Framework.dll (XNA Graphic Engine) Microsoft.Xna.Framework.Game.dll (XNA Game Application Model) Microsoft.Xna.Framework.Conponent.Pipeline.dll (XNA Content Pipeline)
  • 31. Q & A Any questions? Refer to Creators Club ( http://creators.xna.com ) & MSDN!! E-mail: eykim@microsoft.com