C# WinForm 開發游戲——小雞快跑


首先,了解下WinForm做游戲的基本思路:

做游戲需要的最基本的兩個元素,一個是屏幕,另一個就是在屏幕的移動的對象了。

然后,了解下parint事件,WinForm的對象都是繼承至Control類的,而Control類中包含一個事件PaintEventHandler Paint,paint翻譯過來就是噴繪,類似於繪畫,當容器刷新時,就等於重新噴繪一次圖像,就會觸發此事件。

有了這些,就可以開始做游戲了。

先是定義一個元素(本文是小雞),這個元素包含一張圖片,和X坐標和Y坐標,然后將元素按其坐標,添加進屏幕(WinForm窗口或者其他容器,本文使用PictureBox)中,這樣就屏幕就會在剛才定義的X坐標和Y坐標處,出現一個元素的圖像。

然后,定義一個定時器timer,每30毫秒運行一次,每次運行都要刷新屏幕。自然屏幕刷新就會觸發paint事件啦,本文中的paint事件為GamepictureBox_Paint

那么怎么移動小雞呢?很簡單,在定時器timer的事件里(本文為timer1_Tick)將元素的X坐標改變一下就可以了,然后timer里會進行容器刷新,容器刷新就會觸發

paint事件,然后在paint事件里,重新定位下小雞的X坐標就行了。

不多說了,上代碼。

Form頁面代碼如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Chicken.Properties;

namespace Chicken
{
    public partial class MyG : Form
    {
        Element Chicken;//小雞類
        Road GameRoad;//陸塊類
        public int RoadCount;//陸塊數
        public int Length;//陸塊長度
        int EndX;//設置終點X
        EventHandler TimerHandler;//時間控制手柄
        bool TimerHandlerbool;//是否已傳遞時間手柄
        EventHandler AgainGame;//時間控制手柄
        int GamePicX;
        int GamePicY;
        public MyG()
        {
            InitializeComponent();

            Initial(20, Resources.Bird.Width + 10);//陸塊長度為小雞長度加10 50個陸塊
        }

        private void Initial(int Rcount, int Len)
        {
            AgainGame += new EventHandler(AgainGame_Start);//實例化重新開始手柄
            TimerHandler += new EventHandler(Timer_Enabled);//實例化時間手柄
            RoadCount = Rcount;//陸塊數
            Length = Len;//陸塊長度
            TimerHandlerbool = false;//未已傳遞時間手柄
            Chicken = new Element(0, 100-Resources.Bird.Height);
            GameRoad = new Road(RoadCount, Len);
            GamePicX = 0;
            GamePicY = 0;
            Point p = new Point();
            p.Offset(GamePicX, GamePicY);
            GamepictureBox.Location = p;

        }
        private void InitialLand(Graphics g)
        {
            //Pen pen = new Pen(Color.Green);
            for (int i = 0; i < GameRoad.ListRoad.Count; i++)
            {
                RoadItem Item = GameRoad.ListRoad[i];
                if (Item.type == 1)//如果類型為1 是陸塊是陸地
                {

                    Image img = GameRoad.LandImgList[Item.imageIndex];

                    g.DrawImage(img,
                                    new Rectangle(
                                            Item.start.X,
                                            Item.end.Y,
                                            Item.end.X - Item.start.X,
                                            img.Height
                                            )
                               );//畫陸塊
                }
            }
            EndX = GameRoad.ListRoad.ElementAt(RoadCount - 1).end.X;//設置終點X
            this.GamepictureBox.Width = EndX;
        }
        /// <summary>
        /// 時間控制函數
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Timer_Enabled(object sender, EventArgs e)
        {
            TimerHandler -= new EventHandler(Timer_Enabled);
            timer1.Enabled = false;
            Dead D = new Dead(AgainGame);
            D.Show();
        }
        /// <summary>
        /// 游戲開始控制函數
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AgainGame_Start(object sender, EventArgs e)
        {
            AgainGame -= new EventHandler(AgainGame_Start);
            Initial(RoadCount, Length);
            timer1.Enabled = true;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            //設置屏幕移動
            if ((Chicken.x + this.GamepictureBox.Location.X) > this.Width / 2 &&
                (this.GamepictureBox.Width + this.GamepictureBox.Location.X) > this.Width)
            {
                int OffX = 1;

                if (Chicken.IsSpeedUp)
                {
                    OffX = 2;
                }
                GamePicX = GamePicX - OffX;

                Point p = GamepictureBox.Location;

                p.Offset(GamePicX, GamePicY);
                GamepictureBox.Location = p;




            }

            if (Chicken.x + Chicken.bmp.Width / 2 >= EndX)
            {
                timer1.Enabled = false;
                Replay R = new Replay(AgainGame);
                R.Show();

            }
            int CurrentRoadsIndex = Chicken.x / Length;//獲取當前為第幾個陸塊
            if (CurrentRoadsIndex >= RoadCount) { CurrentRoadsIndex = RoadCount - 1; }//如果大於定義總陸塊數 設置為最大數
            if (CurrentRoadsIndex < 0) { CurrentRoadsIndex = 0; }
            if (GameRoad.ListRoad.ElementAt(CurrentRoadsIndex).type == 0)//如果當前陸塊為空 
            {
                // Y坐標等於空陸塊Y坐標
                if ((Chicken.y + Chicken.bmp.Height) == GameRoad.ListRoad.ElementAt(CurrentRoadsIndex).start.Y)
                {
                    int DepthEndX = GameRoad.ListRoad.ElementAt(CurrentRoadsIndex).end.X;//X下落點為當前陸塊的X
                    if (CurrentRoadsIndex + 1 <= RoadCount - 1)//如果下一個陸塊存在
                    {
                        if (GameRoad.ListRoad.ElementAt(CurrentRoadsIndex + 1).type == 0)//如果下一個陸塊也是空
                        {
                            DepthEndX = GameRoad.ListRoad.ElementAt(CurrentRoadsIndex + 1).end.X;//X下落點為下一個陸塊的X
                        }
                    }
                    if (Chicken.x + Chicken.bmp.Width < DepthEndX)//對象的坐標加對象的寬度 小於空陸塊的尾坐標
                    {

                        Chicken.IsFalling = true;//下降
                        if (!TimerHandlerbool)
                        {
                            Chicken.GetHandler(TimerHandler);//傳遞時間控制手柄
                            TimerHandlerbool = true;

                        }
                    }

                }

            }
            GamepictureBox.Refresh();

        }

