自己动手写游戏:Flappy Bird

  START:最近闲来无事,看了看一下《C#开发Flappy Bird游戏》的教程,自己也试着做了一下,实现了一个超级简单版(十分简陋)的Flappy Bird,使用的语言是C#,技术采用了快速简单的WindowsForm,图像上主要是采用了GDI+,游戏对象的创建控制上使用了单例模式,现在我就来简单地总结一下。

一、关于Flappy Bird

  《Flappy Bird》是由来自越南的独立游戏开发者Dong Nguyen所开发的作品,游戏中玩家必须控制一只小鸟,跨越由各种不同长度水管所组成的障碍,而这只鸟其实是根本不会飞的……所以玩家每点击一下小鸟就会飞高一点,不点击就会下降,玩家必须控制节奏,拿捏点击屏幕的时间点,让小鸟能在落下的瞬间跳起来,恰好能够通过狭窄的水管缝隙,只要稍一分神,马上就会失败阵亡。简单但不粗糙的8比特像素画面、超级马里奥游戏中的水管、眼神有点呆滞的小鸟和几朵白云,白天夜晚两种模式便构成了游戏的一切。玩家需要不断控制点击屏幕的频率来调节小鸟的飞行高度和降落速度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟不小心擦碰到了管子的话,游戏便宣告结束。

二、游戏设计

2.1 总结游戏印象

  玩过的Flappy Bird的童鞋们应该都对这款游戏有印象,现在我们来看看这款游戏的特点:

  (1)这款游戏的画面很简单:一张背景图,始终就没有变过;

  (2)这款游戏的对象只有俩:一个小鸟(有三种挥动翅膀的状态)以及一对管道(有管道向上和向下两个方向);

    小鸟:①

    管道:

2.2 总结设计思路

  (1)万物皆对象

  在整个游戏中,我们看到的所有内容,我们都可以理解为游戏对象;(在Unity中,GameObject即游戏对象)每一个游戏对象,都由一个单独的类来创建;在游戏中,总共只有两个游戏对象:小鸟和管道,那么我们就可以创建两个类:BirdPipe。但是,我们发现小鸟和管道都有一些共同的属性和方法,例如X,Y轴坐标,长度和宽度,以及绘制(Draw())和移动(Move())的方法,这时我们可以设计一个抽象类,将共有的东西封装起来,减少开发时的冗余代码,提高程序的可扩展性,符合面向对象设计的思路:

  (2)计划生育好

  在整个游戏中,我们的小鸟对象只有一个,也就是说在内存中只需要存一份即可。这时,我们想到了伟大的计划生育政策,于是我们想到了使用单例模式。借助单例模式,可以保证只生成一个小鸟的实例,即为程序提供一个全局访问点,避免重复创建浪费不必要的内存。

  (3)对象的运动

  在整个游戏中,小鸟会受重力默认向下坠落,而用户可以根据点击或按键盘Space键使小鸟向上飞,从图像呈现上其本质就是更改游戏对象在Y轴的位置,使其从下往上移动;而管道则会从屏幕右侧出现,从屏幕左侧消失,又从屏幕右侧出现,再从屏幕左侧消失,一直循环往复。可以看到,从图像呈现上期本质就是更改管道对象在X轴的位置,使其从右往左移动。

  (4)设计流程图

  在整个开发设计过程中,我们可以根据优先级设计开发流程,根据流程一步一步地实现整个游戏。

三、关键代码

3.1 设计抽象父类封装共有属性

    /// <summary>
    /// 游戏对象基类
    /// </summary>
    public abstract class GameObject
    {
        #region 01.构造函数及属性
        public int X { get; set; }

        public int Y { get; set; }

        public int Width { get; set; }

        public int Height { get; set; }

        public GameObject(int x, int y)
        {
            this.X = x;
            this.Y = y;

            this.Width = this.Height = 0;
        }

        public GameObject(int x, int y, int width, int height)
        {
            this.X = x;
            this.Y = y;
            this.Width = width;
            this.Height = height;
        }
        #endregion

        #region 02.抽象方法
        /// <summary>
        /// 抽象方法1:绘制自身
        /// </summary>
        public abstract void Draw(Graphics g);

        /// <summary>
        /// 抽象方法2:移动自身
        /// </summary>
        public abstract void Move();
        #endregion

        #region 03.实例方法
        public Rectangle GetRectangeleArea()
        {
            return new Rectangle(this.X, this.Y, this.Width, this.Height);
        }
        #endregion
    }

  一切皆对象,这里封装了游戏对象小鸟和管道共有的属性,以及两个抽象方法,让小鸟和管道自己去实现。

