flappy bird
flappy bird_百度百科
《flappy bird》是一款由来自
越南
的独立
游戏开发者
Dong Nguyen所开发的作品,
游戏
于2013年5月24日上线,并在2014年2月突然暴红。2014年2月,《Flappy Bird》被开发者本人从
苹果
及谷歌应用商店(
Google Play
)撤下。2014年8月份正式回归
App Store
,正式加入Flappy迷们期待已久的多人对战模式。游戏中
玩家
必须控制一只小鸟,跨越由各种不同长度水管所组成的障碍。
中文名
飞扬的小鸟
原版名称
Flappy Bird
别 名
像素鸟、下坠的小鸟、笨鸟
游戏类型
益智
休闲
游戏平台
iOS
、Android
开发商
dotGEARS
发行日期
2013年5月24日
制作人
阮哈东
玩家人数
单人
游戏画面
像素复古
最新版本
1.3
主要角色
小鸟
在《FlappyBird》这款游戏中,玩家只需要用一根手指来操控,点击触摸屏幕,小鸟就会往上飞,不断的点击就会不断的往高处飞。放松手指,则会快速下降。所以玩家要控制小鸟一直向前飞行,然后注意躲避途中高低不平的管子。
1、在游戏开始后,点击屏幕,要记住是有间歇的点击屏幕,不要让小鸟掉下来。
2、尽量保持平和的心情,点的时候不要下手太重,尽量注视着小鸟。
3、游戏的得分是,小鸟安全穿过一个柱子且不撞上就是1分。当然撞上就直接挂掉,只有一条命。
整体来看,Flappy Bird的那只鸟无非将Piou Piou的卡通小鸟改为了像素风格,颜色也一样,而且玩法上,实在是太过相似。Flappy Bird改动最大的是把核心玩法导向了完全的自虐模式,为此在玩法上相比Piou Piou做了简化,这也是为何其能获得病毒式下载量的重要原因,而Flappy Bird是手机游戏支持
iOS
、
android
。
电脑版
Flappy Bird电脑版(又称笨鸟先飞电脑版)是一款来自
iOS
平台的小游戏,该游戏是由一名越南游戏制作者开发独自开发而成,玩法极为简单,但是玩家千万不能小瞧这款作品,就是这么一款单纯的小游戏在短短几天之内几乎占据了80%的欧美手机游戏用户,并且这一数字依然在飙涨。
Web App版
虽然《Flappy Bird》已经被作者选择下架,但在第三方市场还能找到下载,另有网友开发了
Web App
版《Flappy Bird》,没有下载的朋友可以直接使用
浏览器
进行游戏。
微信、QQ空间版
与原版几乎完全一致的Flappy Bird手机微信客户端朋友圈与QQ空间版本也出现在
互联网
上。有网友通过分享某网站上的Flappy Bird页面,实现了在
微信
朋友圈
和
QQ空间
中的无缝运行。
WindowsPhone版
作者正在开发中的
wp
版由于作者停止了开发未有官方版,
winphone
玩家可以切换美国区来下载一模一样的第三方应用。
开发者阮哈东
Flappy Bird的开发者——越南游戏工程师
阮哈东
,毕业于越南河内科技大学。他开发了接近30款游戏,曾经获得过几个游戏开发奖项,而Flappy Bird这个难到让玩家崩溃的小游戏最终让其一举成名。
2013年4月
阮哈东
(Dong Nguyen)父母一起住在
越南
河内
的家里,做着为计程车定位设备
编程
的日常工作,节假日和周末则用于制作一款手机游戏。他希望这款手机游戏简单而富有挑战性,具有他从小所玩的
任天堂
游戏精髓。游戏目标是让一只暴眼而大嘴的膨胀小鸟在一系列绿色垂直管间飞行。玩家点触屏幕的速度越快,小鸟飞得就越高,他将其称为《Flappy Bird》。
2013年5月24日在
苹果
App Store
上线,Nguyen推出的是免费模式,希望通过游戏内置广告获得每月数百美元的收益。
2014年2月份《Flappy Bird》在100多个国家/地区的榜单一跃登顶,下载量突破5000万次。
爆红手游《Flappy Bird》的作者Dong Nguyen在
上放出狠话,说要22小时之后把《下坠的小鸟》从
App Store
和
安卓商店
上撤下,这款游戏也确实已经下架了,国内用户仍可通过其他软件免费安装下载。
flappy bird
2014年2月9日,
阮哈东
认为媒体的报道高估了这款游戏,这也是他所不愿看到的,这样的现况让他倍感压力。在
消息中称《Flappy Bird》将撤下。声称坚决不会将游戏出售,自己以后也会继续做游戏。10日,手机小游戏《Flappy Bird》从
苹果
和谷歌应用商店(
Google Play
)下线。
[1-3]
2014年2月9日向
美国专利商标局
(USPTO)正式提出了商标申请。由于Flappy Bird这款游戏的最初开发者
阮哈东
已经表示放弃了这款游戏,因此OneClick公司极有可能获得Flappy Bird商标的所有权。而这位半路杀出来的“程咬金”甚至已经完成了自己的版本,并且放出了运行演示视频。
而一旦OneClick公司成功的获得Flappy Bird商标的所有权,那么这款游戏将很有可能重新在各大应用商店中“复活”。不过我们不知道这款游戏的原版开发者阮哈东看到这条消息会是怎样的心理。毕竟从游戏运行视频中我们看到,新版的Flappy Bird可以说与下架之前的原版一模一样,除了配色稍深,在玩法和界面布局上几乎完全相同。
2014年3月,Flappy Bird的开发者DongNguyen似乎想让FlappyBird游戏重新上架苹果商店,DongNguyen于本周早些时候与滚石公司商讨他的杰作—FlappyBird。表示将在游戏中加入“请休息一会”的提示。
因为
苹果公司
的开发商协议,苹果开发者协议规定:如果你将应用从苹果商店删除,那么商品编号或名称将不能被同一机构重新使用。如果Nguyen确实想让他的游戏重返
iOS系统
,他将需要一个全新的游戏名,而且对于那些已经下载了FlappyBird的5000万用户,他已无权访问他们的设备平台。
2014年5月14日,阮哈东正式决定:Flappy Bird将在8月份正式回归
App Store
。在Flappy Bird新作中,还将正式加入Flappy迷们期待已久的多人对战模式,作者
阮哈东
还表示不排除会带来“更多的惊喜”。
参考资料
Play Flappy Bird
Play flappy bird here online for free. Click on the screen, or use your spacebar to getstarted. Fly the bird as far as you can without hitting a pipe.
Make sure to check the
blog
for additionalinformation and updates.Site and html5 game created by
@mxmcd
Flappy Bird在线玩,flappy bird像素鸟,flappy bird中文
Loading...
Flappy Bird
Flappy Bird
Flappy Bird is an arcade game where you control a likeable bird that has to fly through many obstacles all made up of pipes.
The mechanics are very simple: you have to tap the screen so that the bird flaps its wings, trying to keep a steady rhythm in order to pass through the pipes scattered through its path. It won't be at all easy to do.
This is a frustrating game to some people, as if you hit a pipe, your bird falls and you restart the game. However, some people believe that not fully focusing on the game or focusing only on the bottom pipe helps you get a higher score.
It will be a challenge for you right?
Have fun!
Control
The controls in Flappy Bird are simple - you just tap to flap - but the game is so hard that you can spend an hour practicing and you'll still only have made it through five or six gaps.
python之flappy bird(飞扬的小鸟)小游戏分享,内附源码哦~_迢迢x的博客-CSDN博客_python飞翔的小鸟源代码
导语:
哈喽,哈喽~今天小编又来分享
小游戏了——flappy bird(飞扬的小鸟)
,这个游戏非常的经典,游戏中玩家必须控制一只小鸟,跨越由各种不同长度水管所组成的障碍。这个游戏能对于小编来说还是有点难度的。开发工具:👉
Python
版本:
3.6.4相关模块:
pygame模块;
以及一些python自带的模块。
环境搭建
安装Python并添加到环境变量,pip安装需要的相关模块即可。
运行视频:
正文:
首先,我们来写个开始界面,让他看起来更像个游戏一些。效果大概是这样的:
原理也简单,关键点有三个:(1)下方深绿浅绿交替的地板不断往左移动来制造小鸟向前飞行的假象;(2)每过几帧切换一下小鸟的图片来实现小鸟翅膀扇动的效果:
(3)有规律地改变小鸟竖直方向上的位置来实现上下移动的效果。
具体而言,代码实现如下:
'''显示开始界面'''def startGame(screen, sounds, bird_images, other_images, backgroud_image, cfg): base_pos = [0, cfg.SCREENHEIGHT*0.79] base_diff_bg = other_images['base'].get_width() - backgroud_image.get_width() msg_pos = [(cfg.SCREENWIDTH-other_images['message'].get_width())/2, cfg.SCREENHEIGHT*0.12] bird_idx = 0 bird_idx_change_count = 0 bird_idx_cycle = itertools.cycle([0, 1, 2, 1]) bird_pos = [cfg.SCREENWIDTH*0.2, (cfg.SCREENHEIGHT-list(bird_images.values())[0].get_height())/2] bird_y_shift_count = 0 bird_y_shift_max = 9 shift = 1 clock = pygame.time.Clock() while True: for event in pygame.event.get(): if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE or event.key == pygame.K_UP: return {'bird_pos': bird_pos, 'base_pos': base_pos, 'bird_idx': bird_idx} sounds['wing'].play() bird_idx_change_count += 1 if bird_idx_change_count % 5 == 0: bird_idx = next(bird_idx_cycle) bird_idx_change_count = 0 base_pos[0] = -((-base_pos[0] + 4) % base_diff_bg) bird_y_shift_count += 1 if bird_y_shift_count == bird_y_shift_max: bird_y_shift_max = 16 shift = -1 * shift bird_y_shift_count = 0 bird_pos[-1] = bird_pos[-1] + shift screen.blit(backgroud_image, (0, 0)) screen.blit(list(bird_images.values())[bird_idx], bird_pos) screen.blit(other_images['message'], msg_pos) screen.blit(other_images['base'], base_pos) pygame.display.update() clock.tick(cfg.FPS)
点击空格键或者↑键进入主程序。对于主程序,在进行了必要的初始化工作之后,在游戏开始界面中实现的内容的基础上,主要还需要实现的内容有以下几个部分:
(1) 管道和深绿浅绿交替的地板不断往左移来实现小鸟向前飞行的效果;
(2) 按键检测,当玩家点击空格键或者↑键时,小鸟向上做加速度向下的均减速直线运动直至向上的速度衰减为0,否则小鸟做自由落体运动(实现时为了方便,可以认为在极短的时间段内小鸟的运动方式为匀速直线运动);
(3) 碰撞检测,当小鸟与管道/游戏边界碰撞到时,游戏失败并进入游戏结束界面。注意,为了碰撞检测更精确,我们使用:
pygame.sprite.collide_mask
管道:
(4) 进入游戏后,随机产生两对管道,并不断左移,当最左边的管道快要因为到达游戏界面的左边界而消失时,重新生成一对管道(注意不要重复生成);
(5) 当小鸟穿越一个上下管道之间的缺口时,游戏得分加一(注意不要重复记分)。
计分表
这里简单贴下主程序的源代码吧:
# 进入主游戏score = 0bird_pos, base_pos, bird_idx = list(game_start_info.values())base_diff_bg = other_images['base'].get_width() - backgroud_image.get_width()clock = pygame.time.Clock()# --管道类pipe_sprites = pygame.sprite.Group()for i in range(2): pipe_pos = Pipe.randomPipe(cfg, pipe_images.get('top')) pipe_sprites.add(Pipe(image=pipe_images.get('top'), position=(cfg.SCREENWIDTH+200+i*cfg.SCREENWIDTH/2, pipe_pos.get('top')[-1]))) pipe_sprites.add(Pipe(image=pipe_images.get('bottom'), position=(cfg.SCREENWIDTH+200+i*cfg.SCREENWIDTH/2, pipe_pos.get('bottom')[-1])))# --bird类bird = Bird(images=bird_images, idx=bird_idx, position=bird_pos)# --是否增加pipeis_add_pipe = True# --游戏是否进行中is_game_running = Truewhile is_game_running: for event in pygame.event.get(): if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE or event.key == pygame.K_UP: bird.setFlapped() sounds['wing'].play() # --碰撞检测 for pipe in pipe_sprites: if pygame.sprite.collide_mask(bird, pipe): sounds['hit'].play() is_game_running = False # --更新小鸟 boundary_values = [0, base_pos[-1]] is_dead = bird.update(boundary_values, float(clock.tick(cfg.FPS))/1000.) if is_dead: sounds['hit'].play() is_game_running = False # --移动base实现小鸟往前飞的效果 base_pos[0] = -((-base_pos[0] + 4) % base_diff_bg) # --移动pipe实现小鸟往前飞的效果 flag = False for pipe in pipe_sprites: pipe.rect.left -= 4 if pipe.rect.centerx < bird.rect.centerx and not pipe.used_for_score: pipe.used_for_score = True score += 0.5 if '.5' in str(score): sounds['point'].play() if pipe.rect.left < 5 and pipe.rect.left > 0 and is_add_pipe: pipe_pos = Pipe.randomPipe(cfg, pipe_images.get('top')) pipe_sprites.add(Pipe(image=pipe_images.get('top'), position=pipe_pos.get('top'))) pipe_sprites.add(Pipe(image=pipe_images.get('bottom'), position=pipe_pos.get('bottom'))) is_add_pipe = False elif pipe.rect.right < 0: pipe_sprites.remove(pipe) flag = True if flag: is_add_pipe = True # --绑定必要的元素在屏幕上 screen.blit(backgroud_image, (0, 0)) pipe_sprites.draw(screen) screen.blit(other_images['base'], base_pos) showScore(screen, score, number_images) bird.draw(screen) pygame.display.update() clock.tick(cfg.FPS)
游戏结束
假如我们的主角真的一个不小心如我们所料的撞死在了钢管上(往上翻,就在游戏开始那里),那就表示gameOver();
'''游戏结束界面'''def endGame(screen, sounds, showScore, score, number_images, bird, pipe_sprites, backgroud_image, other_images, base_pos, cfg): sounds['die'].play() clock = pygame.time.Clock() while True: for event in pygame.event.get(): if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE or event.key == pygame.K_UP: return boundary_values = [0, base_pos[-1]] bird.update(boundary_values, float(clock.tick(cfg.FPS))/1000.) screen.blit(backgroud_image, (0, 0)) pipe_sprites.draw(screen) screen.blit(other_images['base'], base_pos) showScore(screen, score, number_images) bird.draw(screen) pygame.display.update() clock.tick(cfg.FPS)
结尾:
需要完整的项目源码的可以私信我即可哟!或者点击这行
蓝色字体
!这期游戏分享就到这结束啦喜欢的友友们动手试试看哦!制作不易记得给小编三连噢~家人们的支持是小编更新最大的动力
FlappyBird OG - Play FlappyBird OG online on Agame
Wij begrijpen dat het belangrijk is dat minderjarigen op een veilige manier gebruik maken van het internet. We beschouwen het ook als onze verantwoordelijkheid om de website kindvriendelijk te maken en inhoud te tonen die bij jouw leeftijdsgroep past. Als je aangeeft dat je nog geen 18 jaar of ouder bent, zal je daarom automatisch geen advertenties te zien krijgen die bedoeld zijn voor een oudere doelgroep.
Jonger dan 18
18 jaar of ouder
手把手教你完美复刻《flappy bird》 - 知乎
本文节选自《趣学Python游戏编程》。
我们将学习制作曾经风靡一时的移动游戏——Flappy Bird,游戏中玩家需要控制一只小鸟来飞越重重障碍。我们将使用之前学到的技能来完成这款游戏。同时为了让游戏的画面更加生动,我们将着重讨论如何滚动显示游戏的背景图像,以及播放小鸟角色的飞行动画。此外,我们还将对游戏的图形用户界面设计进行介绍。
主要涉及如下知识点:
滚动背景图像
模拟重力效果
播放角色动画
设计图形用户界面
播放游戏音乐
1 创建游戏场景
1.1设置背景图像
相信经过之前的游戏制作学习,大家都十分清楚游戏设计的第一步就是创建游戏场景。我们首先设置常量WIDTH和HEIGHT的值,用来确定场景的大小。我们在程序的开头编写如下代码:
WIDTH
=
138
*
4
HEIGHT
=
396
你或许会觉得奇怪,这里的WIDTH和HEIGHT值代表什么含义呢?为何不像之前游戏中那样,将场景的宽度和高度设为10的整数倍呢?那是因为,之前的游戏都只是用单一的白色作为游戏的背景,所以游戏场景的具体尺寸并不重要,只要能提供足够大小的区域将角色显示出来即可。然而在本章的游戏中,我们将使用图像作为游戏的背景,所使用的图片文件是“flappy_background.png”,它的尺寸为138×396。可以看到该图片的宽度较小,如果只用一幅图片作为背景,则游戏的显示区域将十分有限。因此我们可以将四幅这样的图片拼起来形成一幅完整的背景图像,于是场景总的宽度值就是138的4倍,而高度值就是396,效果如图1所示。
图1 游戏背景图像
由此可见,我们只需使用图片“flappy_background.png”分别创建4个角色,并将它们紧挨在一起显示出来,便可以形成一幅完成的背景图像。接下来定义一个列表backgrounds,用来保存所有的背景角色,然后循环生成各个背景角色,并保存在backgrounds中。我们在程序中加入如下代码:
backgrounds
=
[]
for
i
in
range
(
5
):
backimage
=
Actor
(
"flappybird_background"
,
topleft
=
(
i
*
138
,
0
))
backgrounds
.
append
(
backimage
)
上面的代码是不是有点问题?我们明明只需要创建4个角色,可是程序循环了5次。的确,我们这儿总共创建了5个角色,其中4个用来组成背景图像,剩下的一个自有妙用,后面我们再具体解释。
需要注意,上述代码在创建角色时,为Actor构造方法中传入了一个topleft参数,用来指定角色左上角的横、纵坐标值。它的作用相当于如下语句:
backimage
=
Actor
(
"flappybird_background"
)
backimage
.
top
=
0
backimage
.
left
=
i
*
138
接下来,我们为draw()函数编写代码,以便将背景图像显示出来。代码如下所示:
def
draw
():
for
backimage
in
backgrounds
:
backimage
.
draw
()
运行游戏,可以看到游戏窗口中出现了图1所示的背景图像。
1.2 滚动背景图像
我们知道游戏的场景是有固定大小的,它实际上是由WIDTH和HEIGHT值所确定的一块显示区域。为了拓展场景的空间范围,让有限的场景区域看起来无限延伸,我们可以对背景图像实行滚动显示。所谓滚动显示,就是不断地移动背景图像,当图像从窗口的某个边界移出去之后,再让其从另一边重新移进来,从而形成连绵不绝的显示效果。
然而这样做有一个问题,就是当背景图像尚未完全移出窗口边界时,它此前所占据的区域会留下一段空白。为了实现滚动显示时的无缝连接,我们可以多准备一幅背景图像,将它放在窗口边界之外“待命”,只要当前背景图像稍微移出了窗口边界,那么后备的图像便立即从窗口另一侧移进来。如此便不难理解,之前我们为何要循环地创建5个角色来组成游戏背景。场景滚动的原理如图2所示,图中红色的矩形框表示游戏窗口。
图 2 背景图像滚动示意图
我们首先在程序开头定义一个全局常量SPEED,用来表示背景滚动的速度。
接着定义一个update_background()函数,用来滚动背景图像,代码如下所示:
SPEED
=
3
def
update_background
():
for
backimage
in
backgrounds
:
backimage
.
x
-=
SPEED
if
backimage
.
right
<=
0
:
backimage
.
left
=
WIDTH
可以看到,update_background()函数对列表backgrounds进行遍历,对于其中的每个背景角色,首先减少它的x属性值以使其向左移动,然后判断它的right属性值是否小于等于0,若是则说明角色的右边缘超出了窗口左边界,于是将它的left属性设置为WIDTH的值,从而重新将角色置于窗口的右边界处。
最后为update()函数编写代码,在其中调用update_background()函数,代码如下所示:
def
update
():
update_background
()
现在运行游戏,你会惊喜地看到,游戏的背景图像一直不停地移动着。就如同坐在一辆高速行驶的列车上,游戏窗口就好似列车的车窗,而滚动的场景就像是车窗外的风景依次地从眼前掠过。是不是感觉很酷呢?
2 添加障碍物
现在我们已经创建了游戏场景,而且实现了背景图像的滚动显示。然而,背景图像的作用主要是美化游戏场景,它并不会与游戏角色进行交互。为此,我们还要继续在场景中添加用来交互的角色。根据Flappy Bird游戏规则,场景中会出现两种障碍物——地面和水管,它们用来阻碍小鸟的飞行。下面我们来设置障碍物角色。
2.1 设置地面
目前游戏背景描绘的是天空中的景象,我们可以在背景图像的底部加入表示地面的角色,一方面可以让场景显得更完整,另一方面又能与小鸟进行交互,从而阻止小鸟向下飞行。
我们准备了一个图片文件“flappybird_ground.png”用来创建地面角色,然后在程序中加入下面一行代码:
ground
=
Actor
(
"flappybird_ground"
,
bottomleft
=
(
0
,
HEIGHT
))
这里定义了一个变量ground用来保存地面角色。通过在Actor构造方法中设置bottomleft参数,我们将地面放置在窗口的底端。
接着修改draw()函数,加入显示地面的代码,如下所示:
现在运行游戏,你是否觉得呈现在眼前的画面有点奇怪?那是因为背景图像在不停地滚动,而地面的图像却是静止不动。为此,我们要让地面与背景同步地滚动起来。由于地面图像的宽度很大,它超过了窗口的宽度,因此这里直接使用一幅地面的图像来实现滚动效果,原理如图3所示。其中左图表示地面的初始位置,当它不断向左移动到达右图所示的位置时,则重新将它设为左图的初始位置。如此往复,便形成了地面循环滚动的效果。
图3 地面滚动示意图
我们定义一个update_ground()函数,用来移动地面角色,然后在update()函数中调用它来执行。update_ground()函数的代码如下所示:
def
update_ground
():
ground
.
x
-=
SPEED
if
ground
.
right
<
WIDTH
:
ground
.
left
=
0
上述代码先将地面角色的x属性值减去SPEED的值,以使其保持与背景相同的速度向左移动。然后判断它的right属性值是否小于WIDTH的值,若是则说明地面到达了图3中右图所示的位置,于是将它的left属性值设为0,从而让地面重新回到初始位置。
2.2 设置水管
游戏中还有另外一类障碍物,那便是水管,它们用来妨碍小鸟在水平方向的飞行。水管是成对出现的,一根位于窗口上方,另一根位于下方。它们彼此相对,并且间隔一段距离,以便留出空隙让小鸟穿越。
我们准备了图片“flappybird_top_pipe.png”和“flappybird_bottom_pipe.png”,分别用来创建上方水管角色及下方水管角色。接着在程序中加入如下代码:
pipe_top
=
Actor
(
"flappybird_top_pipe"
)
pipe_bottom
=
Actor
(
"flappybird_bottom_pipe"
)
上述代码定义了变量pipe_top和pipe_bottom,分别保存上方水管角色和下方水管角色。可以看到,目前我们并没有为这两个角色指定初始位置。
根据游戏规则,水管要从窗口的右侧出现,并且每次出现时水管的高度要发生改变。然而我们所准备的水管图片高度是确定的,怎样改变水管的高度呢?实际上,我们可以随机地改变水管在垂直方向上的位置,使得水管每次出现在窗口中的部分都不相同,从而看上去就像是水管的高度发生了改变。此外,由于上、下水管的间距是保持不变的,因此我们只需要随机生成上方水管的高度,便可进一步求得下方水管的高度,原理如图4所示。
图4 随机生成水管的高度
我们首先在程序开头定义一个全局常量GAP,用来表示上、下水管的间距。然后定义一个reset_pipes()函数,用来设置水管出现的位置。接着在程序中调用reset_pipes()函数来生成一对水管。新添加的代码如下所示:
GAP
=
150
def
reset_pipes
():
pipe_top
.
bottom
=
random
.
randint
(
50
,
150
)
pipe_bottom
.
top
=
pipe_top
.
bottom
+
GAP
pipe_top
.
left
=
WIDTH
pipe_bottom
.
left
=
WIDTH
reset_pipes
()
可以看到,reset_pipes()函数首先随机生成一个指定范围内的整数值,并将其赋给上方水管的bottom属性。然后将该值与GAP的值求和,并赋给下方水管的top属性,以此来确定下方水管的位置。最后将上、下水管的left属性都设为WIDTH的值,以便让水管从窗口的右边界开始出现。
此外根据游戏规则,水管也要跟随背景一起向左移动,当其移出窗口左边界之后要重新出现在窗口右侧。于是我们再定义一个update_pipes()函数,用来移动上、下水管,同时在update()函数中调用该函数来执行。update_pipes()函数的代码如下所示:
def
update_pipes
():
pipe_top
.
x
-=
SPEED
pipe_bottom
.
x
-=
SPEED
if
pipe_top
.
right
<
0
:
reset_pipes
()
上述代码首先将上、下水管的x属性都减去SPEED的值,以便让它们同时向左移动,并与背景的滚动速度保持一致。接着检查上方水管的right属性值是否小于0,若是则说明水管超出了窗口的左边界,于是调用reset_pipes()函数来重新生成水管的位置。
最后修改draw()函数,在其中加入显示水管的代码,如下所示:
pipe_top.draw() pipe_bottom.draw()
运行游戏可以看到,游戏的背景及障碍物在以相同的速度循环滚动。游戏画面如图5所示。
图5 添加障碍物后的游戏画面
3 添加小鸟
3.1 创建小鸟角色
现在是时候让游戏的主角登场了,下面我们来添加小鸟角色。我们准备了一个图片文件“flappybird1.png”,用来创建小鸟角色。然后在程序中加入如下代码:
bird
=
Actor
(
"flappybird1"
,
(
WIDTH
//
2
,
HEIGHT
//
2
))
bird
.
vy
=
0
上述代码创建了一个小鸟角色,并将它保存在变量bird中,同时将它的初始位置设定为窗口的正中央。此外还为小鸟角色定义了一个vy属性,用来表示垂直方向的飞行速度,初值设为0。
接下来修改draw()函数,在其中加入显示小鸟的一行代码,如下所示:
现在运行游戏,可以看到小鸟竟然飞起来了,而且一直朝右水平飞行!这是怎么回事呢?我们明明没有编写小鸟移动的代码啊!其实这不过是你的错觉罢了,小鸟根本没有移动,因为它的坐标值没有发生任何改变。是不是感觉又领略了游戏设计的一大奥秘呢?
说明:
小鸟之所以看上去向右移动,是因为背景及障碍物都在向左移动,从而反衬出小鸟的向右移动。事实上,很多滚屏游戏都是采用类似的技巧,即固定角色的位置不动,而让场景进行滚动,从而看起来就像是角色在移动。
3.2 模拟重力下的飞行
现在小鸟看上去是在飞行,但也只是在水平方向上飞行,垂直方向好像并没有什么变化。为了让游戏的效果更加逼真,我们可以模拟重力的作用,即在垂直方向对小鸟施加重力的影响,从而实现小鸟在重力加速度下的飞行。
在我们之前制作的游戏中,角色的运动形式都是匀速运动,即角色的速度保持不变,而位置则均匀地改变。然而在加入重力作用后,小鸟的速度不再保持恒定,它会在重力影响下产生一个加速度,使得小鸟的位置不再均匀地改变。就像是从高空中坠落的物体,速度会变得越来越快,位置的变化也会愈来愈大。
另一方面,为了防止小鸟持续地向下坠落,我们需要对小鸟施加控制,让它能够向上飞起来。这里不妨使用鼠标来控制,每当玩家点击鼠标时,小鸟便会飞扬起来。而当它向上飞行达到最高点后,随即又会在重力影响下快速地坠落。图6显示了小鸟在垂直方向的速度变化。
图6 小鸟垂直速度变化示意图
为了达到上述效果,我们需要定义一个全局常量GRAVITY,用来表示重力加速度的值;同时还要定义一个全局常量FLAP_VELOCITY,用来表示小鸟飞扬时的初始速度。我们在程序的开头加入如下代码:
GRAVITY
=
0.2
FLAP_VELOCITY
=
-
5
可以看到,我们为初速度设置了一个负的整数值,表示小鸟飞起时的方向是朝上的。而重力加速度的值虽然看起来很小,但它对速度的影响却是相当大的,如果这个值设得比较大,小鸟可能会迅速地掉落到地面上。
接着定义一个fly()函数用来实现小鸟的飞扬,然后在update()函数中调用该函数来执行。fly ()函数的代码如下所示:
def
fly
():
bird
.
vy
+=
GRAVITY
bird
.
y
+=
bird
.
vy
if
bird
.
top
<
0
:
bird
.
top
=
0
上述代码首先将小鸟的vy属性累加了GRAVITY的值,表示垂直速度受到重力的影响而改变。然后将vy属性值累加到给小鸟的y属性,以此来改变小鸟的纵坐标。此外,为了防止小鸟飞出窗口的上边界,程序还对小鸟的top属性值进行了检查,若该值小于0,说明小鸟超出了上边界,则将它的top属性值设为0,以将其限制在窗口范围之内。
最后我们为on_mouse_down()函数编写代码,用来处理鼠标的点击事件。代码如下所示:
def
on_mouse_down
(
pos
):
bird
.
vy
=
FLAP_VELOCITY
sounds
.
flap
.
play
()
可以看到,当程序检测到鼠标点击的动作后,首先将小鸟的vy属性设置为FLAP_VELOCITY的值,相当于给小鸟施加一个向上的爆发力,让它能够瞬间飞扬起来。同时播放一个音效文件“flap.wav”,用来增强交互的效果。
现在运行游戏,试着点击鼠标来操纵小鸟,看看它是否能够自由翱翔了呢?
3.3 播放飞行动画
你也许会觉得小鸟飞行时看上去有点不自然,它的翅膀总是向上扬起,而没有随着飞行上下摆动。的确是这样,那是因为目前我们仅仅使用了一个图片文件“flappybird1.png”来表示小鸟角色,其图像描绘的正是小鸟翅膀上扬的形态。为了让小鸟的飞行效果显得更加生动,我们希望能以动画的形式来展示小鸟飞行的姿态,让小鸟看上去是在不断地拍打着翅膀。那么如何播放角色的动画呢?
说明:
其实所谓的动画,不过是由一幅幅静止的图像而组成,只不过当图像快速切换时,人眼会所产生错觉,误认为图像是活动的。相关研究表明,当以不低于每秒24幅的速度切换图像时,就可以产生动画的效果,当然切换速度越快效果会越好。
由此可见,若要播放小鸟飞行的动画,仅使用一张图片是不够的,我们还得另外为小鸟角色准备两张图片“flappybird2.png”和“flappybird3.png”,分别呈现小鸟翅膀放平以及翅膀下摆的姿态,如图7所示。这样一来,我们就可以利用这3张图片来形成小鸟的飞行动画了。
图7小鸟飞行的图片
然而,如果每一次游戏循环就切换一幅图像的话,那动画的速度未免太快了,这样会让小鸟飞行的效果看上去不太自然。为了控制动画播放的速度,减缓图像切换的频率,我们需要再次借助延迟变量来实现。
首先在程序开头定义一个全局变量anim_counter 作为延迟变量,并将其初值设为0,代码如下所示:
然后定义一个animation()函数用来播放小鸟的飞行动画,并在update()函数中调用该函数来执行。animation()函数的代码如下所示:
def
animation
():
global
anim_counter
anim_counter
+=
1
if
anim_counter
==
2
:
bird
.
image
=
"flappybird1"
elif
anim_counter
==
4
:
bird
.
image
=
"flappybird2"
elif
anim_counter
==
6
:
bird
.
image
=
"flappybird3"
elif
anim_counter
==
8
:
bird
.
image
=
"flappybird2"
anim_counter
=
0
上述代码首先增加 anim_counter变量的值,然后根据它的值来修改小鸟的image属性,以便为它设置不同的图像。具体来说,当 anim_counter的值增加到2时,将小鸟图像设置为“flappybird1.png”,即图7左边的那幅图像,此时小鸟的翅膀向上扬起;当 anim_counter的值增加到4时,将小鸟图像设置为“flappybird2.png”,即图7中间的那幅图像,此时小鸟的翅膀水平不动;当 anim_counter的值增加到6时,将小鸟图像设置为“flappybird3.png”,即图7右边的那幅图像,此时小鸟的翅膀向下摆动;当 anim_counter的值增加到8时,又将小鸟图像设置为“flappybird2.png”,此时小鸟的翅膀重新放平。自此小鸟的飞行动画便播放完一轮,这时需要将 anim_counter的值重新设为0,从而开始下一轮的动画播放。
再次运行游戏,看看现在小鸟飞行时是否更加真实自然了呢?
4 小鸟与障碍物的交互
现在小鸟虽然可以飞行了,但看上去似乎畅通无阻,无论是水管还是地面都不能阻止它的前进。那是因为我们还没有实现小鸟与障碍物之间的交互行为,接下来便要分别对小鸟与地面,以及小鸟与水管实施碰撞检测。
4.1 小鸟与地面碰撞
根据游戏规则,若小鸟下落时碰撞到地面,则游戏结束。因此我们需要检测小鸟是否和地面发生了碰撞,并且在检测到碰撞后让游戏停止运行。为此,可以给小鸟添加一个布尔类型的属性dead,用来表示小鸟是否存活,当小鸟碰撞到地面后将该值设为True。我们在创建小鸟的语句后面加入这一行代码:
然后定义一个check_collision()函数来执行碰撞检测,代码如下:
def
check_collision
():
if
bird
.
colliderect
(
ground
):
sounds
.
fall
.
play
()
bird
.
dead
=
True
可以看到,代码调用了小鸟角色bird的colliderect()方法,并将地面角色ground作为参数传递给该方法,从而对小鸟与地面实施碰撞检测。若该方法的返回值为True,说明两者发生了碰撞,这时播放碰撞的音效文件“fall.wav”,同时将小鸟的dead属性设为True。
最后对update()函数进行修改,以便在小鸟撞上地面后停止游戏的运行。修改后的update()函数如下所示:
def
update
():
if
bird
.
dead
:
return
update_background
()
update_ground
()
update_pipes
()
fly
()
animation
()
check_collision
()
update()函数首先对小鸟的dead属性值进行判断,如果该值为True,说明小鸟已经撞上了地面,于是调用return语句直接返回。此时后面的代码将不会执行,从而阻止了游戏的继续运行。
运行游戏测试一下,看看小鸟撞上地面后游戏是否会停下来。
4.2 小鸟与水管碰撞
接下来继续处理小鸟与水管的碰撞。其实与处理小鸟与地面的碰撞类似,我们只需要对小鸟与水管实施碰撞检测即可。只不过水管分为上、下两根,所以我们需要分别对上方水管和下方水管实施碰撞检测。为此我们在check_collision()函数中加入如下代码:
if
bird
.
colliderect
(
pipe_top
)
or
bird
.
colliderect
(
pipe_bottom
):
sounds
.
collide
.
play
()
bird
.
dead
=
True
上述代码两次调用了小鸟的colliderect()方法,并分别将上方水管pipe_top及下方水管pipe_bottom作为参数传递给该方法,从而对小鸟与上、下水管分别实施碰撞检测。若是小鸟与上方水管或者下方水管其中之一发生了碰撞,则播放碰撞的音效文件“collide.wav”,同时将小鸟的dead属性设为True。
再次运行游戏测试一下,看看小鸟撞上水管后游戏是否会停下来。
4.3 小鸟飞越水管
目前我们已经实现了游戏的惩罚机制,即当小鸟撞上地面或水管时停止游戏。那么相应地我们还应该为游戏设计奖励机制,以便激励玩家在游戏中坚持下去。类此之前游戏设计中采取的做法,我们可以设置游戏积分,当玩家操纵小鸟从上、下水管之间穿越时,便增加分数值。
那么该如何判定小鸟飞越了水管呢?是否需要利用小鸟和水管的坐标来判断呢?的确如此。我们可以将小鸟与水管的横坐标进行比较,若发现前者大于后者,则判定小鸟飞越了水管,于是增加游戏积分的值。但这样存在一个问题,就是一旦小鸟飞越了水管,那么此后它的横坐标将始终大于水管的横坐标,这就意味着分数值会一直不断地增加,而事实上我们只希望分数增加一次。为此,我们需要借助布尔变量的“开关”作用。
说明:
布尔变量在游戏设计中使用得非常广泛,它除了用来表示游戏或角色的状态,还能对游戏中的各种操作进行控制,以防止连续执行操作。比如当布尔变量的值为True时,我们执行了某个操作,随即将它的值设为False,相当于“关闭”了开关,这就阻止了再一次执行该操作。直到某种情况下布尔变量的值重新被设为True,相当于“打开”了开关,这时才能重新执行该操作。
我们可以在程序中定义两个全局变量score和score_flag,前者是整型变量,用来表示游戏的分数值;后者是布尔变量,用来控制游戏积分的操作,只有它的值为True时才能增加游戏分数。代码如下所示:
score
=
0
score_flag
=
False
初始时我们将score的值设为0,同时将score_flag的值设为False。然后修改reset_pipes()函数,在其中对score_flag的值进行设置。修改后的reset_pipes()函数如下所示(粗体部分表示新添加的代码):
def
reset_pipes
():
global
score_flag
score_flag
=
True
pipe_top
.
bottom
=
random
.
randint
(
50
,
150
)
pipe_bottom
.
top
=
pipe_top
.
bottom
+
GAP
pipe_top
.
left
=
WIDTH
pipe_bottom
.
left
=
WIDTH
如此一来,每当重新设置水管时,便会将score_flag的值设为True,相当于“打开”了积分操作的开关,此时便具备了增加分数的必要条件。
接着修改fly()函数,在其中添加代码来实现游戏积分的功能。修改后的fly()函数如下所示(粗体部分表示新添加的代码):
def
fly
():
global
score
,
score_flag
if
score_flag
and
bird
.
x
>
pipe_top
.
right
:
score
+=
1
score_flag
=
False
bird
.
vy
+=
GRAVITY
bird
.
y
+=
bird
.
vy
if
bird
.
top
<
0
:
bird
.
top
=
0
可以看到,需要同时满足两个条件才会增加游戏分数,一个条件是score_flag的值为True,另一个条件是小鸟的x属性值大于水管的right属性值,即小鸟越过了水管的右边缘。若这两个条件都满足,则将score的值加1,同时将score_flag的值设为False,相当于“关闭”了积分操作的开关,从而阻止了分数值持续地增加。
最后修改draw()函数,在其中添加如下一行代码来显示游戏积分:
screen
.
draw
.
text
(
str
(
score
),
topleft
=
(
30
,
30
),
fontsize
=
30
)
现在运行游戏,可以看到如图8所示的游戏画面。试着玩一下,看看你最多能玩到多少分。
图8添加积分后的游戏画面
5 设计图形用户界面
至此游戏的基本功能已经实现了,但我们还可以更进一步,把游戏的功能设计得更加完善。那什么地方还能继续完善呢?如果对比市面上发行的Flappy Bird游戏,不难发现我们目前的游戏缺少一个初始界面,其中包含了游戏标题、开始按钮、游戏提示等基本元素。这个初始界面就是通常所说的图形用户界面(简称GUI),它往往由一些文字或图像的元素组成,用来显示游戏的基本信息,或者提供一些游戏设置的选项。图形用户界面建立了玩家与游戏之间的沟通渠道,使得玩家能够快速方便地了解及操作游戏。
下面我们来为游戏设计图形用户界面。
5.1 显示GUI图像
我们事先准备了一些图片文件用来表示GUI中的图像元素,它们分别是:“flappybird_title.png”,用来显示游戏的标题;“flappybird_get_ready.png”,用来显示游戏的操作提示;“flappybird_start_button.png”,用来作为游戏的开始按钮;“flappybird_game_over.png”,用来显示游戏的结束信息。然后可以使用上述图片来创建GUI角色,并将它们显示在游戏窗口中。我们在程序中加入如下代码:
gui_title
=
Actor
(
"flappybird_title"
,
(
WIDTH
//
2
,
72
))
gui_ready
=
Actor
(
"flappybird_get_ready"
,
(
WIDTH
//
2
,
204
))
gui_start
=
Actor
(
"flappybird_start_button"
,
(
WIDTH
//
2
,
345
))
gui_over
=
Actor
(
"flappybird_game_over"
,(
WIDTH
//
2
,
HEIGHT
//
2
))
可以看到,上述代码在创建各个GUI角色时为其指定了初始位置,事实上参数中的坐标值并没有什么特殊的含义,可以根据实际的显示效果来进行调整。
由于图形用户界面需要在游戏开始运行之前来显示,因此我们还要对游戏的状态进行标记,以便确定何时显示GUI角色。我们在程序开头定义一个全局布尔变量started,用来表示游戏是否开始,代码如下所示:
代码将started的初始值设为False,表示游戏尚未开始的状态。
接着修改draw()函数,在其中加入显示GUI角色的代码。修改后的draw()函数如下所示(粗体部分表示新添加的代码):
def
draw
():
for
backimage
in
backgrounds
:
backimage
.
draw
()
if
not
started
:
gui_title
.
draw
()
gui_ready
.
draw
()
gui_start
.
draw
()
return
pipe_top
.
draw
()
pipe_bottom
.
draw
()
ground
.
draw
()
bird
.
draw
()
screen
.
draw
.
text
(
str
(
score
),
topleft
=
(
30
,
30
),
fontsize
=
30
)
if
bird
.
dead
:
gui_over
.
draw
()
可以看到,我们在draw()函数中插入了两段代码,一段放在显示背景图像的语句之后,它检查全局变量started的值,若为False说明游戏尚未开始,于是分别显示游戏标题、操作提示及开始按钮的GUI图像;另一段代码则放在了最后,它检查小鸟的dead属性值,若为True说明小鸟撞上了地面或水管,于是显示游戏结束的GUI图像。
现在运行游戏,可以看到窗口中显示了图形用户界面,如图9所示。
图9 游戏的图形用户界面
提示:
由于游戏中角色数量众多,所以要小心它们的显示顺序。倘若显示语句的执行顺序不当,则可能造成错误的遮挡关系。试着改变draw()函数中各语句的执行顺序,看看游戏的画面会发生什么改变。
5.2 点击开始按钮
观察一下刚刚添加的图形用户界面,可以看到其中有一个开始按钮的图像,直觉告诉你这个按钮是有用的。的确如此,我们需要用它来启动游戏。具体来说,当玩家用鼠标点击该按钮时,游戏便开始运行。
然而这个开始按钮只是看起来像一个按钮,它本质上不过是一幅图像罢了。那又如何让它响应鼠标的点击操作呢?这个问题不难解决。由于我们能够获取鼠标点击处的坐标,因此可以判断鼠标点的坐标是否位于按钮图像的范围之内,若是则说明鼠标点击了开始按钮。
接下来修改on_mouse_down()函数,在其中加入点击开始按钮的功能。修改后的on_mouse_down()函数如下所示:
def
on_mouse_down
(
pos
):
global
started
if
bird
.
dead
:
return
if
started
:
bird
.
vy
=
FLAP_VELOCITY
sounds
.
flap
.
play
()
return
if
gui_start
.
collidepoint
(
pos
):
started
=
True
reset_pipes
()
上述代码有三个if语句,第一个if语句检查小鸟的dead属性,若为True说
明小鸟撞上地面或水管,于是调用return语句返回;若小鸟的dead属性为False,则执行第二个if语句,检查全局变量started的值,若为True说明游戏已经开始,于是执行小鸟飞扬的操作;若started的值为False,则执行第三个if语句,并通过按钮的collidepoint()方法来检查它是否被点击,若为True说明鼠标点击了按钮,于是将started的值设为True,并调用reset_pipes()函数生成水管的位置。
5.3 播放背景音乐
现在游戏已经相当完美了,最后再让我们锦上添花,为游戏添加一段背景音乐。在此前的游戏设计中,我们仅仅播放了动作音效,目的是让游戏及时地对玩家的操作进行反馈,以此增强游戏的交互效果。然而背景音乐是一段比较长的乐曲,它具有特定的旋律,主要用来烘托游戏场景的气氛。那么如何播放背景音乐呢?
事实上利用Pgzero库来播放音乐十分简单,因为它提供了一个music对象,能够方便地实现音乐的播放及控制功能。我们这里只需使用music对象的两个方法,一个是play()方法,用来播放音乐文件;另一个是stop()方法,用来停止播放音乐。
我们事先准备了一个音乐文件“flappybird.mp3”作为游戏的背景音乐,然后将其放置在“music”文件夹之下。接着修改on_mouse_down()函数,在鼠标点击开始按钮后的操作中加入如下一行代码:
最后修改check_collision()函数,在小鸟碰撞地面及水管后的处理中分别加入下面这行代码:
现在运行游戏可以发现,当鼠标点击开始按钮的同时,紧张刺激的背景音乐便随之响起了,玩家将在音乐的激励下斗志昂扬地“投入战斗”。而当小鸟碰到地面或水管的那一刻,背景音乐便会戛然而止,整个游戏世界将重新归于寂静。
6 回顾与总结
我们学习制作了Flappy Bird游戏。我们首先讨论了如何滚动游戏的背景图像,并设法让地面及水管障碍物跟随背景一起移动,同时还介绍了如何随机生成一对水管的高度。然后我们重点讨论了小鸟角色的操作,一方面通过模拟重力效果实现了它的垂直飞行,另一方面为其播放了飞行时的动画。接着对小鸟与地面及水管的碰撞进行了处理,并实现了游戏积分的功能。最后我们为游戏添加了图形用户界面,并实现了背景音乐的播放。
下面给出Flappy Bird游戏的完整源程序代码。
# Flappy Bird游戏源代码flappybird.py
import
random
WIDTH
=
138
*
4
# 窗口宽度(由四张背景图片组成)
HEIGHT
=
396
# 窗口高度
GAP
=
150
# 上下水管间的缺口大小
SPEED
=
3
# 场景滚动速度
GRAVITY
=
0.2
# 重力加速度
FLAP_VELOCITY
=
-
5
# 飞扬时的初始速度
anim_counter
=
0
# 小鸟动画计数器
score
=
0
# 游戏积分
score_flag
=
False
# 得分标记
started
=
False
# 游戏开始标记
backgrounds
=
[]
# 背景图像列表
# 创建五张背景图像角色,用于循环滚动游戏场景
for
i
in
range
(
5
):
backimage
=
Actor
(
"flappybird_background"
,
topleft
=
(
i
*
138
,
0
))
backgrounds
.
append
(
backimage
)
# 创建地面角色
ground
=
Actor
(
"flappybird_ground"
,
bottomleft
=
(
0
,
HEIGHT
))
# 创建上下水管角色
pipe_top
=
Actor
(
"flappybird_top_pipe"
)
pipe_bottom
=
Actor
(
"flappybird_bottom_pipe"
)
# 创建小鸟角色
bird
=
Actor
(
"flappybird1"
,
(
WIDTH
//
2
,
HEIGHT
//
2
))
bird
.
dead
=
False
# 标记小鸟是否存活
bird
.
vy
=
0
# 设置小鸟垂直速度
# 创建GUI角色
gui_title
=
Actor
(
"flappybird_title"
,
(
WIDTH
//
2
,
72
))
gui_ready
=
Actor
(
"flappybird_get_ready"
,
(
WIDTH
//
2
,
204
))
gui_start
=
Actor
(
"flappybird_start_button"
,
(
WIDTH
//
2
,
345
))
gui_over
=
Actor
(
"flappybird_game_over"
,(
WIDTH
//
2
,
HEIGHT
//
2
))
# 游戏逻辑更新
def
update
():
if
not
started
or
bird
.
dead
:
return
update_background
()
update_ground
()
update_pipes
()
fly
()
animation
()
check_collision
()
# 绘制游戏角色
def
draw
():
screen
.
fill
((
255
,
255
,
255
))
for
backimage
in
backgrounds
:
backimage
.
draw
()
if
not
started
:
gui_title
.
draw
()
gui_ready
.
draw
()
gui_start
.
draw
()
return
pipe_top
.
draw
()
pipe_bottom
.
draw
()
ground
.
draw
()
bird
.
draw
()
screen
.
draw
.
text
(
str
(
score
),
topleft
=
(
30
,
30
),
fontsize
=
30
)
if
bird
.
dead
:
gui_over
.
draw
()
# 处理鼠标点击事件
def
on_mouse_down
(
pos
):
global
started
if
bird
.
dead
:
return
if
started
:
bird
.
vy
=
FLAP_VELOCITY
sounds
.
flap
.
play
()
return
# 若点击开始按钮,初始化游戏
if
gui_start
.
collidepoint
(
pos
):
started
=
True
reset_pipes
()
music
.
play
(
"flappybird"
)
# 更新游戏场景,循环滚动背景图像
def
update_background
():
for
backimage
in
backgrounds
:
backimage
.
x
-=
SPEED
if
backimage
.
right
<=
0
:
backimage
.
left
=
WIDTH
# 更新地面角色
def
update_ground
():
ground
.
x
-=
SPEED
if
ground
.
right
<
WIDTH
:
ground
.
left
=
0
# 更新水管角色
def
update_pipes
():
pipe_top
.
x
-=
SPEED
pipe_bottom
.
x
-=
SPEED
if
pipe_top
.
right
<
0
:
reset_pipes
()
# 重新设置上下水管出现的位置
def
reset_pipes
():
global
score_flag
score_flag
=
True
# 随机生成上方水管的垂直位置
pipe_top
.
bottom
=
random
.
randint
(
50
,
150
)
# 根据上方水管的垂直位置来设置下方水管的垂直位置
pipe_bottom
.
top
=
pipe_top
.
bottom
+
GAP
# 设置上下水管的水平位置
pipe_top
.
left
=
WIDTH
pipe_bottom
.
left
=
WIDTH
# 控制小鸟飞行
def
fly
():
global
score
,
score_flag
# 当小鸟越过水管,则分数加一
if
score_flag
and
bird
.
x
>
pipe_top
.
right
:
score
+=
1
score_flag
=
False
# 更新小鸟坐标
bird
.
vy
+=
GRAVITY
bird
.
y
+=
bird
.
vy
# 防止小鸟飞出窗口上边界
if
bird
.
top
<
0
:
bird
.
top
=
0
# 播放小鸟飞行的动画
def
animation
():
global
anim_counter
anim_counter
+=
1
if
anim_counter
==
2
:
bird
.
image
=
"flappybird1"
elif
anim_counter
==
4
:
bird
.
image
=
"flappybird2"
elif
anim_counter
==
6
:
bird
.
image
=
"flappybird3"
elif
anim_counter
==
8
:
bird
.
image
=
"flappybird2"
anim_counter
=
0
# 小鸟与水管和地面的碰撞检测
def
check_collision
():
if
bird
.
colliderect
(
pipe_top
)
or
bird
.
colliderect
(
pipe_bottom
):
sounds
.
collide
.
play
()
music
.
stop
()
bird
.
dead
=
True
elif
bird
.
colliderect
(
ground
):
sounds
.
fall
.
play
()
music
.
stop
()
bird
.
dead
=
True
PS:若要全面系统的学习可以参考《趣学Python游戏编程》,该书通过十个经典游戏案例,深入浅出地介绍了游戏编程的基本原理,以及使用Python编写游戏的具体方法。相信学完这本书后你也能开发出如此精彩的小游戏。
Flappy Bird 🕹️ Play Flappy Bird on CrazyGames
Flappy Bird in a nutshell
Flappy Bird's gameplay is best described as simple, addictive, yet mildly infuriating. Tap to raise the bird up and carefully get it through the pipes without crashing. When you fail, you’ll get a high score, and you’ll want to do it all over again to try and outdo your previous self.
Play it on the web - only
Flappy Bird was originally created as a mobile game, but was later removed from the app stores. In an interview with Forbes, Dong Nguyen, the developer of Flappy Bird, cited the game's addictive nature as the reason for its removal from the stores.
Despite this fact, you can still play the original Flappy Bird for free in your web browser, using a desktop or mobile device!
The legacy of Flappy Bird
Flappy Bird was part of an early revolution in hypercasual mobile games. Plenty of simple one-button and side-scrolling games have been released since its inception.
Square Bird
andTap Tap Shots
are two excellent games to check out!Features
Incredibly simple mechanics
Frustrating but addictive
High score tracking
The original Flappy Bird
Release Date
Early 2014
Platform
Web browser (desktop and mobile)