        private void GamepictureBox_Paint(object sender, PaintEventArgs e)
        {
            Chicken.Draw(e.Graphics);
            InitialLand(e.Graphics);
        }

        private void MyG_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Right)
            { Chicken.IsRuning = true; }
            if (e.KeyData == Keys.Space && Chicken.IsRuning)
            { Chicken.IsSpeedUp = true; }
            if (e.KeyData == Keys.Up)
            { Chicken.IsJumping = true; }
            int CurrentRoadsIndex = Chicken.point.X / Length;//當前陸塊
            if (e.KeyData == Keys.Left)
            { Chicken.Back = true; }

        }

        private void MyG_KeyUp(object sender, KeyEventArgs e)
        {
            Chicken.IsRuning = false;
            Chicken.IsSpeedUp = false;
            Chicken.Back = false;
        }

        private void GamepictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            Chicken.x = e.X;
            Chicken.y = e.Y;
        }
    }
}

 

元素類,定義幾個變量來控制對象,注釋還算比較多,就不一一解釋了,如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Chicken.Properties;

namespace Chicken
{
    class Element
    {
        public int x; 
        public int y;
        public int JumpHeight = 0;//跳躍高度 
        private bool JumpTop = false;//是否跳到最高點
        public int FallHeight = 0;//跳躍高度 
        public bool FallDepth = false;//是否跳到最高點
        public int BasicSpeed = 1;//基本速度
        public bool IsRuning = false;//是否移動
        public bool Back = false;//是否后退
        public bool IsJumping = false;//是否跳躍   
        public bool IsSpeedUp = false;//是否加速
        public bool IsFalling = false;//是否降落
        public Image bmp;//對象圖形
        public Image img;//對象圖形 暫不使用
        public Point point;//坐標 暫不使用
        public EventHandler TimerHandler;
        public Element(int x, int y)
        {
            bmp = Resources.Bird;
            this.x = x;
            this.y = y;
        }
        public Element(int x,int y,Image img)
        {
            bmp = Resources.Bird;
            this.x = x;
            this.y = y;
            this.img = img;
        }
        public void Draw(Graphics G)
        {
            G.DrawImage(bmp, x, y);
            Move();
        }
        public void Move()
        {
            if (IsFalling)
            {
                IsSpeedUp = false;
                IsJumping = false;
                IsRuning = false;
                if (!FallDepth)
                {
                    this.y += BasicSpeed * 2;
                    FallHeight += BasicSpeed * 2;
                }
                if (FallHeight == 50) 
                { 
                    FallDepth = true; 
                    IsFalling = false;
                    TimerHandler(null, null);
                    
                }//如果下降了50 則下降完成 不在下降
              
                
            }
            if (Back)
            {
                bmp = Resources.BirdBack;
                this.x -= BasicSpeed;
                
            }
            if (IsSpeedUp)
            {
                bmp = Resources.Bird;
                this.x += BasicSpeed*3;
            }
            else if (IsRuning)
            {
                bmp = Resources.Bird;
                this.x +=  BasicSpeed;
            }
            if (IsJumping)
            {
                if (!JumpTop)
                {
                    this.y += BasicSpeed * (-2);
                    JumpHeight += BasicSpeed * (2);
                }
                else
                {
                    this.y += BasicSpeed * 2;
                    JumpHeight += BasicSpeed * (-2);
                }
                if (JumpHeight == 30) { JumpTop = true; }//如果跳躍了30 則跳躍到頂部 不在上升
                if (JumpHeight == 0) { JumpTop = false; IsJumping = false; }//如果回到地面 不在下降 跳躍結束
            }
           
        }
        public void GetHandler(EventHandler TimerHandler)
        {
            this.TimerHandler = TimerHandler;
        }

    }
}

然后,創建陸塊類,如下:

