《Flappy Bird》 Python Clone 学习之路
目录
背景
准备
获取游戏资源包
工程搭建
代码编写
背景
前阵学习了一些python的简单语法,前两天又了解到有个pygame库可以用python写些简单的2D游戏,昨晚无意中从GitHub上搜的了一个Flappy Bird用pygame写的源码,copy在电脑上竟然可以完美运行,这激起我的学习兴趣,也想完全从头做一个一样的,学习和成长的过程不就是一个在不断重复和模仿别人的过程嘛
准备
- python环境(强烈建议安装32位python)
- pygame库 (cmd 下 输入 pip install pygame即可)
- ---------游戏图片和音效直接下载的话(资源包下载点我)可跳过以下步骤直接从工程搭建开始-----------------------
- opencv-python 库用于截图 (cmd 下 输入 pip install opencv-python)
- 游戏资源包(游戏中用到的图片文件和音效文件)
(1、 2自行百度,下面主要介绍一下资源包的获取)
获取游戏资源包
能力和精力有限,游戏声音和图片就没有从头学起自己制作了,作者主要目的以学习python为主。
网上下载一个Flappy Bird原版的APK安装包,将.apk后缀改为.zip,然后右键解压(电脑安装了压缩软件,本人用的WinRAR),解压后在\assets\sounds可以找到音频文件,在assets\gfx文件夹下找到atlas.png图片。
可以发现,他讲游戏图片中所有元素都拼到一张图里了,这得需要进行图片分割,打开\res\raw\atlas.txt
可以看到游戏元素图片的在整张拼图的具体信息,下面就是用这个信息进行图片分割提取
从上图看出每行是一张图片信息,
[图片名称,图片宽度(单位像素点),图片高度,图片在整张拼图相对位置x(整张宽度为1),图片在整张拼图相对位置y(整张宽度为1),图片在整张拼图相对宽度x(整张宽度为1),图片在整张拼图相对高度y(整张宽度为1)]
在D盘根目录下新建一个Split文件夹将atlas.png和atlas.txt都拷过去,新建split.py文件,内容如下:
import cv2 as cv
import os
#读取原图
image = cv.imread('atlas.png', -1)
#打印图片形状(高,宽,通道数),高宽的单位是像素数
raw_width, raw_height, _ = image.shape
#创建文件夹
if not os.path.exists("./output/"):os.makedirs("./output/")
#读取txt文件
with open('atlas.txt') as f:for line in f:strs = line.split(" ")filename = strs[0]imgWidth = int(strs[1])imgHeight = int(strs[2])imgX = float(strs[3])*raw_widthimgY = float(strs[4])*raw_height# 对感兴趣区域进行截图imageROI=image[int(imgY):int(imgY+imgHeight),int(imgX):int(imgX+imgWidth)]# 截图部分生成新图片cv.imwrite("./output/"+filename+".png", imageROI)
执行完成后就在新产生的output文件夹里就会有分割出所需图像
最后音频文件解压出来的ogg格式,防止在有的Windows系统中没有响应ogg播放软件,最好用转换软件(比如格式工厂)将ogg转换成wav格式
至此,所有资源包都已获取,资源包下载点我
工程搭建
新建一个FlappyBird文件夹作为工程文件夹,期内新建FlappyBird.py文件作为代码文件,然后新建assets文件夹,assets文件夹内新建audio文件夹存放音频,sprites文件夹存放图片元素
至此所有准备工作完成,开始撸代码
代码编写
#从迭代器库中调用cycle方法
from itertools import cycle
import random
import sys
import pygame
from pygame.locals import *SCREEN_WIDTH = 288
SCREEN_HEIGHT = 512
# 游戏状态
ANIMATION = 0
RUNNING = 1
GAMEOVER = 2pygame.init()
pygame.display.set_caption("Flappy Bird by GHL")
#设置游戏对话框尺寸
SCREEN = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
#取背景图,抠掉边缘转换
background_day = pygame.image.load('assets/sprites/background-day.png').convert_alpha()
#取小鸟图
bird = [pygame.image.load('assets/sprites/redbird-upflap.png').convert_alpha(),pygame.image.load('assets/sprites/redbird-midflap.png').convert_alpha(),pygame.image.load('assets/sprites/redbird-downflap.png').convert_alpha()
]
# 取数字
number = [pygame.image.load('assets/sprites/0.png').convert_alpha(),pygame.image.load('assets/sprites/1.png').convert_alpha(),pygame.image.load('assets/sprites/2.png').convert_alpha(),pygame.image.load('assets/sprites/3.png').convert_alpha(),pygame.image.load('assets/sprites/4.png').convert_alpha(),pygame.image.load('assets/sprites/5.png').convert_alpha(),pygame.image.load('assets/sprites/6.png').convert_alpha(),pygame.image.load('assets/sprites/7.png').convert_alpha(),pygame.image.load('assets/sprites/8.png').convert_alpha(),pygame.image.load('assets/sprites/9.png').convert_alpha()
]
# 地面图
ground = pygame.image.load('assets/sprites/base.png').convert_alpha()
# 开场动画
message = pygame.image.load('assets/sprites/message.png').convert_alpha()
# 取柱子
pipe_down = pygame.image.load('assets/sprites/pipe-green.png').convert_alpha()
pipe_up = pygame.transform.rotate(pipe_down, 180)
# 取logo
logo = pygame.image.load('assets/sprites/logo.png').convert_alpha()
# 取声音
if "win" in sys.platform:soundExt = ".wav"
else:soundExt = ".ogg"
sound_wing = pygame.mixer.Sound('assets/audio/wing'+soundExt)
sound_hit = pygame.mixer.Sound('assets/audio/hit'+soundExt)
sound_point = pygame.mixer.Sound('assets/audio/point'+soundExt)
sound_die = pygame.mixer.Sound('assets/audio/die'+soundExt)
# 取game over文本
text_game_over = pygame.image.load('assets/sprites/text_game_over.png').convert_alpha()def showScore(score):# 拆分数字scoreDigits = [int(x) for x in list(str(score))]# 计算数字总宽度totalWidth = 0for digit in scoreDigits:totalWidth += number[digit].get_width()# 居中摆放score_x_position = (SCREEN_WIDTH - totalWidth)//2score_y_position = int(0.2 * SCREEN_HEIGHT)# 刷图for digit in scoreDigits:SCREEN.blit(number[digit], (score_x_position, score_y_position))score_x_position += number[digit].get_width()def main():# 创建帧率实例FPS = pygame.time.Clock()# 迭代器 小鸟翅膀 0-1-2-1-0循环wing_position_iter = cycle([0,1,2,1])wing_position = 0# 迭代器 小鸟上下抖动 bird_shake_iter = cycle([0,1,2,3,4,3,2,1,0,-1,-2,-3,-4,-3,-2,-1])bird_shake = 0bird_position = int(0.5 * SCREEN_HEIGHT) # 小鸟起始高度bird_x_position = int(0.2*SCREEN_WIDTH) # 小鸟水平位置ground_position = int(0.8 * SCREEN_HEIGHT-bird[0].get_height()) # 地面高度fps_count = 0 # 帧数处理key_down = 0 # 按键按下 gravity = 1 # 重力大小down_velocity = 0 # 下降速度head_direction = 0 # 鸟头方向game_state = ANIMATION # 游戏状态pipe_move_distance = SCREEN_WIDTH*4//3+pipe_down.get_width()#管子需要移动的距离pipe_gap = 150 # 管空隙大小pipe2_gap = 150 # 管2空隙大小pipe_x_position = SCREEN_WIDTH # 柱子水平位置pipe_down_position = 0 # 下柱子管口位置pipe2_x_position = pipe_move_distance # 柱子水平位置pipe2_up_position = 0pipe2_down_position = 0 # 下柱子管口位置score = 0 # 得分ground_x_position = 0 # 地面水平位置bird_actual_position = 0# 小鸟实际高度# fisrt_fly_no_pipe = 90 # 开始一段没有柱子while 1:# 获取鼠标按键状态for event in pygame.event.get(): if event.type == QUIT:pygame.quit()sys.exit()if event.type == KEYDOWN and event.key == K_SPACE:if game_state == ANIMATION:game_state = RUNNINGdown_velocity = 0 # 下降速度head_direction = 0 # 鸟头方向fps_count = 0 # 帧数处理score = 0 # 得分pipe_x_position = SCREEN_WIDTH # 柱子水平位置pipe2_x_position = pipe_move_distance # 柱子水平位置if game_state == RUNNING:key_down = 6sound_wing.play()if game_state == GAMEOVER:# 小鸟落地后游戏才能重新开始 if bird_actual_position == ground_position:game_state = ANIMATIONbird_position = int(0.5 * SCREEN_HEIGHT) # 小鸟起始高度# 刷新背景SCREEN.blit(background_day, (0, 0))# 重绘logo大小logo_resize = pygame.transform.scale(logo,(25,25))# 设置左上角iconpygame.display.set_icon(logo_resize)if game_state == ANIMATION:fps_count += 1SCREEN.blit(message,(SCREEN_WIDTH/2-message.get_width()/2, 0.1* SCREEN_HEIGHT))# 草地动起来ground_x_position = -1*((fps_count*4)%(ground.get_width()-SCREEN_WIDTH))SCREEN.blit(ground, (ground_x_position,int(0.8 * SCREEN_HEIGHT)))# 更新翅膀位置,比帧数放慢5倍if fps_count % 5 == 0:wing_position = next(wing_position_iter)# 小鸟上下抖动 bird_shake = next(bird_shake_iter)# 小鸟动起来bird_actual_position = bird_position+bird_shakeSCREEN.blit(bird[wing_position], (bird_x_position, bird_actual_position)) if game_state == RUNNING:# 帧数处理fps_count += 1# 游戏刚开始时候没有柱子空飞一段# if fps_count*4 > SCREEN_WIDTH+pipe_down.get_width(): if 1:# 柱子动起来if pipe_x_position == SCREEN_WIDTH:pipe_down_position = random.randrange(int(0.3 * SCREEN_HEIGHT),int(0.7 * SCREEN_HEIGHT), 10)pipe_gap = random.randrange(100,151, 10)pipe_up_position = pipe_down_position - pipe_gap - pipe_up.get_height()pipe_x_position = SCREEN_WIDTH-(fps_count*4)%pipe_move_distanceSCREEN.blit(pipe_down, (pipe_x_position,pipe_down_position))SCREEN.blit(pipe_up, (pipe_x_position,pipe_up_position))if fps_count*4 > pipe_move_distance//2:# 柱子2动起来if pipe2_x_position == pipe_move_distance:pipe2_down_position = random.randrange(int(0.3 * SCREEN_HEIGHT),int(0.7 * SCREEN_HEIGHT), 10)pipe2_gap = random.randrange(100,151, 10)pipe2_up_position = pipe2_down_position - pipe2_gap - pipe_up.get_height()pipe2_x_position = pipe_move_distance-pipe_down.get_width()-(fps_count*4-pipe_move_distance//3)%pipe_move_distanceSCREEN.blit(pipe_down, (pipe2_x_position,pipe2_down_position))SCREEN.blit(pipe_up, (pipe2_x_position,pipe2_up_position))# 草地动起来ground_x_position = -1*((fps_count*4)%(ground.get_width()-SCREEN_WIDTH))SCREEN.blit(ground, (ground_x_position,int(0.8 * SCREEN_HEIGHT)))# 更新翅膀位置,比帧数放慢5倍if fps_count % 5 == 0:wing_position = next(wing_position_iter)# 小鸟上下抖动 bird_shake = next(bird_shake_iter)# 小鸟受重力下落, 按键按下则上升if key_down:# 刷新的过程需要key_down递减之0 用几帧画面完成key_down -= 1 bird_position -= 6bird_position = max(0, bird_position) # 边界检测down_velocity = 0head_direction += 6head_direction = min(24, head_direction) # 边界检测else:down_velocity += gravitybird_position += down_velocitybird_position = min(bird_position, ground_position)# 边界检测head_direction -= 3head_direction = max(-42, head_direction) # 边界检测# 调整头的方向bird_head = pygame.transform.rotate(bird[wing_position], head_direction)# 小鸟动起来bird_actual_position = bird_position+bird_shakeSCREEN.blit(bird_head, (bird_x_position, bird_actual_position))# 检测撞地if bird_position == ground_position:game_state = GAMEOVERsound_hit.play()# 检测撞柱子# 小鸟水平位置在柱子管水平宽度内if bird_x_position+bird[0].get_width() > pipe_x_position and \bird_x_position < pipe_x_position+pipe_down.get_width():# 小鸟高度比下管道低或比上管道高if bird_actual_position+bird[0].get_height() > pipe_down_position or \bird_actual_position < pipe_down_position - pipe_gap:game_state = GAMEOVERsound_hit.play()sound_die.play()# 小鸟水平位置在柱子2管水平宽度内if bird_x_position+bird[0].get_width() > pipe2_x_position and \bird_x_position < pipe2_x_position+pipe_down.get_width():# 小鸟高度比下管道低或比上管道高if bird_actual_position+bird[0].get_height() > pipe2_down_position or \bird_actual_position < pipe2_down_position - pipe2_gap:game_state = GAMEOVERsound_hit.play()sound_die.play()# 小鸟飞过管道后壁刷新得分if abs(bird_x_position - (pipe_x_position+pipe_down.get_width() ) ) < 3 or\abs(bird_x_position - (pipe2_x_position+pipe_down.get_width() ) ) < 3:sound_point.play()score += 1showScore(score)if game_state == GAMEOVER:# 调整头的方向bird_head = pygame.transform.rotate(bird[wing_position], -90)SCREEN.blit(pipe_down, (pipe_x_position,pipe_down_position))SCREEN.blit(pipe_up, (pipe_x_position,pipe_up_position))SCREEN.blit(pipe_down, (pipe2_x_position,pipe2_down_position))SCREEN.blit(pipe_up, (pipe2_x_position,pipe2_up_position))SCREEN.blit(ground, (ground_x_position,int(0.8 * SCREEN_HEIGHT)))bird_actual_position += 10bird_actual_position = min(ground_position, bird_actual_position)SCREEN.blit(bird_head, (bird_x_position, bird_actual_position))showScore(score)# 小鸟落地后游戏才能重新开始 if bird_actual_position == ground_position:SCREEN.blit(text_game_over, (SCREEN_WIDTH/2-text_game_over.get_width()/2, 0.4*SCREEN_HEIGHT))# 设置文中logoSCREEN.blit(pygame.image.load('assets/sprites/ghl_white50.png').convert_alpha(), (SCREEN_WIDTH-50,SCREEN_HEIGHT-50))# 图片刷新pygame.display.update()# 设置帧率30帧FPS.tick(30)if __name__ == "__main__":main()
文件不是一天写完的,最终的源码及用到的资源包及用pyinstaller生成的exe,我放在了 完整源码及资源包 供下载
明天就是中秋了,最后,祝大家中秋节快乐!
2018年9月23日于上海图书馆
《Flappy Bird》 Python Clone 学习之路相关推荐
- python爬虫学习之路
python爬虫学习之路 第一章:爬虫基础 1.爬虫前奏 爬虫的实际例子: 1.搜索引擎(百度.谷歌.360搜索等.) 2.伯乐在线 3.惠惠购物助手 4.数据分析与研究(数据冰山知乎专栏). 5.抢 ...
- python爬虫学习之路(二)re库的使用方法
python基础爬虫学习之路(二) 在上一篇文章中,我们已经学习了有关爬虫对URL的访问以及利用xpath表达式对网页中的信息进行提取,这一篇文章我们将从信息提取的角度来进一步学习爬虫. 正则表达式提 ...
- 我的python爬虫学习之路
很早之前就想学一下爬虫,但是一直没学成,这次终于趁着做完毕设的这段时间,把基于python的爬虫学了一下,现在记录一下我的学习之路,以供想学习爬虫的新手参考. 虽然在写爬虫程序之前没有用过py ...
- Python Diango学习之路
Python Django 学习(1) 小白的web开发初创历程,持续更新中 先是理论学习-- 经典的MVC设计模式 Web服务器开发领域有着著名的MVC设计模式:数据存储逻辑.业务逻辑和表现逻辑. ...
- Python爬虫学习之路(1)--前端
学习前的准备 最近一直想找一种督促自己学习的方法,感觉似乎写博客是一个不错的选择.所以这些博客的主要目的当然是让自己静下心来巩固复习,当然如果能对别人有所帮助,那就再好不过了. 我是在win10+py ...
- PYTHON 自动化学习之路
一.用户交互小程序 username = 'gyc' password = 'gyc'u = input("what is you name?:") p = input(" ...
- 【Python深度学习之路】-3.2PR曲线
1.何谓PR曲线 所谓PR曲线是指用横轴表示召回率,纵轴表示精确率,将数据绘制成图表的形式所得到的曲线. 召回率和准确率两个指标成反比关系,所谓反比关系,指的是当提升精确率时,召回率会降低,相反如果要 ...
- 【Python深度学习之路】-3.1性能评价指标
1.理解混淆矩阵 所谓混淆矩阵,是指将模型对各个测试数据的预测结果分为真阳性.真阴性.假阳性和假阴性并对符合各个观点的预测结果的数量进行统计的一种表格. 其中,真阳性和真阴性表示机器学习模型的回答是正 ...
- python 爬虫 包_python爬虫学习之路-抓包分析
利用浏览器抓包,是爬虫中的很实用的技能.在爬虫编程之前,我们要对抓取的目标页面有所了解,比如浏览器的这个请求这个页面中间都经历了什么,数据是怎么发送和返回的. 抓包的作用 我把抓包分析的作用简单列一下 ...
最新文章
- [转]各种字符集和编码详解
- Java面向对象(1) —— 封装
- alientek ministm32液晶显示程序_佳显12864中文字库液晶专业生产液晶显示模块
- JNI开发笔记(五)--JNI语法总结
- spring中事务失效的几种情况
- 在nginx中配置如何防止直接用ip访问服务器web server及server_name特性讲解
- 30岁就退休,你也可以做到
- thymeleaf常用语法
- Spring框架工作原理
- 极客大学架构师训练营 大数据可视化、机器学习、PageRank算法、KNN分类算法、贝叶斯分类算法、推荐引擎算法、感知机、神经网络 第26课 听课总结
- UltraISO 软碟通注册码
- opendrive文件结构
- 初识Kinect之二
- 计算机图形学(六)-光栅化、采样、走样与反走样、滤波与卷积
- Conflux 的自我进化:从 DAG 到树图| 对话伍鸣
- 湖南省工信厅党组书记、厅长雷绍业一行莅临麒麟信安调研
- 使用BERT做中文文本相似度计算与文本分类
- 印象笔记,石墨笔记和Effie哪个更适合影评人?
- Python基础笔记(二)整数缓存、字符串驻留机制、字符串格式化等
- 华为计算机单位换算在哪里,单位换算