这些天开始在深圳找工作,想着把从前有些淡忘的技术再温故下。看到尊敬的《传智播客》有一期公开课,讲的是用c#编写flappybird小游戏,也就自己搜了下游戏资源,也来试试看。

其实用到的技术就是传智播客讲的。只不过项目结构和一些逻辑稍微修改了下,更加符合原游戏的特点。

————————再次声明:非本人独创方案————————————

导入资源

窗体背景设置为background图片382*681,然后在窗体上加个picturebox,放入road图片,大小拉伸.

而后加三个timer,依次设置100ms,10ms,10ms

如此,游戏界面搭建完成。

下面是代码结构,所有游戏元素的父类,抽象类:public abstract class GameObj

小鸟类:public class Bird : GameObj

管道类:public class Pipe : GameObj

重力类:public static class Gravity

GameObj类:添加两个抽象方法void Move()  和 void Draw(Graphics g)

下面是整个类的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace flappybird
{
/// <summary>
/// 游戏对象的父类
/// </summary>
public abstract class GameObj
{
public int X { get; set; }
public int Y { get; set; }
public int Height { get; set; }
public int Width { get; set; } public GameObj(int x,int y,int height,int width)
{
this.X = x;
this.Y = y;
this.Height = height;
this.Width = width;
}
public abstract void Move();
public abstract void Draw(Graphics g);
}
}
 

下面是Bird类源码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using flappybird.Properties; namespace flappybird
{
public class Bird : GameObj
{
/// <summary>
/// 各种小鸟形态数组
/// </summary>
private static Image[] imgs ={
Properties.Resources.bird_blue_0,
Properties.Resources.bird_blue_1,
Properties.Resources.bird_blue_2
};
/// <summary>
/// 当前绘制的小鸟形态的索引
/// </summary>
public int BirdIndex { get; set; }
/// <summary>
/// 存储当前下落速度
/// </summary>
public float Speed { get; set; }
/// <summary>
/// 存储已下落持续时间
/// </summary>
public float Time { get; set; } private static Bird bird = null;
/// <summary>
/// 单例模式,确保只实例化一个Bird对象
/// </summary>
/// <returns></returns>
public static Bird GetSingleBird()
{
if (bird == null)
{
bird = new Bird(100, 200, 0);
}
return bird;
}
/// <summary>
/// 调用父类的构造函数
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="birdIndex"></param>
private Bird(int x, int y, int birdIndex)
: base(x, y, imgs[0].Height, imgs[0].Width)
{
this.BirdIndex = birdIndex;
this.Time = 10f;
this.Speed = 0f;
} /// <summary>
/// 重写父类的抽象Draw方法
/// </summary>
public override void Draw(Graphics g)
{
switch (this.BirdIndex)
{
case 0:
g.DrawImage(imgs[0], this.X, this.Y);
break;
case 1:
g.DrawImage(imgs[1], this.X, this.Y);
break;
case 2:
g.DrawImage(imgs[2], this.X, this.Y);
break;
default:
break;
}
this.BirdIndex++;
this.BirdIndex %= 3;
}
/// <summary>
/// 重写父类的抽象Move方法
/// </summary>
public override void Move()
{
this.Y = this.Y <= 60 ? 0 : this.Y - 60;
//this.Y -= 30;
}
/// <summary>
/// 获取图形区域,用于碰撞检测
/// </summary>
/// <returns></returns>
public static Rectangle GetRectangle()
{
return new Rectangle(bird.X, bird.Y, Resources.bird_blue_0.Width, Resources.bird_blue_0.Height);
}
}
}

结合注释,相信大家都能搞懂。。。

Pipe类:

此类中,我定义了一个pipes数组,用来向画面中呈现3列水管(每列包含上下两个水管)

然后用InitPipes()函数初始化。此函数中,用了一种比较讨巧的方法,就是先判断数组中最后一个元素是否为null,类似Bird类中使用的单例模式。

