zoukankan      html  css  js  c++  java
  • 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。

    代码很简单,可以复制到别的环境中运行。

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

    注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
    若您觉得这篇文章还不错,请点击下方的推荐】,非常感谢!

     

  • 相关阅读:
    JavaScript中get和set访问器的实现
    Vue render 函数和JSX语法的使用
    vue项目将第三方包以cdn的方式引入页面不显示
    今天帮忙HMF这位ui大神(傻)解决的小问题。
    SET
    webpack 折腾
    sass折腾
    gulp折腾
    css3
    js递归函数
  • 原文地址:https://www.cnblogs.com/kiba/p/2545280.html
Copyright © 2011-2022 走看看