前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C# WinForm 开发游戏——小鸡快跑

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

作者头像
Kiba518
发布2019-05-25 15:26:06
1K0
发布2019-05-25 15:26:06
举报
文章被收录于专栏:Kiba518Kiba518

首先,了解下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页面代码如下:

代码语言:javascript
复制
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;
        }
    }
}

元素类,定义几个变量来控制对象,注释还算比较多,就不一一解释了,如下:

代码语言:javascript
复制
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;
        }

    }
}

然后,创建陆块类,如下:

代码语言:javascript
复制
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不太好看,就换了个窗口,但貌似也没好看到那里去。。。哈哈

代码语言:javascript
复制
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);
        }
    }
}
代码语言:javascript
复制
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。

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2012-06-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档