具体代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using flappybird.Properties;
namespace flappybird
{
public class Pipe : GameObj
{
public override void Move()
{
for (int i = 0; i < 3; i++)
{
pipes[i, 0].X -= 1;
pipes[i, 1].X -= 1;
}
// this.X -= 1;
}
public override void Draw(System.Drawing.Graphics g)
{
for (int i = 0; i < 3; i++)
{
//画出上水管
g.DrawImage(Resources.bound_up, pipes[i, 0].X, pipes[i, 0].Y);
//画出下水管
g.DrawImage(Resources.bound_down, pipes[i, 1].X, pipes[i, 1].Y);
} }
private Pipe(int x, int y)
: base(x, y, Resources.bound_down.Height, Resources.bound_down.Width)
{ } private static Pipe[,] pipes = new Pipe[3, 2];
private static void InitPipes()
{
if (pipes[2, 1] == null)
{
Random rand = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < 3; i++)
{
int y = rand.Next(80, 380);
//上水管
pipes[i, 0] = new Pipe(300 + i * 200, -y);
//下水管
pipes[i, 1] = new Pipe(300 + i * 200, -y + Resources.bound_up.Height + 170);
}
}
}
public static Pipe GetPipes(int row, int col)
{
if (row > 1 || col > 2)
{
return null;
}
InitPipes();
return pipes[col, row];
}
public static Rectangle GetRectangle(int row,int col)
{
return new Rectangle(pipes[col, row].X, pipes[col, row].Y,
Resources.bound_up.Width, Resources.bound_up.Height);
}
}
}

然后Gravity类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace flappybird
{
public static class Gravity
{
private const float g=9.8f;
private const float a = 40f;
/// <summary>
/// 获取下落的距离
/// </summary>
/// <param name="time"></param>
/// <param name="speed"></param>
/// <returns></returns>
public static int GetDropHeight(float time,float speed)
{ if (speed<0)
{
return (int)(0.5f * a * time * time + speed * time); }
return (int)(0.5f * g * time * time + speed * time); }
/// <summary>
/// 获取下落后的速度
/// </summary>
/// <param name="time"></param>
/// <param name="speed"></param>
/// <returns></returns>
public static float GetDropSpeed(float time,float speed)
{
if (speed<0)
{
return speed + a * time;
}
return speed + g * time;
}
}
}

定义一个 float a;这个表示小鸟向上飞的时候受到的向下的加速度。之所以这样设计,是为了小鸟向上飞的时候,不是一下就跳到某个高度,让它连续上升,但是减速特快的那种效果。

我看传智播客里赵老师是直接设置个上升高度,给人的感觉是小鸟一下就”飞“上去了,这样设计会简单些,缺点是没有连续效果。

大餐来了,Form1代码:

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 flappybird.Properties;
namespace flappybird
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private bool IsGameOver = false;
private void Form1_Load(object sender, EventArgs e)
{
timer1.Enabled = true;
timer2.Enabled = true;
timer3.Enabled = true; } private void Form1_Paint(object sender, PaintEventArgs e)
{
if (IsGameOver)
{
return;
}
Bird.GetSingleBird().Draw(e.Graphics);
Pipe.GetPipes(0, 0).Draw(e.Graphics);
} private void timer1_Tick(object sender, EventArgs e)
{
if (IsGameOver)
{
return;
}
this.Invalidate();
} private void Form1_MouseClick(object sender, MouseEventArgs e)
{
if (IsGameOver)
{
return;
}
//Bird.GetSingleBird().Move();
Bird.GetSingleBird().Speed = -80f;
} private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{ } private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (IsGameOver)
{
return;
}
if (e.KeyCode == Keys.Space)
{
//Bird.GetSingleBird().Move();
Bird.GetSingleBird().Speed = -80f;
}
}
/// <summary>
/// 计算重力影响
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer2_Tick(object sender, EventArgs e)
{
if (IsGameOver)
{
return;
}
int dropHeight = Gravity.GetDropHeight(Bird.GetSingleBird().Time * 0.01f, Bird.GetSingleBird().Speed); int currentY = Bird.GetSingleBird().Y + dropHeight;
int maxY = ClientRectangle.Height - pictureBox1.Height - Bird.GetSingleBird().Height;
if (currentY >= maxY)
{
Bird.GetSingleBird().Y = maxY;
}
else if (currentY < 0)
{
Bird.GetSingleBird().Y = 0;
}
else
{
Bird.GetSingleBird().Y = currentY;
}
Bird.GetSingleBird().Speed = Gravity.GetDropSpeed(Bird.GetSingleBird().Time * 0.01f, Bird.GetSingleBird().Speed);
} Random rand = new Random(DateTime.Now.Millisecond); private void timer3_Tick(object sender, EventArgs e)
{
if (IsGameOver)
{
return;
}
Pipe.GetPipes(0, 0).Move();
//最靠近左边的管道列,为了下面的碰撞检测做准备。
//因为只有最左边的管道才需要碰撞检测。避免不必要的操作。
int left = 0;
for (int i = 0; i < 3; i++)
{
//若某一列管道左移出了屏幕范围,则把其放到屏幕右端外
//然后再进行左移
if (Pipe.GetPipes(0, i).X < -90)
{
//若i列移出了屏幕,则现在是(i + 1) % 3列在最左侧
left = (i + 1) % 3;
Console.WriteLine(left);
int y = rand.Next(80, 380);
//上面的管道
Pipe.GetPipes(0, i).X = 500;
Pipe.GetPipes(0, i).Y = -y;
//下面的管道
Pipe.GetPipes(1, i).X = 500;
Pipe.GetPipes(1, i).Y = -y + Resources.bound_up.Height + 170;
}
}
bool isInterUp = Bird.GetRectangle().IntersectsWith(Pipe.GetRectangle(0, left));
bool isInterDown = Bird.GetRectangle().IntersectsWith(Pipe.GetRectangle(1, left));
bool isInterGround = Bird.GetRectangle().IntersectsWith(new Rectangle(pictureBox1.Location, pictureBox1.Size));
if (isInterUp||isInterDown||isInterGround)
{
timer1.Enabled = false;
timer2.Enabled = false;
timer3.Enabled = false;
IsGameOver = true;
} }
}
}

结合前面的几个类,这里就留给大家自己体会吧。(绝不是我懒得写说明。哈哈哈)

传送门:http://download.csdn.net/detail/geeking/8255401

c#版flappybird 未完全实现的更多相关文章

  1. qq面板(仿版,未完待续中。。。。)---2017-04-24

    主要实现效果: 1.点击对话,显示对话:点击联系人,显示联系人 2.在联系人界面: 实现好友列表的展开与折叠:(图12) 实现鼠标移到好友列表上的背景颜色的变化:(图3) 选中的好友背景颜色改变(图4 ...

  2. 面向对象 初级版 (Preview) 未完

    概述: 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数里,日后使用无需重复编写,直接调用韩顺即可. 面向对象: 对函数进行分类和封装,让开发'更快更强' 面向对象和面向过程的通 ...

  3. 再谈git的http服务-权限控制gitweb版(未成功)

    截至目前,对gitweb的掌握还没达到最终目标,仅仅实现了通过浏览器来浏览项目,通过git命令仍然未能clone项目.但仍然要记录下来,主要是因为打算暂时放弃这条路,而所收获的一些经验还是要记录下来. ...

  4. java线程详细版(未完待续)

    1. Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一 ...

  5. 洛谷P1276 校门外的树(增强版)未完工

    题目描述 校门外马路上本来从编号0到L,每一编号的位置都有1棵树.有砍树者每次从编号A到B处连续砍掉每1棵树,就连树苗也不放过(记 0 A B ,含A和B):幸运的是还有植树者每次从编号C到D 中凡是 ...

  6. 大数四则运算之加法运算--------C语言版(未考虑负数)

    /* 声明两个字符数组,用于存储大数,声明两个整数型数组便于计算,将字符数组中的元素转换为对应整数存于整数数组中,将低位放在整数数组低位,便于对齐计算 判断是否有进位,计算结果高位先输出,从数组后往前 ...

  7. .NET(C#)实现桌面背景切换(控制台应用程序,windows服务版的未实现成功)

    AdvancedBackgroundJimmy.Program.cs using AdvancedBackground; using Microsoft.Win32; using System; us ...

  8. MyEclipse使用手册(详细版)

    0. 快捷键================================================================================编辑:Ctrl+Shift+ ...

  9. iOS学习之flappyBird游戏的实现

    导言 在本人还是学生的时候,flappyBird这款游戏非常火爆,最后等到Android版的出来之后,也是很痴迷的玩了一把.可是,本人游戏天赋一直平平,几度玩得想摔手机.本文主要介绍如何开发iOS平台 ...

随机推荐

  1. Alpha冲刺报告(11/12)(麻瓜制造者)

    今日已完成 邓弘立: 整合了主页的功能 符天愉: 大致上完成了留言部分的添加,删除,查询功能 江郑: 测试了剩余四个查询,一个添加接口,也搞定了接口说明. 刘双玉: 测试了剩余四个查询,一个添加接口, ...

  2. 028、HTML 标签1列表、图片、表格

    内容简单看一下理解就行了. HTML 是用来描述网页的一种语言.就是用来编写网页的语言 内容:换行.分割,标签属性,编码方式设置,字体标签,特殊符号,列表.图片.表格标签############### ...

  3. moand的编程学形式:一个(高阶)类型包办程序的组织--类型关乎复合

    moand的编程学形式:一个(高阶)类型完成程序的组织. 将类型系统的转换与高阶函数进行了融合,相对于链式编程而言. 类型关乎复合 范畴论与箭头的复合有关.但是并非任意两个箭头都可以复合.一个箭头的目 ...

  4. 容器内部设置JVM的Heap大小

    容器内部利用脚本来获取容器的CGroup资源限制,并通过设置JVM的Heap大小. Docker1.7开始将容器cgroup信息挂载到容器中,所以应用可以从 /sys/fs/cgroup/memory ...

  5. find和find_if

    find函数 是在一个迭代器范围内查找特定元素得函数,可将将他用于任意容器类型得元素.这个函数返回的是所找元素得引用,如果没有找到元素就会返回这个容器得尾迭代器. #include <iostr ...

  6. android camera 摄像头预览画面变形

    问题:最近在处理一下camera的问题,发现在竖屏时预览图像会变形,而横屏时正常.但有的手机则是横竖屏都会变形. 结果:解决了预览变形的问题,同时支持前后摄像头,预览无变形,拍照生成的jpg照片方向正 ...

  7. Kafka设计解析(二十)Apache Flink Kafka consumer

    转载自 huxihx,原文链接 Apache Flink Kafka consumer Flink提供了Kafka connector用于消费/生产Apache Kafka topic的数据.Flin ...

  8. nginx rewrite flag

    1.break可以理解为switch中的break,而last可以理解为continue,一个是跳出server{}的匹配规则,一个还将继续匹配之后的规则. 无论使用last还是break,浏览器上面 ...

  9. day29

    今日内容: 异常处理 元类介绍 元类(通过元类中的__init__方法,改变实例化获得的类) 元类(通过元类中的__call__方法,改变实例化获得的类实例化出来的对象) 1.异常处理: 什么是异常处 ...

  10. jqgrid 配置分页大小及下拉项

    如何配置分页大小的下拉项?显示效果如下: 通过 rowNum 来设置默认分页大小 通过 rowList 来设置分页下拉.   rowList 的值为一个数组,比如:[10,20,30] $(" ...