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 = ;
} 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
- ;
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 == || 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();
}
}
}

四、开发小结

  从运行效果可以看出,此次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/

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

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

  1. 在code.org上自己写一个flappy bird游戏

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:在code.org上自己写一个flappy bird游戏.

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

    在上一篇教程中我们完成了boot.preload.menu这三个state的制作,下面我们就要进入本游戏最核心的一个state的制作了.play这个state的代码比较多,我不会一一进行说明,只会把一 ...

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

    Phaser是一个简单易用且功能强大的html5游戏框架,利用它可以很轻松的开发出一个html5游戏.在这篇文章中我就教大家如何用Phaser来制作一个前段时间很火爆的游戏:Flappy Bird,希 ...

  4. canvas小游戏——flappy bird

    前言 如果说学编程就是学逻辑的话,那锻炼逻辑能力的最好方法就莫过于写游戏了.最近看了一位大神的fly bird小游戏,感觉很有帮助.于是为了寻求进一步的提高,我花了两天时间自己写了一个canvas版本 ...

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

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. flappy bird游戏源代码揭秘和下载

    转:http://blog.csdn.net/touchsnow/article/details/19071961 背景: 最近火爆全球的游戏flappy bird让笔者叹为观止,于是花了一天的时间山 ...

  7. C语言版flappy bird黑白框游戏

    在此记录下本人在大一暑假,2014.6~8这段时间复习C语言,随手编的一个模仿之前很火热的小游戏----flappy bird.代码bug基本被我找光了,如果有哪位兄弟找到其他的就帮我留言下吧,谢谢了 ...

  8. 开源游戏 “Elvish Bird”

    简介: 这个游戏是我在今年(2014/03)课余时闲着无聊做的一个冒险类小游戏,总共花了5个工作日才完成,为了游戏的效率,做了很多优化,目前在IE8以上浏览器能够流畅运行,运行时如果屏幕分辨率不兼容, ...

  9. 闲扯游戏编程之html5篇--山寨版《flappy bird》源码

    新年新气象,最近事情不多,继续闲暇学习记点随笔,欢迎拍砖.之前的〈简单游戏学编程语言python篇〉写的比较幼稚和粗糙,且告一段落.开启新的一篇关于javascript+html5的从零开始的学习.仍 ...

随机推荐

  1. Linux系统目录结构以及简单说明

    Linux系统目录结构以及简单说明 linux目录图: / root --- 启动Linux时使用的一些核心文件.如操作系统内核.引导程序Grub等. home --- 存储普通用户的个人文件 ftp ...

  2. vmware中虚拟机与主机ping不通,桥接模式,IP地址在同一网段,无法互ping!

    现象描述:网卡选用的桥接模式,IP地址在同一个网段,虚拟机内部可以正常上网,但是Guest OS和Host OS无法互ping! 原因:虚拟机里的防火墙没有关闭,导致禁用ping功能. 解决方法:关闭 ...

  3. Denormalization

    Denormalization In computing, denormalization is the process of attempting to optimize the read perf ...

  4. 用WinForm写的员工考勤项目!!!!!!

    先说几句,作为一个还在学习的程序员,掌握的知识有限:但我利用自身所学,给一些像我一样还在学习的码农提供我的绵薄之力! 写的不好,但是尽力了,希望大牛指点.多多吐槽!!! 好了开始说项目需求: 实现新增 ...

  5. Linux学习笔记(12)-进程间通信|匿名管道

    Linux的进程间通信有几种方式,包括,管道,信号,信号灯,共享内存,消息队列和套接字等-- 现在一个个的开始学习! ----------------------------------------- ...

  6. 求50-100内的素数(java)

    实现代码: public class sushu { public static void main(String[] args) { for(int i=50 ; i<=100; i++){ ...

  7. hoops暂时用过的一些方法

    (中文全部为谷歌翻译) HC_EXTERNAL void HC_CDECL HC_Show_Shell HC_PROTO ((HC_KEY, int *, HC_POINT *, int *, int ...

  8. Git小记

    Git简~介 Git是一个分布式版本控制系统,其他的版本控制系统我只用过SVN,但用的时间不长.大家都知道,分布式的好处多多,而且分布式已经包含了集中式的几乎所有功能.Linus创造Git的传奇经历就 ...

  9. 【BZOJ3943】[Usaco2015 Feb]SuperBull 最小生成树

    [BZOJ3943][Usaco2015 Feb]SuperBull Description Bessie and her friends are playing hoofball in the an ...

  10. 介绍两个挺好用的基于Jquery的上传工具

    一.ajaxFileUpload 二.fineUploader