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

简介: 首先,了解下WinForm做游戏的基本思路: 做游戏需要的最基本的两个元素,一个是屏幕,另一个就是在屏幕的移动的对象了。 然后,了解下parint事件,WinForm的对象都是继承至Control类的,而Control类中包含一个事件PaintEventHandler Paint,paint翻译过来就是喷绘,类似于绘画,当容器刷新时,就等于重新喷绘一次图像,就会触发此事件。

首先,了解下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。

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

 

目录
相关文章
|
8月前
|
XML 测试技术 API
利用C#开发ONVIF客户端和集成RTSP播放功能
利用C#开发ONVIF客户端和集成RTSP播放功能
4478 123
|
物联网 数据处理 C#
C#实现上位机开发,串口通信,读写串口数据并处理16进制数据
C#实现上位机开发,串口通信,读写串口数据并处理16进制数据。在自动化、物联网以及工业控制行业中,上位机开发是一项重要的技能。本教程主要介绍使用C#进行上位机开发,重点在于串口通信和数据处理。
2713 82
|
前端开发 JavaScript 安全
C#一分钟浅谈:Blazor WebAssembly 开发
Blazor WebAssembly 是一个客户端框架,允许开发者使用C#和Razor语法构建Web应用。本文介绍了Blazor WebAssembly的基本概念、常见问题及解决方案,包括路由配置、数据绑定、异步操作、状态管理和性能优化等方面的内容,并分享了一些易错点及如何避免的方法。希望这些内容能帮助你在Blazor WebAssembly开发中少走弯路,提高开发效率。
535 51
|
SQL 小程序 API
如何运用C#.NET技术快速开发一套掌上医院系统?
本方案基于C#.NET技术快速构建掌上医院系统,结合模块化开发理念与医院信息化需求。核心功能涵盖用户端的预约挂号、在线问诊、报告查询等,以及管理端的排班管理和数据统计。采用.NET Core Web API与uni-app实现前后端分离,支持跨平台小程序开发。数据库选用SQL Server 2012,并通过读写分离与索引优化提升性能。部署方案包括Windows Server与负载均衡设计,确保高可用性。同时针对API差异、数据库老化及高并发等问题制定应对措施,保障系统稳定运行。推荐使用Postman、Redgate等工具辅助开发,提升效率与质量。
538 0
|
缓存 算法 安全
精选10款C#/.NET开发必备类库(含使用教程),工作效率提升利器!
精选10款C#/.NET开发必备类库(含使用教程),工作效率提升利器!
683 12
|
Linux C# iOS开发
开源GTKSystem.Windows.Forms框架让C# Winform支持跨平台运行
开源GTKSystem.Windows.Forms框架让C# Winform支持跨平台运行
439 12
|
缓存 C# 开发者
C# 一分钟浅谈:Blazor Server 端开发
本文介绍了 Blazor Server,一种基于 .NET 的 Web 开发模型,允许使用 C# 和 Razor 语法构建交互式 Web 应用。文章从基础概念、创建应用、常见问题及解决方案、易错点及避免方法等方面详细讲解,帮助开发者快速上手并提高开发效率。
493 2
|
开发框架 缓存 .NET
C# 一分钟浅谈:Blazor Server 端开发
Blazor Server 是基于 ASP.NET Core 的框架,允许使用 C# 和 Razor 语法构建交互式 Web 应用。本文介绍 Blazor Server 的基本概念、快速入门、常见问题及解决方案,帮助开发者快速上手。涵盖创建应用、基本组件、数据绑定、状态管理、跨组件通信、错误处理和性能优化等内容。
1016 1
|
8月前
|
XML 前端开发 C#
C#编程实践:解析HTML文档并执行元素匹配
通过上述步骤,可以在C#中有效地解析HTML文档并执行元素匹配。HtmlAgilityPack提供了一个强大而灵活的工具集,可以处理各种HTML解析任务。
349 19
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
404 3