// Game Stuffprotected int score;protected int power; private const int INITIALPOWER = 100; public PlayerGame game, ref Texture2D theTexture, PlayerIndex playerID, Rectangle rectangle :
Trang 1// Game Stuffprotected int score;
protected int power;
private const int INITIALPOWER = 100;
public Player(Game game, ref Texture2D theTexture, PlayerIndex playerID, Rectangle rectangle) : base(game)
{texture = theTexture;
position = new Vector2();
playerIndex = playerID;
// Create the source rectangle
// This represents where the sprite picture is in the surfacespriteRectangle = rectangle;
#if XBOX360
// On the 360, we need to take care about the TV "safe" area
screenBounds = new Rectangle((int)(Game.Window.ClientBounds.Width * 0.03f),(int)(Game.Window.ClientBounds.Height * 0.03f),
Game.Window.ClientBounds.Width - (int)(Game.Window.ClientBounds.Width * 0.03f),Game.Window.ClientBounds.Height -
/// Put the ship in your start position in screen/// </summary>
public void Reset(){
if (playerIndex == PlayerIndex.One){
position.X = screenBounds.Width/3;
}else{
Trang 2position.X = (int) (screenBounds.Width/1.5);
}position.Y = screenBounds.Height - spriteRectangle.Height;score = 0;
score = 0;
}else{score = value;
}}}/// <summary>
/// Remaining Power/// </summary>
public int Power{
get { return power; }set { power = value; }}
Trang 3GamePadState gamepadstatus = GamePad.GetState(playerIndex);
position.Y += (int) ((gamepadstatus.ThumbSticks.Left.Y*3)*-2);
position.X += (int) ((gamepadstatus.ThumbSticks.Left.X*3)*2);
// Move the ship with the keyboard
if (playerIndex == PlayerIndex.One){
HandlePlayer1KeyBoard();
}else{HandlePlayer2KeyBoard();
}// Keep the player inside the screenKeepInBound();
// Update scoreelapsedTime += gameTime.ElapsedGameTime;
if (elapsedTime > TimeSpan.FromSeconds(1)){
elapsedTime -= TimeSpan.FromSeconds(1);
score++;
power ;
}base.Update(gameTime);
}/// <summary>
/// Keep the ship inside the screen/// </summary>
private void KeepInBound(){
if (position.X < screenBounds.Left){
Trang 4if (position.Y < screenBounds.Top){
/// Handle the keys for the player 1 (arrow keys)/// </summary>
private void HandlePlayer1KeyBoard(){
KeyboardState keyboard = Keyboard.GetState();
if (keyboard.IsKeyDown(Keys.Up)){
position.Y -= 3;
}
if (keyboard.IsKeyDown(Keys.Down)){
position.Y += 3;
}
if (keyboard.IsKeyDown(Keys.Left)){
position.X -= 3;
}
if (keyboard.IsKeyDown(Keys.Right)){
position.X += 3;
}}/// <summary>
/// Handle the keys for the player 2 (ASDW)/// </summary>
private void HandlePlayer2KeyBoard(){
KeyboardState keyboard = Keyboard.GetState();
if (keyboard.IsKeyDown(Keys.W))
Trang 5{position.Y -= 3;
}
if (keyboard.IsKeyDown(Keys.S)){
position.Y += 3;
}
if (keyboard.IsKeyDown(Keys.A)){
position.X -= 3;
}
if (keyboard.IsKeyDown(Keys.D)){
position.X += 3;
}}
// Draw the shipsBatch.Draw(texture, position, spriteRectangle, Color.White);
Trang 6As you can see, this is practically the same class as in the previous chapter, but in the
Update()method you handle the user input a little differently, testing the PlayerIndextocheck for the correct gamepad or keyboard keys In a multiplayer game, you’ll instantiatetwo objects for this class with different PlayerIndexes and different rectangles in texture,for different ship sprites
Bringing Everything Together
Now you have all the action scene components The meteors, the score, and the player(or players) are ready to be put to work Now add a class called ActionScene This scene isthe most complex scene of the game It coordinates the action of all the components, aswell as controls the game state, such as pauseand gameOver
Start declaring all elements of this scene, as follows:
// Basics
protected Texture2D actionTexture;
protected Cue backMusic;
protected SpriteBatch spriteBatch = null;
// Game Elements
protected Player player1;
protected Player player2;
protected MeteorsManager meteors;
protected PowerSource powerSource;
protected SimpleRumblePad rumblePad;
protected ImageComponent background;
protected Score scorePlayer1;
protected Score scorePlayer2;
// GUI Stuff
protected Vector2 pausePosition;
protected Vector2 gameoverPosition;
protected Rectangle pauseRect = new Rectangle(1, 120, 200, 44);
protected Rectangle gameoverRect = new Rectangle(1, 170, 350, 48);
// GameState elements
protected bool paused;
protected bool gameOver;
protected TimeSpan elapsedTime = TimeSpan.Zero;
protected bool twoPlayers;
Trang 7It looks like the attributes from the game in the previous chapter, but you now havetwo Playerinstances (for a multiplayer game), two attributes for controlling the game
state (pausedand gameOver) and the components for Score,PowerSource,Meteors, and so on
The constructor initializes all these objects, as follows:
/// <summary>
/// Default Constructor
/// </summary>
/// <param name="game">The main game object</param>
/// <param name="theTexture">Texture with the sprite elements</param>
/// <param name="backgroundTexture">Texture for the background</param>
/// <param name="font">Font used in the score</param>
public ActionScene(Game game, Texture2D theTexture,
Texture2D backgroundTexture, SpriteFont font) : base(game){
// Get the current audiocomponent and play the background musicaudioComponent = (AudioComponent)
meteors = new MeteorsManager(Game, ref actionTexture);
scorePlayer1 = new Score(game, font, Color.Blue);
scorePlayer1.Position = new Vector2(10, 10);
Trang 8scorePlayer2 = new Score(game, font, Color.Red);
scorePlayer2.Position = new Vector2(
You also need to control the game state and define if the game is for one or two players,
or check if some of the players are already dead Add these properties to the class:
Trang 9{paused = value;
if (paused){
backMusic.Pause();
}else{backMusic.Resume();
}}}
Like all the other scenes, you can use the Show()and Hide()methods to initialize andrelease scene components In the Show()method you start playing the background music
and setting the player2status if you have a two-player game:
pausePosition.Y = (Game.Window.ClientBounds.Height - pauseRect.Height)/2;
gameOver = false;
gameoverPosition.X = (Game.Window.ClientBounds.Width - gameoverRect.Width)/2;
gameoverPosition.Y = (Game.Window.ClientBounds.Height - gameoverRect.Height)/2;
Trang 10/// <param name="gameTime">Provides a snapshot of timing values.</param>
public override void Update(GameTime gameTime)
scorePlayer1.Power = player1.Power;
Trang 11if (twoPlayers){
scorePlayer2.Value = player2.Score;
scorePlayer2.Power = player2.Power;
}// Check if player is deadgameOver = ((player1.Power <= 0) || (player2.Power <= 0));
if (gameOver){
player1.Visible = (player1.Power > 0);
player2.Visible = (player2.Power > 0) && twoPlayers;
// Stop the musicbackMusic.Stop(AudioStopOptions.Immediate);
// Stop rumblerumblePad.Stop(PlayerIndex.One);
rumblePad.Stop(PlayerIndex.Two);
}// Update all other GameComponentsbase.Update(gameTime);
}// In gameOver state, keep the meteors' animation
if (gameOver){
meteors.Update(gameTime);
}}
The HandleDamages()and HandlePowerSourceSprite()methods check the collisionswith the meteors (and lose some player power), check the collision with the power source
(and add some power to the player), and check if a player has zero or less power to end
the game and put him or her in a game over state
The HandleDamages()method is also similar to the collision test method from the vious chapter Again, this method checks the collision with the players and meteors and
pre-one player with another player For each collision the player loses ten points and ten
Trang 12// Check Collision for player 1
if (meteors.CheckForCollisions(player1.GetBounds())){
// Shake!
rumblePad.RumblePad(PlayerIndex.One, 500, 1.0f, 1.0f);
// Player penaltyplayer1.Power -= 10;
player1.Score -= 10;
}// Check Collision for player 2
if (twoPlayers){
if (meteors.CheckForCollisions(player2.GetBounds())){
// Shake!
rumblePad.RumblePad(PlayerIndex.Two, 500, 1.0f, 1.0f);
// Player penaltyplayer2.Power -= 10;
player2.Score -= 10;
}// Check for collision between the players
if (player1.GetBounds().Intersects(player2.GetBounds())){
The HandlePowerSourceSprite()method does the same job, but with the PowerSource
sprite If some player collides with this sprite, he or she gets 50 power units The methodalso checks if it’s time to send a new power source in the game, using an interval of
15 seconds
Trang 13// Player 1 gets the power sourceaudioComponent.PlayCue("powerget");
// Player 2 gets the power source
if (powerSource.CheckCollision(player2.GetBounds())){
if (elapsedTime > TimeSpan.FromSeconds(15)){
elapsedTime -= TimeSpan.FromSeconds(15);
powerSource.Enabled = true;
}}
And finally, the Draw()method just draws some objects for a specified game state:
/// <summary>
/// Allows the GameComponent to draw itself
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
public override void Draw(GameTime gameTime)
{
Trang 14// Draw all GameComponentsbase.Draw(gameTime);
if (paused){
// Draw the "pause" textspriteBatch.Draw(actionTexture, pausePosition, pauseRect, Color.White);
}
if (gameOver){
// Draw the "gameover" textspriteBatch.Draw(actionTexture, gameoverPosition, gameoverRect, Color.White);
}
Observe that once again, a great deal of the game logic that you created in the ous chapter was kept You only added the two-player support and two more game states:one when the user pauses the game (pressing the Enter key or pressing the A button onthe Xbox 360 gamepad during the game), or when one of the players runs out of energy.When this happens, the game shows a message on the screen and waits for the player topress the Enter key or the A button on the Xbox 360 gamepad
previ-Navigating Between the Scenes
With all the scenes created, now you only need to show them according to users’ wishes.Through the menu in the opening scene, users can show the help scene, the action scene(with one or two players), or just leave the game Here, you’ll use a technique in whichyou concentrate all the inputs that refer to the navigation or control of the scene states inone class In this case you use the Game1class, so that you have a central point where youshoot the scenes and control the Game1class’s state Add the following code in the Game1
class:
private readonly GraphicsDeviceManager graphics;
private SpriteBatch spriteBatch;
// Textures
protected Texture2D helpBackgroundTexture, helpForegroundTexture;
protected Texture2D startBackgroundTexture, startElementsTexture;
protected Texture2D actionElementsTexture, actionBackgroundTexture;
// Game Scenes
protected HelpScene helpScene;
Trang 15protected StartScene startScene;
protected ActionScene actionScene;
protected GameScene activeScene;
// Audio Stuff
private AudioComponent audioComponent;
// Fonts
private SpriteFont smallFont, largeFont, scoreFont;
// Used to handle input
protected KeyboardState oldKeyboardState;
protected GamePadState oldGamePadState;
In the LoadContent()method, add the code to create and load the content for the
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Handle Game Inputs
HandleScenesInput();
Trang 16}// Handle Help Scene Inputelse if (activeScene == helpScene){
if (CheckEnterA()){
ShowScene(startScene);
}}// Handle Action Scene Inputelse if (activeScene == actionScene){
HandleActionInput();
}}
The CheckEnterA()method is a simple code to test the Enter key and the A button on
an Xbox 360 gamepad:
/// <summary>
/// Check if the Enter Key or A button was pressed
/// </summary>
/// <returns>true, if Enter key or A button was pressed</returns>
private bool CheckEnterA()
Trang 17bool result = (oldKeyboardState.IsKeyDown(Keys.Enter) &&
audioComponent.PlayCue("menu_select3");
switch (startScene.SelectedMenuIndex){
Trang 18HandleActionInput()handles input in the action scene to pause and cancel a game,using a keyboard or an Xbox 360 gamepad:
/// <summary>
/// Check if the Enter Key or A button was pressed
/// </summary>
/// <returns>true, if Enter key or A button was pressed</returns>
private void HandleActionInput()
{
// Get the Keyboard and GamePad stateGamePadState gamepadState = GamePad.GetState(PlayerIndex.One);
KeyboardState keyboardState = Keyboard.GetState();
bool backKey = (oldKeyboardState.IsKeyDown(Keys.Escape) &&
if (actionScene.GameOver){
ShowScene(startScene);
}else{audioComponent.PlayCue("menu_back");
actionScene.Paused = !actionScene.Paused;
}}
if (backKey){
ShowScene(startScene);
Trang 19The ShowScene()method is just a helper to Show()a new scene and Hide()a previousscene, as follows:
/// <summary>
/// Open a new scene
/// </summary>
/// <param name="scene">Scene to be opened</param>
protected void ShowScene(GameScene scene)
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
However, try putting new meteor types or new ways to acquire energy, for instance You’ll
start to understand how games are “assembled” from GameComponents
Trang 20You started from a simple game and evolved that into a more elaborate game with simpletechniques that are useful to any kind of game You saw the value of the GameComponentsand their reuse capability Feel free to improve and change this game and build your ownawesome version of Rock Rain!
Trang 21Basics of Game Networking
In this chapter you’ll see basic concepts involved in creating games that support
net-working, so you’ll be prepared to create a real multiplayer game in the next chapter
Introducing Multiplayer Games
Online multiplayer games, also known as network-enabled games or simply networked
games, are hard to code Period
That said, it’s also important to state that, in XNA, this difficulty is not related to ing for connecting the machines (PCs or Xbox 360) or making them talk with each other
cod-That’s because XNA hides all complexities from you in this case, as it does with
every-thing else in the framework
Networked games are hard to code because there are many extra problems to dealwith: your program will receive messages from the host or other players, send messages
back to them, process the local player input, and perform the physics and artificial
intel-ligence calculations, while not letting the screen freeze between each frame drawn (one
of the worst things that might happen in a multiplayer game)
Fortunately, XNA can help us with most of the communication problems, such asproviding ways to control the message flow between players and host to guarantee that
no message is lost and that all messages arrive in the same order they were sent, if you
want to Nevertheless, there will still be some problems to solve
Before discussing the details of XNA support for networking, let’s look at some basicconcepts about networked games and some of the most common problems faced when
coding such games, in the next sections
Choosing the Network Topology
The most common topologies for networked games are peer-to-peer and client/server
connections, and because XNA network implementation is not tied to any type of
con-nection, you can code any of these types depending on the way you organize your
network code
129
C H A P T E R 5
Trang 22In peer-to-peer connections, every player is aware of every other player in the game,sending and receiving messages from, and to, all players, as illustrated in Figure 5-1.
Figure 5-1.Peer-to-peer connection
The most obvious benefit of using this network organization is that you don’t need adedicated server to play the game, so every group of players can play it within their ownlocal area network (LAN), or even through the Internet, as long as they know the
addresses of the other members of the group
In this type of connection, one of the players acts as a host, so all the new playersconnect to that player However, once connected, the messages flow directly from oneplayer to all the others If the player who is also the host disconnects from the game, thegame might stop or simply choose another player as the new host, depending on whatthe game developers defined
The main problem you face when coding peer-to-peer games is that you can’t havetoo many players in the same game session, because the number of messages willincrease exponentially with every new player who joins For instance, in Figure 5-1 wehave 4 players, so every time a player needs to update his or her status (for example,move), you send 3 messages, one for each player Because you have 4 players, duringeach game turn you exchange 4 ✕3 = 12 messages Making the same calculations with a 5-player game increases this to 5 ✕4 = 20 messages per turn, and in a 6-player gameyou’ll reach 6 ✕5 = 30 messages
Usually, having more than ten players in the same game session is not suggested,because every message can take dozens of bytes and you’ll consume the bandwidthavailable in your network quickly But it’s still possible if the game development team canmake the messages as small as possible; for example, passing only the players’ inputsacross the computers, and letting games on every player’s machine calculate everythingelse from these inputs
The second most common game network topology is client/server In this kind ofnetwork, all players connect to a host, which usually processes the messages and does