打砖块
此游戏代码的核心部分来自于§3.7Breakout游戏,我只是根据引擎适当地修改了代码。比起上一个乒乓游戏(用于Sliverlight的StunEngine引擎),场景只有一个,其实更简单,新多出的东西就是使用包围盒BoundingBox进行碰撞检测,XNA框架中已经内置了BoundingBox.Intersects方法进行相交性检测,所以并没有特别难的东西,细节部分可参考代码的注释。
游戏主类GameplayScreen的代码如下:
namespace Silverlight3dApp { /// <summary> /// 实际进行游戏的屏幕 /// </summary> class GameplayScreen:Scene2D { #region 常量 static readonly Rectangle GamePaddleRect = new Rectangle(39, 1, 93, 23), GameBallRect = new Rectangle(1, 1, 36, 36), GameBlockRect = new Rectangle(136, 1, 62, 27), GameYouWonRect = new Rectangle(2, 39, 92, 21), GameYouLostRect = new Rectangle(105, 39, 94, 21); // 球的速度的乘积因数,0.85表示球的运动速度是挡板速度的0.85倍 const float BallSpeedMultiplicator = 0.85f; // 球拍的Y坐标 const float paddlePositionY = 570; /// <summary> /// 砖块的行列数,设置为11行11列 /// </summary> const int NumOfColumns = 11,NumOfRows = 11; #endregion #region 变量 // 球拍、小球、背景等Renderable2DSceneNode对象。 Renderable2DSceneNode paddle, ball, youWon, youLost, background; // 挡板的位置,初始设在中间 float paddlePositionX = 353.5f; // 挡板大小 Vector2 paddleSize = new Vector2(GamePaddleRect.Width, GamePaddleRect.Height); // 小球的位置 Vector2 ballPosition; // 小球的大小 Vector2 ballSize = new Vector2(36f, 36f); // 球的速度 Vector2 ballSpeedVector = new Vector2(0, 0); // 砖块的数组,如果全部被消,则进入下一个关卡。 bool[,] blocksVisible = new bool[NumOfColumns, NumOfRows]; Renderable2DSceneNode[,] blocks=new Renderable2DSceneNode[NumOfColumns, NumOfRows]; // 砖块的位置数组。 Vector2[,] blockPositions = new Vector2[NumOfColumns, NumOfRows]; // 砖块的包围盒数组。 BoundingBox[,] blockBoxes = new BoundingBox[NumOfColumns, NumOfRows]; // 现在的关卡和得分 int level = 1, score = 0; // 玩家按space键后开始关卡 bool pressSpaceToStart = true, lostGame = false; // 显示当前关卡和分数的文字标签 UILabel lblLevel, lblScore; // 音效 readonly SoundEffectInstance BlockKillInstance; readonly SoundEffectInstance VictoryInstance; readonly SoundEffectInstance BallHitInstance; readonly SoundEffectInstance BallLostInstance; #endregion public GameplayScreen(Engine engine):base(engine,"Gameplay") { // 加载素材 Texture2D backgroundTexture = engine.Content.Load<Texture2D>("SpaceBackground"); Texture2D gameTexture = engine.Content.Load<Texture2D>("BreakoutGame"); SpriteFont font = engine.Content.Load<SpriteFont>("Font"); SoundEffect BlockKill = engine.Content.Load<SoundEffect>("BreakoutBlockKill"); BlockKillInstance = BlockKill.CreateInstance(); SoundEffect Victory = engine.Content.Load<SoundEffect>("BreakoutVictory"); VictoryInstance = Victory.CreateInstance(); SoundEffect BallHit = engine.Content.Load<SoundEffect>("PongBallHit"); BallHitInstance = BallHit.CreateInstance(); SoundEffect BallLost = engine.Content.Load<SoundEffect>("PongBallLost"); BallLostInstance = BallLost.CreateInstance(); // 背景 background = new Renderable2DSceneNode(backgroundTexture) { Size = new Vector2(800, 600) }; AddNode(background); // 球拍 paddle = new Renderable2DSceneNode(gameTexture) { Size = paddleSize,RectSourceOrigin = GamePaddleRect, Position = new Vector2(paddlePositionX, paddlePositionY) }; AddNode(paddle); // 小球 ball = new Renderable2DSceneNode(gameTexture) { Size = ballSize, RectSourceOrigin = GameBallRect }; AddNode(ball); // 初始化所有砖块,设置位置和包围盒 for (int y = 0; y < NumOfRows; y++) for (int x = 0; x < NumOfColumns; x++) { blockPositions[x, y] = new Vector2( 15f + 715f * x / (float)(NumOfColumns - 1), 15f + 330f * y / (float)(NumOfRows - 1)); Vector3 pos = new Vector3(blockPositions[x, y], 0); Vector3 blockSize = new Vector3(GameBlockRect.Width, GameBlockRect.Height, 0); blockBoxes[x, y] = new BoundingBox(pos, pos + blockSize); blocks[x, y] = new Renderable2DSceneNode(gameTexture) { Size = new Vector2(GameBlockRect.Width,GameBlockRect.Height), RectSourceOrigin = GameBlockRect, Position = blockPositions[x, y] }; AddNode(blocks[x, y]); } // 显示的youWon信息 youWon = new Renderable2DSceneNode(gameTexture) { Size = new Vector2(GameYouWonRect.Width, GameYouWonRect.Height), RectSourceOrigin = GameYouWonRect, Position = new Vector2(400 - GameYouWonRect.X / 2, 300 - GameYouWonRect.Y / 2), Visible = false }; AddNode(youWon); // 显示的youLost信息 youLost = new Renderable2DSceneNode(gameTexture) { Size = new Vector2(GameYouLostRect.Width, GameYouLostRect.Height), RectSourceOrigin = GameYouLostRect, Position = new Vector2(400 - GameYouLostRect.Width / 2, 300 - GameYouLostRect.Height / 2), Visible = false }; AddNode(youLost); // 显示当前关卡 lblLevel = new UILabel(this.UiManager, font) { Position = new Vector2(10, 10) }; AddNode(lblLevel); // 显示当前得分 lblScore = new UILabel(this.UiManager, font); lblScore.Position = new Vector2(680, 10); AddNode(lblScore); // 开始关卡 StartLevel(); } void StartLevel() { // 随机产生关卡,但一关比一关难 for (int y = 0; y < NumOfRows; y++) for (int x = 0; x < NumOfColumns; x++) { blocksVisible[x, y] = RandomHelper.GetRandomInt(10) < level; blocks[x, y].Visible = blocksVisible[x, y]; } // 后面的关卡会使用更下面行的砖块增加难度 if (level < 6) for (int x = 0; x < NumOfColumns; x++) { blocksVisible[x, NumOfRows - 1] = false; blocks[x, NumOfRows - 1].Visible = false; } if (level < 4) for (int x = 0; x < NumOfColumns; x++) { blocksVisible[x, NumOfRows - 2] = false; blocks[x, NumOfRows - 2].Visible = false; } if (level < 2) for (int x = 0; x < NumOfColumns; x++) { blocksVisible[x, NumOfRows - 3] = false; blocks[x, NumOfRows - 3].Visible = false; } // 暂停小球 ballSpeedVector = Vector2.Zero; // 显示文字 lblLevel.Text = "Level:" + level; lblScore.Text = "Score:" + score; //youLost.Visible = false; // 等待玩家按空格键开始游戏 pressSpaceToStart = true; } /// <summary> /// 新建一个小球 /// </summary> public void StartNewBall() { if (lostGame) { level = 1; score = 0; lostGame = false; StartLevel(); } // 随机产生小球方向,但总是向上方发射 ballSpeedVector = new Vector2(RandomHelper.GetRandomFloat(-1, +1), -1); ballSpeedVector.Normalize(); pressSpaceToStart = false; } public override void Update(GameTime gameTime) { float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds; // 每秒移动400像素的距离 float moveFactorPerSecond = 400f * (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f; // 按键盘左右键移动挡板 if (Input.Keyboard.IsKeyDown(Key.Right)) { paddlePositionX += moveFactorPerSecond; paddle.Position = new Vector2(paddlePositionX, paddlePositionY); } if (Input.Keyboard .IsKeyDown(Key.Left)) { paddlePositionX -= moveFactorPerSecond; paddle.Position = new Vector2(paddlePositionX, paddlePositionY); } // 将球板的运动限制在屏幕中 if (paddlePositionX < 4f) paddlePositionX = 4f; if (paddlePositionX > 705f) paddlePositionX = 705f; // 如果游戏还未开始,则将球放置在球板上。 if (pressSpaceToStart) { ballPosition = new Vector2(paddlePositionX - 20 + paddleSize .X /2, 537f); ball.Position = ballPosition; // 按下键盘空格键开始游戏发射小球。 if (Input.KeyboardKeyJustPressed (Key.Space)) { StartNewBall(); youWon.Visible = false; youLost.Visible = false; } } // 如果游戏开始则进行碰撞检测,更新小球位置 else { // 碰撞检测 CheckBallCollisions(moveFactorPerSecond); // 更新小球位置 ballPosition += ballSpeedVector * moveFactorPerSecond * BallSpeedMultiplicator; ball.Position = ballPosition; // 检测是否所有砖块都被消除,如果是则完成了此关卡。 bool allBlocksKilled = true; for (int y = 0; y < NumOfRows; y++) { for (int x = 0; x < NumOfColumns; x++) { if (blocksVisible[x, y]) { allBlocksKilled = false; break; } } } // 如果游戏胜利则进入下一关卡 if (allBlocksKilled) { // 播放声音 VictoryInstance.Play(); youWon.Visible = true; level++; StartLevel(); } // 小球出下边界 if (ballPosition.Y > 564f) { // 播放声音 BallLostInstance.Play(); // 显示失败信息 youLost.Visible = true; lostGame = true; pressSpaceToStart = true; } } base.Update(gameTime); } /// <summary> /// 碰撞检测 /// </summary> void CheckBallCollisions(float moveFactorPerSecond) { // 如果小球碰到顶部,则向下弹回 if ((ballPosition.Y < 0)&&(ballSpeedVector .Y <0)) { ballSpeedVector.Y = -ballSpeedVector.Y; if (ballPosition.X < 0) ballPosition.X = 0; // 播放声音 BallHitInstance.Play(); } // 如果小球与左右边界碰撞,则反弹小球 if (ballPosition.X < 0 || ballPosition.X > 764) { ballSpeedVector.X = -ballSpeedVector.X; if (ballPosition.X < 0) ballPosition.X = 0; if (ballPosition.X > 764) ballPosition.X = 764; // 播放声音 BallHitInstance.Play(); } // ------------------------- // 检查小球与挡板的碰撞 // ------------------------- // 小球的包围盒 BoundingBox ballBox = new BoundingBox( new Vector3(ballPosition.X, ballPosition.Y, 0), new Vector3(ballPosition.X + ballSize.X, ballPosition.Y + ballSize.Y, 0)); // 挡板的包围盒 BoundingBox paddleBox = new BoundingBox( new Vector3(paddlePositionX, paddlePositionY, 0), new Vector3(paddlePositionX + paddleSize.X, paddlePositionY + paddleSize.Y, 0)); if (ballBox.Intersects(paddleBox)) { // 若小球中心正好撞于挡板中心,则X方向速度保持不变; // 若碰撞位置位于挡板右侧,则越靠右X方向的速度增加(减少)得越多;碰撞在挡板左侧处理方法相同。 ballSpeedVector.X += (18f + ballPosition.X - paddlePositionX-47) / (50f); // 将X方向的反弹限制在-1到+1之间 if (ballSpeedVector.X < -1) ballSpeedVector.X = -1; if (ballSpeedVector.X > 1) ballSpeedVector.X = 1; // 反弹小球 if(ballSpeedVector.Y>0) ballSpeedVector.Y = -ballSpeedVector.Y; // 归一化球的速度矢量 ballSpeedVector.Normalize(); // 播放声音 BallHitInstance.Play(); } // ------------------------- // 检查小球与砖块的碰撞 // ------------------------- for (int y = 0; y < NumOfRows; y++) { for (int x = 0; x < NumOfColumns; x++) { if (blocksVisible[x, y]) { if (ballBox.Intersects(blockBoxes[x, y])) { // 消去砖块 blocksVisible[x, y] = false; blocks[x, y].Visible = false; // 加分 score++; lblScore.Text = "Score:" + score; // 播放声音 BlockKillInstance.Play(); // 反弹小球,但首先需要判断小球撞击了砖块的哪一面。 // 小球是否与砖块的右表面相撞 if (Math.Abs(blockBoxes[x, y].Max.X - ballBox.Min.X) < moveFactorPerSecond) { ballSpeedVector.X = Math.Abs(ballSpeedVector.X); // 将小球回退到一帧前的位置 ballPosition.X += moveFactorPerSecond; } // 小球是否与砖块的左表面相撞 else if (Math.Abs(blockBoxes[x, y].Min.X - ballBox.Max.X) < moveFactorPerSecond) { ballSpeedVector.X = -Math.Abs(ballSpeedVector.X); // 将小球回退到一帧前的位置 ballPosition.X -= moveFactorPerSecond; } // 小球是否与砖块的下表面相撞 else if (Math.Abs(blockBoxes[x, y].Max.Y - ballBox.Min.Y) < moveFactorPerSecond) { ballSpeedVector.Y = Math.Abs(ballSpeedVector.Y); // 将小球回退到一帧前的位置 ballPosition.Y += moveFactorPerSecond; } // 小球是否与砖块的上表面相撞 else if (Math.Abs(blockBoxes[x, y].Min.Y - ballBox.Max.Y) < moveFactorPerSecond) { ballSpeedVector.Y = -Math.Abs(ballSpeedVector.Y); // 将小球回退到一帧前的位置 ballPosition.Y -= moveFactorPerSecond; } else ballSpeedVector *= -1; // 跳出循环,这样一次只处理一个砖块的碰撞 break; } } } } } } }
源代码下载:BreakOut.zip。
文件下载(已下载 2171 次)发布时间:2012/10/11 下午10:25:37 阅读次数:6594