3.2 设计单例模式减少对象创建

    /// <summary>
    /// 小鸟对象单例模式类
    /// </summary>
    public class SingleObject
    {
        private SingleObject() { }

        private static SingleObject singleInstance;

        public static SingleObject GetInstance()
        {
            if (singleInstance == null)
            {
                singleInstance = new SingleObject();
            }
            return singleInstance;
        }

        public Bird SingleBird
        {
            get;
            set;
        }

        /// <summary>
        /// 添加游戏对象
        /// </summary>
        /// <param name="parentObject">游戏对象父类</param>
        public void AddGameObject(GameObject parentObject)
        {
            if(parentObject is Bird)
            {
                SingleBird = parentObject as Bird;
            }
        }

        /// <summary>
        /// 绘制游戏对象
        /// </summary>
        /// <param name="g"></param>
        public void DrawGameObject(Graphics g)
        {
            SingleBird.Draw(g);
        }
    }

  这里借助单例模式使小鸟实例始终只有一个,实现上主要是将小鸟类和单例模式聚合。

3.3 设计重力辅助类使小鸟能够自动下落

  (1)设计重力辅助类

    /// <summary>
    /// 重力辅助类
    /// </summary>
    public class Gravity
    {
        public static float gravity = 9.8f;

        /// <summary>
        /// s = 1/2*gt^2+vt
        /// </summary>
        /// <param name="speed">速度</param>
        /// <param name="second">时间</param>
        /// <returns>位移量</returns>
        public static float GetHeight(float speed, float time)
        {
            float height = (float)(0.5 * gravity * time * time)
                + speed * time;
            return height;
        }
    }

  在Unity游戏引擎中给游戏对象增加一个刚体组件就可以使游戏对象受重力影响,但是在普通的程序中需要自己设计重力类使游戏对象受重力影响下落。这里使用中学物理的知识:求重力加速度的位移量;

  (2)在定时器事件中使小鸟承受重力影响始终下落

        private void GravityTimer_Tick(object sender, EventArgs e)
        {
            Bird singleBird = SingleObject.GetInstance().SingleBird;
            // Step1:获得小鸟下降的高度
            float height = Gravity.GetHeight(singleBird.CurrentSpeed,
                singleBird.DurationTime * 0.001f);
            // singleBird.DurationTime * 0.001f => 将毫秒转换成帧
            // Step2:获得小鸟下落后的坐标
            int y = singleBird.Y + (int)height;
            // Step3:将新Y轴坐标赋给小鸟
            int min = this.Size.Height - this.pbxGround.Height
                - 60;
            if (y > min)
            {
                // 限定小鸟不要落到地面下
                y = min;
            }
            singleBird.Y = y;
            // Step4:使小鸟按照加速度下降 [ 公式:v=v0+at ]
            singleBird.CurrentSpeed = singleBird.CurrentSpeed
                + Gravity.gravity * singleBird.DurationTime * 0.001f;
        }

  这里重点是将毫秒转换为帧,实现上是使DurationTime*0.001f使速度减慢;