using System;
using System.Collections.Generic;
 
using System.Text;
using System.Drawing;
using Chicken.Properties;

namespace Chicken
{
    class Road
    {
        private Random rand = new Random();
       
        public List<Image> LandImgList = new List<Image>();
        public List<RoadItem> ListRoad = new List<RoadItem>();
        public int RoadY = 100;//陸地的Y坐標
        /// <summary>
        /// 構建陸地
        /// </summary>
        /// <param name="Number">陸塊數量 必須大於2</param>
        /// <param name="Length">陸塊長度</param>
        public Road(int Number, int  Length)
        {
            if (Length < 2)
                return;
            
            RoadItem StartItem = new RoadItem(0, Length);
            StartItem.imageIndex = 0;//選擇陸塊的圖像 0為第一個平地 1為第二個左傾斜 2為第三個右傾斜
            StartItem.type = 1;
            ListRoad.Add(StartItem);//先添加一個陸塊 第一個路塊必須是陸地
           
            for (int i = 0; i < Number - 2; i++)
            {
                int Temp = rand.Next(0, 3);
                int Index = 0;//選擇陸塊的圖像 0為第一個平地 1為第二個左傾斜 2為第三個右傾斜 這里暫時不使用
                int Ang = 0;
                if (Temp == 0)
                {
                    Ang = -20;
                    Index = 2;
                }
                else if (Temp == 1)
                {
                    Ang = 0;
                    Index = 0;
                }
                else
                {
                    Ang = 20;
                    Index = 1;
                }
                RoadItem CItem = new RoadItem(Ang, Length);
                //CItem.imageIndex = Index;獲取隨機陸塊的圖片 這樣獲取Y值需要寫一個一元一次方程獲取
                CItem.imageIndex = 0;//這里設置全為第一個圖像 這樣獲取Y值比較方便
                if (rand.Next(0, 4) == 1)//4分之1的可能性為空陸塊
                    CItem.type = 0;
                else
                    CItem.type = 1;

                ListRoad.Add(CItem);//添加中間的陸塊 添加進陸塊列表
            }

            RoadItem EndItem = new RoadItem(0, Length);
            EndItem.imageIndex = 0;//選擇陸塊的圖像 0為第一個平地 1為第二個左傾斜 2為第三個右傾斜 
            EndItem.type = 1;
            ListRoad.Add(EndItem);//添加最后一個陸塊

            for (int i = 0; i < ListRoad.Count; i++)
            {
                RoadItem DrawItem = ListRoad[i];
                if (i == 0)
                { DrawItem.start = new Point(0, RoadY); }
                else
                { DrawItem.start = ListRoad[i - 1].end; }

                DrawItem.end = new Point(DrawItem.start.X + DrawItem.length, RoadY);
            }
            //為每一個陸塊 定義 起始和終止向量坐標

           
            LandImgList.Add(Resources.land);
         
           //為陸塊使用的圖片列表 賦值
        }
    }
    
    public class RoadItem
    {
        public int angle;
        public int length;//陸塊長度
        public int type;//0為空,1為陸地
        public int imageIndex = 0;//使用的圖片
        /// <summary>
        /// 構建路塊
        /// </summary>
        /// <param name="angle"></param>
        /// <param name="length">陸塊長度</param>
        public RoadItem(int angle, int length)
        {
            this.angle = angle;
            this.length = length;
        }
        public Point start;//陸塊起始坐標
        public Point end;//陸塊終止坐標
       
     
    }
}

輔助類,這兩個類是兩個窗口,我閑MessageBox不太好看,就換了個窗口,但貌似也沒好看到那里去。。。哈哈

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Chicken
{
    public partial class Replay : Form
    {
         EventHandler Again;
         public Replay(EventHandler Again)
        {
            this.Again = Again;
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.Close();
            Again(null, null);
        }
    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Chicken
{
    public partial class Dead : Form
    {
        EventHandler Again;
        public Dead(EventHandler Again)
        {
            this.Again = Again;
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.Close();
            Again(null, null);
        }
    }
}

這兩個類是死亡窗口和重新開始窗口。
源代碼下載地址http://download.csdn.net/detail/kiba518/4355712

源代碼中,和文中的代碼稍微有點不一樣,如果我記得沒錯是這里,如下:

 if (Chicken.x + Chicken.bmp.Width / 2 >= EndX)

是修改,如果雞身的一半以上超過終點,到達終點,游戲結束。這個源碼上傳時沒修改這里。

不過不影響運行啦,但是還有一些小BUG。。

如果想升級這個游戲也很簡答,比如,定義一個炮彈類,隨機發一個。

當炮彈的矩形和小雞的矩形相碰撞了,就死亡啦,矩形相撞有函數的,有興趣的朋友可以自己擴展。

補充:上是跳躍,左右可以移動,空格是加速,鼠標全屏飛。。。。

開發環境:VS2008。

代碼很簡單,可以復制到別的環境中運行。

----------------------------------------------------------------------------------------------------

注:此文章為原創,任何形式的轉載都請聯系作者獲得授權並注明出處!
若您覺得這篇文章還不錯,請點擊下方的推薦】,非常感謝!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM