1. 程式人生 > >利用GDI+製作Flappy Bird

利用GDI+製作Flappy Bird

上次介紹用GDI+寫了個驗證碼圖片生成器,這次再來介紹下用GDI+寫之前流行過一段時間的小遊戲:Flappy Bird。通過寫這個遊戲再來熟悉下GDI+的一些簡單利用。

         這是一個粗糙的遊戲畫面,大家不要介意啊,畢竟這是美工做的事:


先來分析一下這個遊戲要怎麼寫。遊戲過程是:1、小鳥不停的往下掉,而且越掉越快;2、障礙物柱子不停地出現並往左移動;3、遊戲一開始下面的前進條就不停地轉動。

遊戲規則:1、小鳥的身體不能觸碰障礙物;2、小鳥的身體不能觸及底部及上部;3、每當小鳥穿過一個障礙物時統計通過障礙物的數量加1。

知道了遊戲過程和遊戲規則,我們來編寫遊戲:


由於遊戲中出現的障礙物高度不一,寬度統一。我們可以寫一個障礙物類用於生成障礙物物件與小鳥物件。

class Diamonds
    {
        int _width = 40;
        /// <summary>
        /// 圖形的寬,預設40
        /// </summary>
        public int Width
        {
            get { return _width; }
            set { _width = value; }
        }
        int _heigth;
        /// <summary>
        /// 圖形高
        /// </summary>
        public int Heigth
        {
            get { return _heigth; }
            set { _heigth = value; }
        }
        float _x;
        /// <summary>
        /// X軸座標
        /// </summary>
        public float X
        {
            get { return _x; }
            set//X軸座標不能小於零
            {
                if (value < 0)
                {
                    value = 0;
                }
                _x = value;
            }
        }
        float _y;
        /// <summary>
        /// Y軸座標不能大於300或小於0
        /// </summary>
        public float Y
        {
            get { return _y; }
            set
            {
                if (value > 400)
                {
                    value = 400;
                }
                if (value < 0)
                {
                    value = 0;
                }
                _y = value;
            }
        }
        float _speed = 0;
        /// <summary>
        /// 座標增減的速度
		/// 小鳥物件的初速度
        /// 正數為上升速度
        /// 負數為下降速度
        /// </summary>
        public float Speed
        {
            get { return _speed; }
            set { _speed = value; }
        }
        /// <summary>
        /// 改變X軸座標
        /// </summary>
        public void ChangeX()
        {
            if (this.X == 0)//每次X軸為0時寬度減1
            {
                this.Width -= 1;
            }
            else
            {
                this.X -= 1;
            }
        }
        /// <summary>
        /// 改變Y軸座標
		/// 控制小鳥物件的上升與下降速度
        /// </summary>
        public void ChangeY()
        {
            //改變Y軸的速度先減速降低後加速增加
            this.Y -= this.Speed;
            this.Speed -= 0.2f;
        }
        /// <summary>
        /// 建構函式
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="heigth"></param>
        public Diamonds(float x, float y, int heigth)
        {
            this.X = x;
            this.Y = y;
            this.Heigth = heigth;
     }

生成障礙物及小鳥的類已經寫完了,並且有了左右移動和上下移動的方法。

接下來開始寫遊戲過程:

一、繪製遊戲圖

利用GDI+繪製遊戲圖,利用時間控制元件不停的重繪遊戲圖,以產生動態的效果。

首先申明全域性變數及物件:

//設定障礙物之間的空隙高度
        static int width = 100;
        //判斷繪製哪個前進條
        bool b = true;
        //統計當前通過障礙物的數量
        int count = 0;
        int _maxCount = 0;
        /// <summary>
        /// 記錄最大通過數量
        /// </summary>
        public int MaxCount
        {
            get { return _maxCount; }
            set { _maxCount = value; }
        }
        //建立畫布,大小為300*400
        static Bitmap bmp = new Bitmap(300, 400);
        static Bitmap bmp1 = new Bitmap(300, 20);
        //建立一隻筆,畫小鳥
        static Pen penOne = new Pen(Brushes.Red, 5);
		//建立一支筆,畫前進條
        static Pen penTwo = new Pen(Color.Black, 2);
        //建立鳥
        static Diamonds bird = new Diamonds(100, 190, 15);
        //建立隨機數產生器
        static Random r = new Random();
        //從畫布bmp中建立GDI+物件
        static Graphics gp = Graphics.FromImage(bmp);
        static Graphics gp1 = Graphics.FromImage(bmp1);
        //建立兩個障礙物的物件
        static int up1Height = r.Next(80, 241);
        Diamonds up1 = new Diamonds(300, 0, up1Height);
        Diamonds down1 = new Diamonds(300, width + up1Height, 400 - width - up1Height);
        static int up2Height = r.Next(80, 241);
        Diamonds up2 = new Diamonds(450, 0, up1Height);
        Diamonds down2 = new Diamonds(450, width + up1Height, 400 - width - up1Height);

繪製遊戲圖:
//小鳥飛
            bird.ChangeY();
            //障礙物移動
            up1.ChangeX();
            down1.ChangeX();
            up2.ChangeX();
            down2.ChangeX();
            //判斷遊戲是否結束
            IsEnd();
            //統計通過個數
            PassCount();
            //清除畫面
            gp.Clear(Color.Blue);
            //如果一個障礙物消失,則新出現一個障礙物
            if (up1.Width == 0)
            {
                up1Height = r.Next(80, 241);
                up1 = new Diamonds(300, 0, up1Height);
                down1 = new Diamonds(300, width + up1Height, 400 - width - up1Height);
            }
            //如果一個障礙物消失,則新出現一個障礙物
            if (up2.Width == 0)
            {
                up2Height = r.Next(80, 241);
                up2 = new Diamonds(300, 0, up2Height);
                down2 = new Diamonds(300, width + up2Height, 400 - width - up2Height);
            }

            //畫出各個物件
            //鳥
            gp.DrawRectangle(penOne, bird.X, bird.Y, bird.Width - 10, bird.Heigth);
            //障礙
            gp.FillRectangle(Brushes.Green, up1.X, up1.Y, up1.Width, up1.Heigth);
            gp.FillRectangle(Brushes.Green, down1.X, down1.Y, down1.Width, down1.Heigth);
            gp.FillRectangle(Brushes.Green, up2.X, up2.Y, up2.Width, up2.Heigth);
            gp.FillRectangle(Brushes.Green, down2.X, down2.Y, down2.Width, down2.Heigth);
            //將圖片給圖片框
            pbxImage.Image = bmp;
}
/// <summary>
        /// 畫面前進條運轉
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer2_Tick(object sender, EventArgs e)
        {
            gp1.Clear(Color.Blue);
            //畫前進條
            gp1.DrawLine(penTwo, 0, 0, 300, 0);
            gp1.DrawLine(penTwo, 0, 20, 300, 20);
            if (b)
            {
                for (int i = 0; i < 300; i += 20)
                {
                    gp1.FillRectangle(Brushes.Green, i, 0, 10, 20);
                }
                b = false;
            }
            else
            {
                for (int i = 10; i < 300; i += 20)
                {
                    gp1.FillRectangle(Brushes.Green, i, 0, 10, 20);
                }
                b = true;
            }

            pbxLines.Image = bmp1;

        }
