《XNA高级编程:Xbox 360和Windows》2-3(2)
2010-05-11 21:11
267 查看
2.3开始编写Pong
处理用户输入
正如您在第一章所看到的,在XNA中捕捉用户的键盘和Gamepad输入是非常简单的,单单为它写一个单元测试有点夸张了。您已经知道它是如何工作的,而且也只是想测试一下控制球拍的运动,所以没有必要写一个新的单元测试,完全可以使用前面的TestGameSprites,或许可以把它重新命名为TestSingleplayerGame。单元测试的内容还是一样的,您只需在PongGame的Update方法中修改对输入的处理并更新球拍的位置:
// Get current gamepad and keyboard states
gamePad = GamePad.GetState(PlayerIndex.One);
gamePad2 = GamePad.GetState(PlayerIndex.Two);
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;
gamePad2Up = gamePad2.DPad.Up == ButtonState.Pressed ||
gamePad2.ThumbSticks.Left.Y > 0.5f;
gamePad2Down = gamePad2.DPad.Down == ButtonState.Pressed ||
gamePad2.ThumbSticks.Left.Y < -0.5f;
// Move half way across the screen each second
float moveFactorPerSecond = 0.5f *
(float)gameTime.ElapsedRealTime.TotalMilliseconds / 1000.0f;
// Move up and down if we press the cursor or gamepad keys.
if (gamePadUp || keyboard.IsKeyDown(Keys.Up))
rightPaddlePosition -= moveFactorPerSecond;
if (gamePadDown || keyboard.IsKeyDown(Keys.Down))
rightPaddlePosition += moveFactorPerSecond;
// Second player is either controlled by player 2 or by the computer
if (multiplayer)
else
// Make sure paddles stay between 0 and 1
if (leftPaddlePosition < 0)
leftPaddlePosition = 0;
if (leftPaddlePosition > 1)
leftPaddlePosition = 1;
if (rightPaddlePosition < 0)
rightPaddlePosition = 0;
if (rightPaddlePosition > 1)
rightPaddlePosition = 1;
您可能注意到这里又有些新的变量(multiplayer、gamePad、 gamePad2、keyboard和 ComputerPaddleSpeed),但目前要关心的是改变球拍位置的代码。变量moveFactorPerSecond用来确保球和球拍总是以相同的速度运动,而不管帧的渲染速度是多少,如果是1fps(frame per second),那么moveFactorPerSecond就等于1,如果是10fps,那么它就等于0.1,如果是100fps,那么它就是0.01,以此类推。
接下来,当“Up”和“Down”键或按钮被按下的时候您要改变右边球拍的位置,而左边的球拍由另一个玩家控制,无论是使用Gampad还是“W”和“S”键。如果是单人游戏模式,那么左边的球拍就由计算机控制,并以ComputerPaddleSpeed=0.5f的速度跟着球运动。开始的时候,球运动比较慢,但每次碰撞都会提升一点速度,您也可以使用球拍的边缘撞击球来加速,这样计算机就接不到球您就赢了。
要让新的Update方法能正常工作,需要添加如下的变量和常量:
const float BallSpeedMultiplicator = 0.5f;
const float ComputerPaddleSpeed = 0.5f;//25f;
enum GameMode
GamePadState gamePad, gamePad2;
KeyboardState keyboard;
bool gamePadUp = false,
gamePadDown = false,
gamePad2Up = false,
gamePad2Down = false;
bool multiplayer = false;
GameMode gameMode = GameMode.Menu;
int currentMenuItem = 0;
对于当前的测试您只需要使用我前面提到几个变量,但还是要看一下游戏需要的其他变量。变量BallSpeedMultiplicator决定了球的速度,同时也决定了游戏的整体速度。游戏模式枚举GameMode用来处理当前您可能在的所有三种游戏模式,您可能是刚打开游戏处于菜单界面,或者正在游戏中。当您处在游戏中时,如果一个玩家输了,那么模式将被修改成游戏结束(GameOver)状态,并显示获胜的玩家。
下面是处理菜单输入的代码,虽然您现在还不需要,但它是您要处理的最后一部分输入操作:
// Show screen depending on our current screen mode
if (gameMode == GameMode.Menu)
public void StartNewBall()
// Update ball position and bounce off the borders
ballPosition += ballSpeedVector *
moveFactorPerSecond * BallSpeedMultiplicator;
如果现在使用这个测试运行游戏,球将运动到屏幕的外边,这就是为什么您需要碰撞检测。看一下图2-7所示的构思图,然后添加一些碰撞代码。有时候回过头去看之前设计的构思图,并基于新的想法和知识进行改进是很有必要的,比如这次就是这样。这里有三种碰撞可能会发生:
碰撞屏幕边界——屏幕顶部和底部
碰撞球拍——把球弹回给对手
碰撞球拍后面的屏幕边界——这时玩家会失去一条命,并用StartNewBall方法重新设置球
public static void TestBallCollisions()
// Check top and bottom screen border
if (ballPosition.Y < 0 || ballPosition.Y > 1)
// Check for collisions with the paddles
// Construct bounding boxes to use the intersection helper method.
Vector2 ballSize = new Vector2(
GameBallRect.Width / 1024.0f, GameBallRect.Height / 768.0f);
BoundingBox ballBox = new BoundingBox(
new Vector3(ballPosition.X-ballSize.X/2,
ballPosition.Y-ballSize.Y/2, 0),
new Vector3(ballPosition.X+ballSize.X/2,
ballPosition.Y+ballSize.Y/2, 0));
Vector2 paddleSize = new Vector2(
GameRedPaddleRect.Width / 1024.0f, GameRedPaddleRect.Height / 768.0f);
BoundingBox leftPaddleBox = new BoundingBox(
new Vector3(-paddleSize.X/2, leftPaddlePosition-paddleSize.Y/2, 0),
new Vector3(+paddleSize.X/2, leftPaddlePosition+paddleSize.Y/2, 0));
BoundingBox rightPaddleBox = new BoundingBox(
new Vector3(1-paddleSize.X/2, rightPaddlePosition-paddleSize.Y/2, 0),
new Vector3(1+paddleSize.X/2, rightPaddlePosition+paddleSize.Y/2, 0));
// Ball hit left paddle?
if (ballBox.Intersects(leftPaddleBox))
// Ball lost?
if (ballPosition.X < -0.065f)
else if (ballPosition.X > 1.065f)
// If either player has no more lives, the other one has won!
if (gameMode == GameMode.Game &&
(leftPlayerLives == 0 || rightPlayerLives == 0))
public static void TestSounds()
// Ball lost?
if (ballPosition.X < -0.065f)
else if (ballPosition.X > 1.065f)
{
// Play sound
soundBank.PlayCue("PongBallLost");
// Reduce number of lives
rightPlayerLives--;
// Start new ball
StartNewBall();
} // if
添加这些代码之后,您可以重新启用单元测试TestSingleplayerGame来检查一下声音能否被正确地播放。对于复杂的游戏需要有一个好的系统在播放声音的时候进行检测,而对于大多数的简单游戏,直接使用PlayCue方法就可以了,它可以在需要的时候直接用来播放声音并保存Cue。您也可以自己创建并管理Sound Cue,这样的好处是您可以停止播放或者重新开始等等。
处理用户输入
正如您在第一章所看到的,在XNA中捕捉用户的键盘和Gamepad输入是非常简单的,单单为它写一个单元测试有点夸张了。您已经知道它是如何工作的,而且也只是想测试一下控制球拍的运动,所以没有必要写一个新的单元测试,完全可以使用前面的TestGameSprites,或许可以把它重新命名为TestSingleplayerGame。单元测试的内容还是一样的,您只需在PongGame的Update方法中修改对输入的处理并更新球拍的位置:
// Get current gamepad and keyboard states
gamePad = GamePad.GetState(PlayerIndex.One);
gamePad2 = GamePad.GetState(PlayerIndex.Two);
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;
gamePad2Up = gamePad2.DPad.Up == ButtonState.Pressed ||
gamePad2.ThumbSticks.Left.Y > 0.5f;
gamePad2Down = gamePad2.DPad.Down == ButtonState.Pressed ||
gamePad2.ThumbSticks.Left.Y < -0.5f;
// Move half way across the screen each second
float moveFactorPerSecond = 0.5f *
(float)gameTime.ElapsedRealTime.TotalMilliseconds / 1000.0f;
// Move up and down if we press the cursor or gamepad keys.
if (gamePadUp || keyboard.IsKeyDown(Keys.Up))
rightPaddlePosition -= moveFactorPerSecond;
if (gamePadDown || keyboard.IsKeyDown(Keys.Down))
rightPaddlePosition += moveFactorPerSecond;
// Second player is either controlled by player 2 or by the computer
if (multiplayer)
else
// Make sure paddles stay between 0 and 1
if (leftPaddlePosition < 0)
leftPaddlePosition = 0;
if (leftPaddlePosition > 1)
leftPaddlePosition = 1;
if (rightPaddlePosition < 0)
rightPaddlePosition = 0;
if (rightPaddlePosition > 1)
rightPaddlePosition = 1;
您可能注意到这里又有些新的变量(multiplayer、gamePad、 gamePad2、keyboard和 ComputerPaddleSpeed),但目前要关心的是改变球拍位置的代码。变量moveFactorPerSecond用来确保球和球拍总是以相同的速度运动,而不管帧的渲染速度是多少,如果是1fps(frame per second),那么moveFactorPerSecond就等于1,如果是10fps,那么它就等于0.1,如果是100fps,那么它就是0.01,以此类推。
接下来,当“Up”和“Down”键或按钮被按下的时候您要改变右边球拍的位置,而左边的球拍由另一个玩家控制,无论是使用Gampad还是“W”和“S”键。如果是单人游戏模式,那么左边的球拍就由计算机控制,并以ComputerPaddleSpeed=0.5f的速度跟着球运动。开始的时候,球运动比较慢,但每次碰撞都会提升一点速度,您也可以使用球拍的边缘撞击球来加速,这样计算机就接不到球您就赢了。
要让新的Update方法能正常工作,需要添加如下的变量和常量:
const float BallSpeedMultiplicator = 0.5f;
const float ComputerPaddleSpeed = 0.5f;//25f;
enum GameMode
GamePadState gamePad, gamePad2;
KeyboardState keyboard;
bool gamePadUp = false,
gamePadDown = false,
gamePad2Up = false,
gamePad2Down = false;
bool multiplayer = false;
GameMode gameMode = GameMode.Menu;
int currentMenuItem = 0;
对于当前的测试您只需要使用我前面提到几个变量,但还是要看一下游戏需要的其他变量。变量BallSpeedMultiplicator决定了球的速度,同时也决定了游戏的整体速度。游戏模式枚举GameMode用来处理当前您可能在的所有三种游戏模式,您可能是刚打开游戏处于菜单界面,或者正在游戏中。当您处在游戏中时,如果一个玩家输了,那么模式将被修改成游戏结束(GameOver)状态,并显示获胜的玩家。
下面是处理菜单输入的代码,虽然您现在还不需要,但它是您要处理的最后一部分输入操作:
// Show screen depending on our current screen mode
if (gameMode == GameMode.Menu)
public void StartNewBall()
// Update ball position and bounce off the borders
ballPosition += ballSpeedVector *
moveFactorPerSecond * BallSpeedMultiplicator;
如果现在使用这个测试运行游戏,球将运动到屏幕的外边,这就是为什么您需要碰撞检测。看一下图2-7所示的构思图,然后添加一些碰撞代码。有时候回过头去看之前设计的构思图,并基于新的想法和知识进行改进是很有必要的,比如这次就是这样。这里有三种碰撞可能会发生:
碰撞屏幕边界——屏幕顶部和底部
碰撞球拍——把球弹回给对手
碰撞球拍后面的屏幕边界——这时玩家会失去一条命,并用StartNewBall方法重新设置球
public static void TestBallCollisions()
// Check top and bottom screen border
if (ballPosition.Y < 0 || ballPosition.Y > 1)
// Check for collisions with the paddles
// Construct bounding boxes to use the intersection helper method.
Vector2 ballSize = new Vector2(
GameBallRect.Width / 1024.0f, GameBallRect.Height / 768.0f);
BoundingBox ballBox = new BoundingBox(
new Vector3(ballPosition.X-ballSize.X/2,
ballPosition.Y-ballSize.Y/2, 0),
new Vector3(ballPosition.X+ballSize.X/2,
ballPosition.Y+ballSize.Y/2, 0));
Vector2 paddleSize = new Vector2(
GameRedPaddleRect.Width / 1024.0f, GameRedPaddleRect.Height / 768.0f);
BoundingBox leftPaddleBox = new BoundingBox(
new Vector3(-paddleSize.X/2, leftPaddlePosition-paddleSize.Y/2, 0),
new Vector3(+paddleSize.X/2, leftPaddlePosition+paddleSize.Y/2, 0));
BoundingBox rightPaddleBox = new BoundingBox(
new Vector3(1-paddleSize.X/2, rightPaddlePosition-paddleSize.Y/2, 0),
new Vector3(1+paddleSize.X/2, rightPaddlePosition+paddleSize.Y/2, 0));
// Ball hit left paddle?
if (ballBox.Intersects(leftPaddleBox))
// Ball lost?
if (ballPosition.X < -0.065f)
else if (ballPosition.X > 1.065f)
// If either player has no more lives, the other one has won!
if (gameMode == GameMode.Game &&
(leftPlayerLives == 0 || rightPlayerLives == 0))
public static void TestSounds()
// Ball lost?
if (ballPosition.X < -0.065f)
else if (ballPosition.X > 1.065f)
{
// Play sound
soundBank.PlayCue("PongBallLost");
// Reduce number of lives
rightPlayerLives--;
// Start new ball
StartNewBall();
} // if
添加这些代码之后,您可以重新启用单元测试TestSingleplayerGame来检查一下声音能否被正确地播放。对于复杂的游戏需要有一个好的系统在播放声音的时候进行检测,而对于大多数的简单游戏,直接使用PlayCue方法就可以了,它可以在需要的时候直接用来播放声音并保存Cue。您也可以自己创建并管理Sound Cue,这样的好处是您可以停止播放或者重新开始等等。
相关文章推荐
- 《XNA高级编程:Xbox 360和Windows》2-4/2-5
- 《XNA高级编程:Xbox 360和Windows》3-6
- 《XNA高级编程:Xbox 360和Windows》3-5
- 《XNA高级编程:Xbox 360和Windows》4-3
- 《XNA高级编程:Xbox 360和Windows》2-3(1)
- 《XNA高级编程:Xbox 360和Windows》1-1
- 《XNA高级编程:Xbox 360和Windows》2-6/2-7
- 《XNA高级编程:Xbox 360和Windows》3-7
- 《XNA高级编程:Xbox 360和Windows》4-4
- 《XNA高级编程:Xbox 360和Windows》3-6
- 《XNA高级编程:Xbox 360和Windows》4-4
- 《XNA高级编程:Xbox 360和Windows》2-3(2)
- 《XNA高级编程:Xbox 360和Windows》1-2
- 《XNA高级编程:Xbox 360和Windows》3-1
- 《XNA高级编程:Xbox 360和Windows》3-8
- 《XNA高级编程:Xbox 360和Windows》4-6
- 《XNA高级编程:Xbox 360和Windows》3-7
- 《XNA高级编程:Xbox 360和Windows》1-3
- 《XNA高级编程:Xbox 360和Windows》3-3
- 《XNA高级编程:Xbox 360和Windows》3-9