3.4 设计碰撞检测方法使游戏能够终结

  (1)Rectangle的IntersectsWith方法

  在游戏界面中,任何一个游戏对象我们都可以视为一个矩形区域(Rectangle类实例),它的坐标是X轴和Y轴,它还有长度和宽度,可以轻松地确定一个它所在的矩形区域。那么,我们可以通过Rectangle的IntersectsWith方法确定两个Rectangle是否存在重叠,如果有重叠,此方法将返回 true;否则将返回 false。那么,在FlappyBird中主要是判断两种情况:一是小鸟是否飞到边界(屏幕的上方和下方),二是小鸟是否碰到了管道(向上的管道和向下的管道)。

  (2)在定时器事件中循环判断小鸟是否碰到边界或管道

        private void PipeTimer_Tick(object sender, EventArgs e)
        {
            // 移动管道
            this.MovePipeLine();
            // 碰撞检测
            Bird bird = SingleObject.GetInstance().SingleBird;
            if (bird.Y == 0 || bird.Y == this.pbxGround.Height ||
                bird.GetRectangeleArea()
                .IntersectsWith(pipeDown.GetRectangeleArea()) ||
                bird.GetRectangeleArea()
                .IntersectsWith(pipeUp.GetRectangeleArea()))
            {
                // 暂停游戏
                this.PauseGame();
                if (MessageBox.Show("您已挂了,是否购买王胖子的滑板鞋继续畅玩?",
                    "温馨提示", MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    // 重新初始化游戏对象
                    this.InitialGameObjects();
                    // 重新开始游戏
                    this.RestoreGame();
                }
                else
                {
                    MessageBox.Show("您的选择是明智的,王胖子的滑板鞋太挫了!",
                        "温馨提示", MessageBoxButtons.OK,
                        MessageBoxIcon.Information);
                    Environment.Exit(0);
                }
            }
        }

四、开发小结

  从运行效果可以看出,此次DEMO主要完成了几个比较核心的内容:一是小鸟和管道的移动,二是小鸟和边界(最上方和最下方以及管道)的碰撞检测。当然,还有很多核心的内容没有实现,比如:计算通过的管道数量、游戏欢迎界面和结束界面等。希望有兴趣的童鞋可以去继续完善实现,这里提供一个我的Flappy Bird实现仅供参考,谢谢!

参考资料

  赵剑宇,《C#开发史上最虐人游戏-Flappy Bird像素鸟》:http://bbs.itcast.cn/thread-42245-1-1.html

附件下载

  SimpleFlappyBirdDemo:http://pan.baidu.com/s/1hqtcHIs

作者:周旭龙

出处:http://www.cnblogs.com/edisonchou/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

时间: 2024-08-07 23:23:31

自己动手写游戏:Flappy Bird的相关文章

用Phaser来制作一个html5游戏——flappy bird (一)

Phaser是一个简单易用且功能强大的html5游戏框架,利用它可以很轻松的开发出一个html5游戏.在这篇文章中我就教大家如何用Phaser来制作一个前段时间很火爆的游戏:Flappy Bird,希望大家看后也能做出自己的html5游戏.大家可以先点击这里来试玩一下我已经做好的这个游戏,感受一下Phaser的游戏效果,游戏的完整代码我已经放到github上了.支持的浏览器:IE9+.Firefox.Chrome.Opera.Safari以及移动端的能支持html5的浏览器,推荐使用谷歌浏览器,

用原生js写小游戏--Flappy Bird

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> body { margin: 0; padding: 0; } #game { width: 800px; height: 600px; border: 1px

自己动手写游戏:坦克撕逼大战

START:最近在公交车上无聊,于是用平板看了看下载的坦克大战的开发教程,于是在晚上回家后花了两天模仿了一个,现在来总结一下. 一.关于坦克大战 <坦克大战>(Battle City)是1985年日本南梦宫Namco游戏公司开发并且在任天堂FC平台上,推出的一款多方位平面射击游戏.游戏以坦克战斗及保卫基地为主题,属于策略型联机类.同时也是FC平台上少有的内建关卡编辑器的几个游戏之一,玩家可自己创建独特的关卡,并通过获取一些道具使坦克和基地得到强化. 1985年推出的坦克大战(Battle Ci

C语言版flappy bird黑白框游戏

在此记录下本人在大一暑假,2014.6~8这段时间复习C语言,随手编的一个模仿之前很火热的小游戏----flappy bird.代码bug基本被我找光了,如果有哪位兄弟找到其他的就帮我留言下吧,谢谢了! 代码的完美度肯定是不够的,随手编的嘛,找完bug后就没再去想怎样优化它了,毕竟时间有限. 先说下它的设计思路吧,算法方面,基本是纯靠for if 语句加上纯粹的坐标x,y运算实现的,在下面的代码里,将会看到很多阿拉伯数字的加加减减.没有用到链表什么的,当然,我相信,如果用到链表的话,会更简单,代

开源游戏 “Elvish Bird”

简介: 这个游戏是我在今年课余时闲着无聊做的一个冒险类小游戏,总共花了5个工作日才完成,为了游戏的效率,做了很多优化,目前在IE8以上浏览器能够流畅运行,运行时如果屏幕分辨率不兼容,你可以缩放到最佳显示效果.大家可以学习学习,有不足的地方你们可以自己改改,该款游戏是模仿最火爆的游戏“Flappy Bird”的风格. 授权: 本软件使用的是“GPL”开源协议. 截图:    代码说明: 这个游戏是基于HTML4开发的,所以兼容性也挺不错的,电脑配置一般的就可以流畅玩耍了. 代码预览地址: Elvi

cocos2dx-html5 实现网页版flappy bird游戏

我也是第一次使用cocos2d_html5,对js和html5也不熟,看引擎自带的例子和引擎源码,边学边做,如果使用过cocos2d-x的话,完成这个游戏还是十分简单的.游戏体验地址: http://zhoujianghai.github.io/games/flappybird/ 1. 首先去cocos2d-x官网下载Cocos2d-html5-v2.2.2(目前最新版本)压缩包 2. 下载安装WampServer(http://www.wampserver.com/en/),后期在浏览器运行程

飞翔的圆(Flappy Bird)游戏源码完整版

这个源码是一个不错的休闲类的游戏源码,飞翔的圆(Flappy Bird)游戏源码V1.0,本项目是一个仿Flappy Bird的小游戏,只不过是把Flappy Bird里面的鸟替换成了简单的圆.感兴趣的朋友可以研究一下.本项目默认编码GBK. 源码下载:http://code.662p.com/view/9013.html public class LoadingActivity extends Activity { @Override public void onCreate(Bundle s

用Phaser实现Flappy Bird 游戏

How to Make a Flappy Bird in HTML5 With Phaser - Part 1 Flappy Bird is a nice little game with easy to understand mechanics, and I thought it would be a perfect fit for an HTML5 game tutorial for beginners. We are going to make a simplified version o

[MFC] 高仿Flappy bird 桌面版

这是今年年初做的东西,一直没有时间整理,现在拿出来分享下~ 目录 开发背景 开发语言及运行环境 效果展示 游戏框架说明 游戏状态及逻辑说明 经典算法说明 重量级问题解决 开发感想 一.开发背景: flappy bird由一位来自越南河内的独立游戏开发者阮哈东开发,是一款形式简易但难度极高的休闲游戏.简单但不粗糙的8比特像素画面.超级马里奥游戏中的水管.眼神有点呆滞的小鸟和几朵白云便构成了游戏的一切.你需要不断控制点击屏幕的频率来调节小鸟的飞行高度和降落速度,让小鸟顺利地通过画面右端的通道,如果你