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。

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

 

目录
相关文章
|
16天前
|
前端开发 JavaScript 安全
C#一分钟浅谈:Blazor WebAssembly 开发
Blazor WebAssembly 是一个客户端框架,允许开发者使用C#和Razor语法构建Web应用。本文介绍了Blazor WebAssembly的基本概念、常见问题及解决方案,包括路由配置、数据绑定、异步操作、状态管理和性能优化等方面的内容,并分享了一些易错点及如何避免的方法。希望这些内容能帮助你在Blazor WebAssembly开发中少走弯路,提高开发效率。
88 51
|
14天前
|
开发框架 缓存 .NET
C# 一分钟浅谈:Blazor Server 端开发
Blazor Server 是基于 ASP.NET Core 的框架,允许使用 C# 和 Razor 语法构建交互式 Web 应用。本文介绍 Blazor Server 的基本概念、快速入门、常见问题及解决方案,帮助开发者快速上手。涵盖创建应用、基本组件、数据绑定、状态管理、跨组件通信、错误处理和性能优化等内容。
29 1
|
15天前
|
缓存 C# 开发者
C# 一分钟浅谈:Blazor Server 端开发
本文介绍了 Blazor Server,一种基于 .NET 的 Web 开发模型,允许使用 C# 和 Razor 语法构建交互式 Web 应用。文章从基础概念、创建应用、常见问题及解决方案、易错点及避免方法等方面详细讲解,帮助开发者快速上手并提高开发效率。
39 2
|
24天前
|
测试技术 Go C#
C#一分钟浅谈:ReSharper 插件增强开发效率
【10月更文挑战第25天】ReSharper 是 JetBrains 开发的一款 Visual Studio 插件,旨在提高 .NET 开发者的生产力。它通过代码分析、重构、导航等功能,帮助开发者避免常见错误,提升代码质量和开发效率。本文将通过具体代码案例,详细介绍 ReSharper 的常见功能及其应用。
37 1
|
1月前
|
C# Python
使用wxpython开发跨平台桌面应用,对wxpython控件实现类似C#扩展函数处理的探究
【10月更文挑战第30天】使用 `wxPython` 开发跨平台桌面应用时,可以通过创建辅助类来模拟 C# 扩展函数的功能。具体步骤包括:1. 创建辅助类 `WxWidgetHelpers`;2. 在该类中定义静态方法,如 `set_button_color`;3. 在应用中调用这些方法。这种方法提高了代码的可读性和可维护性,无需修改 `wxPython` 库即可为控件添加自定义功能。但需要注意显式调用方法和避免命名冲突。
|
2月前
|
JSON C# 开发者
C#语言新特性深度剖析:提升你的.NET开发效率
【10月更文挑战第15天】C#语言凭借其强大的功能和易用性深受开发者喜爱。随着.NET平台的演进,C#不断引入新特性,如C# 7.0的模式匹配和C# 8.0的异步流,显著提升了开发效率和代码可维护性。本文将深入探讨这些新特性,助力开发者在.NET开发中更高效地利用它们。
36 1
|
2月前
|
开发框架 NoSQL MongoDB
C#/.NET/.NET Core开发实战教程集合
C#/.NET/.NET Core开发实战教程集合
|
2月前
|
设计模式 程序员 C#
C# 使用 WinForm MDI 模式管理多个子窗体程序的详细步骤
WinForm MDI 模式就像是有超能力一般,让多个子窗体井然有序地排列在一个主窗体之下,既美观又实用。不过,也要小心管理好子窗体们的生命周期哦,否则一不小心就会出现一些意想不到的小bug
125 0
|
23天前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
31 3
|
3月前
|
API C#
C# 一分钟浅谈:文件系统编程
在软件开发中,文件系统操作至关重要。本文将带你快速掌握C#中文件系统编程的基础知识,涵盖基本概念、常见问题及解决方法。文章详细介绍了`System.IO`命名空间下的关键类库,并通过示例代码展示了路径处理、异常处理、并发访问等技巧,还提供了异步API和流压缩等高级技巧,帮助你写出更健壮的代码。
45 2