/// <summary>
        /// 遊戲結束條件
        /// </summary>
        private void IsEnd()
        {
            //由於小鳥身體線條粗細問題,寬相應要增加5個畫素左右,高增加2個畫素
            if (((bird.X + 33 >= up1.X) && (bird.X <= up1.X + 40)) && ((bird.Y <= up1.Heigth + 5) || (bird.Y >= up1.Heigth + width - 20)))//當小鳥頭部進入障礙物1則結束
            {
                GameOver();
            }
            else if (((bird.X + 33 >= up2.X) && (bird.X <= up2.X + 40)) && ((bird.Y <= up2.Heigth + 5) || (bird.Y >= up2.Heigth + width - 20)))//當小鳥頭部進入障礙物2則結束
            {
                GameOver();
            }
            else if (bird.Y >= 375 || bird.Y <= 0)//當小鳥碰頭或觸地
            {
                GameOver();
            }

        }

        /// <summary>
        /// 遊戲結束判斷
        /// </summary>
        private void GameOver()
        {
            //顯示控制元件
            timer1.Enabled = false;
            lblEnd.Visible = true;
            btnStart.Visible = true;
            lblMaxCount.Visible = true;
            label1.Visible = true;
            //得到最高分
            if (this.MaxCount < count)
            {
                this.MaxCount = count;
            }

            lblMaxCount.Text = this.MaxCount.ToString();
            return;
        }

        /// <summary>
        /// 計算通過的數量
        /// </summary>
        private void PassCount()
        {
            //當障礙物的末點X軸過了小鳥則通過一個
            if (up1.X == 65 || up2.X == 65)
            {
                count++;
            }
            lblCount.Text = count.ToString();
        }

        /// <summary>
        /// 開始遊戲
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            //開始,結束控制元件不可見
            btnStart.Visible = false;
            lblEnd.Visible = false;
            lblMaxCount.Visible = false;
            label1.Visible = false;
            btnStart.Text = "重新開始";
            //當前數量歸零
            count = 0;
            //重新初始化圖片
            bird = new Diamonds(100, 190, 15);
            up1Height = r.Next(80, 241);
            up1 = new Diamonds(300, 0, up1Height);
            down1 = new Diamonds(300, width + up1Height, 400 - width - up1Height);
            up2Height = r.Next(80, 241);
            up2 = new Diamonds(450, 0, up1Height);
            down2 = new Diamonds(450, width + up1Height, 400 - width - up1Height);
            //啟用
            timer1.Enabled = true;
        }

        /// <summary>
        /// 點選小鳥飛
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pbxImage_Click(object sender, EventArgs e)
        {
            //經過除錯,這個速度可玩性較高
            bird.Speed = 4.5f;
        }
/// <summary>
        /// 雙擊加速上升
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pbxImage_DoubleClick(object sender, EventArgs e)
        {
            bird.Speed = 7;
        }