打砖块

此游戏代码的核心部分来自于§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

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号