C# WinForm 开发游戏——小鸡快跑

小编 2026-06-24 阅读:1682 评论:0
首先,了解下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。

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

 

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表