《Python编程:从入门到实践》笔记。
本章主要学习如何使用pygame编写一个简单的小飞机打外星人的游戏,因为本人对用python写游戏并非特别感兴趣,因此主要是看代码的构建和一些编程规范,代码会有所简略。
Python标准库中并无自带pygame
模块,因此须要自行安装,能够在控制台(Windows下是cmd)上使用命令行安装:pip install pygame
。若是你是用的PyCharm,也能够在设置中安装:python
点击右边的加号,在弹出的窗口中输入pygame,而后安装便可。程序员
该项目中须要使用一些书中的图片,这些图片均可以在 http://www.ituring.com.cn/boo... 中下载到。编程
首先须要新建一个项目,笔者取名为“alien_invasion”,并在该项目的根目录下新建一个images文件夹,用于存放项目中用到的图片。在本节中,咱们将先建立4个文件:微信
alien_invasion.py
:游戏主程序并发
settings.py
:游戏的配置文件框架
game_functions.py
:存放游戏的控制函数,好比响应鼠标、键盘等函数
ship.py
:飞船类学习
该模块通过重构后的代码以下:网站
import pygame import game_functions as gf from settings import Settings from ship import Ship def run_game(): # 初始化游戏并建立一个屏幕对象 pygame.init() # 初始化背景设置,让pygame能正常工做 ai_settings = Settings() # 实例化一个游戏配置类 # 返回一个游戏窗口 screen = pygame.display.set_mode( (ai_settings.screen_width, ai_settings.screen_height)) pygame.display.set_caption("Alien Invasion") # 给这个游戏窗口设置一个标题 ship = Ship(ai_settings, screen) # 实例化一个飞船类,传入了参数ai_settings和屏幕对象screen # 开始游戏的主循环 while True: gf.check_events(ship) # 用于响应游戏事件 ship.update() # 更新飞船状态 gf.update_screen(ai_settings, screen, ship) # 重绘screen run_game()
①代码第1行导入pygame
模块,它包含开发游戏所需的基本功能;spa
②代码3到5行导入的是自行编写且通过重构的模块;
③第9行代码执行游戏的初始化工做,好比初始化游戏背景等;
④第10行实例化一个游戏配置类,用于配置游戏参数,该类的具体实现见本篇后面的内容;
⑤代码第12-13行用于生成一个名为screen
的显示窗口,长宽从配置对象ai_settings
中读出;display.set_mode()
返回的是一个surface
,在pygame中,surface
是屏幕的一部分,用于显示游戏元素,这里的screen
表示的是整个游戏窗口。咱们激活游戏的循环后,每通过一次循环pygame都将重绘这个screen
。
⑥代码第20行的check_events()
函数用于响应游戏中发生的时间,好比鼠标,键盘,关闭窗口等。
⑦代码第21行用于更新飞船的信息,如飞船位置
⑧最后一行用于启动游戏,即初始化游戏,并开始主循环。
该文件主要是游戏的配置信息,存放游戏的各类参数。
class Settings: """存储《外星人入侵》的全部设置的类""" def __init__(self): """初始化游戏的设置""" # 屏幕设置 self.screen_width = 1200 # 游戏窗口宽度 self.screen_height = 800 # 游戏窗口高度 self.bg_color = (230, 230, 230) # 游戏背景颜色 self.ship_speed_factor = 1.5 # 飞船的移动速度
这里故意将飞船的速度设置为浮点数,也能够是整数。在设置游戏元素的位置时,若是直接使用浮点数,则只会截取整数部分。
该模块描述了一个飞船类的基本内容:
import pygame class Ship: def __init__(self, ai_settings, screen): """初始化飞机并设置其初始位置""" self.screen = screen self.ai_settings = ai_settings # 加载飞机图片并获取其外接矩形 self.image = pygame.image.load("images/ship.bmp") self.rect = self.image.get_rect() self.screen_rect = screen.get_rect() # 将每艘新飞船放在屏幕底部中央 self.rect.centerx = self.screen_rect.centerx self.rect.bottom = self.screen_rect.bottom # 自定义一个能存储浮点数的临时变量,x坐标 self.center = float(self.rect.centerx) # 标志,用于表示是否正在向某个方向移动 self.moving_right = False self.moving_left = False def update(self): """根据移动标志调整飞船的位置""" if self.moving_right and self.rect.right < self.screen_rect.right: self.center += self.ai_settings.ship_speed_factor if self.moving_left and self.rect.left > 0: self.center -= self.ai_settings.ship_speed_factor # 用临时变量更新rect的centerx,截取截取整数部分 self.rect.centerx = self.center def blitme(self): """在指定位置绘制飞船""" self.screen.blit(self.image, self.rect)
①__init__()
中的self.center
属性,代码将self.rect.centerx
即飞船的中心x坐标转换成浮点数,并将其存储在self.cente
r中。之因此转换成浮点数,是由于在settings.py
文件中,咱们将飞船移动速度设置成了浮点数。
②self.moving_right
和self.moving_left
标志,用于表示飞船是否正在移动,用于实现飞船在不松开按键下连续移动。
③udpate()
方法,用于增减飞船的中心位置x
坐标(由于飞船只能在底部移动,因此不用改y
坐标),并防止飞船移动出游戏窗口。
④重写了blitme()
函数,用于绘制飞船
该模块主要是集中处理游戏中发生的各类事件。
import sys import pygame def check_keydown_event(event, ship): """响应按下按键""" if event.key == pygame.K_RIGHT: ship.moving_right = True if event.key == pygame.K_LEFT: ship.moving_left = True def check_keyup_event(event, ship): """响应松开按键""" if event.key == pygame.K_RIGHT: ship.moving_right = False if event.key == pygame.K_LEFT: ship.moving_left = False def check_events(ship): """响应按键和鼠标事件""" for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: check_keydown_event(event, ship) elif event.type == pygame.KEYUP: check_keyup_event(event, ship) def update_screen(ai_settings, screen, ship): """更新屏幕上的图像,并切换到新屏幕""" # 每次循环时都重绘屏幕 screen.fill(ai_settings.bg_color) ship.blitme() # 让最近绘制的屏幕可见 pygame.display.flip()
①在pygame中,用K_RIGHT
,K_LEFT
表示方向按键,其实键盘上每一个键在pygame中都有所对于,以K_
开头。check_keydown_event()
函数和check_keyup_event()
函数都是对下面的check_event()
的进一步简化,这两个函数的代码都可以放在check_event()
中,但这样代码将会很臃肿,结构不清晰。
②check_event()
函数用于监听游戏的事件,好比pygame.QUIT
,它表示游戏推出事件;pygame.KEYDOWN
和pygame.KEYUP
分别表示键盘按下与松开事件。本次大循环中(外层的while
循环)发生的全部事件都存储在pygame.event
中,咱们使用get()
方法得到这些事件。
③在update_screen()
函数中,咱们使用screen
的fill()
方法填充窗体的背景色,调用blitme()
方法来在窗体中绘制飞船,最后,调用pygame.display.flip()
方法让最近的绘制在窗体中可见。
如今咱们运行alien_invasion.py
文件,咱们将获得以下窗体:
目前功能还比较简单,只能实现飞船的左右移动。
为了添加射击功能,须要先添加一个子弹类。
import pygame from pygame.sprite import Sprite class Bullet(Sprite): # 使用精灵 """一个对飞船发射的子弹进行管理的类""" def __init__(self, ai_settings, screen, ship): """在飞船所处的位置建立一个子弹对象""" super(Bullet, self).__init__() self.screen = screen # 在(0,0)处建立一个表示子弹的矩形,再设置正确的位置 self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height) self.rect.centerx = ship.rect.centerx # 从飞机的中央位置射出 self.rect.top = ship.rect.top # 从飞机的顶部射出 # 存储用浮点数表示的子弹位置,由于子弹只在y轴上运动,因此不须要x坐标 self.y = float(self.rect.y) self.color = ai_settings.bullet_color # 子弹颜色 self.speed_factor = ai_settings.bullet_speed_factor # 子弹速度 def update(self): """向上移动子弹""" # 更新表示子弹位置的浮点数值 self.y -= self.speed_factor # 更新表示子弹的rect的位置 self.rect.y = self.y def draw_bullet(self): """在屏幕上绘制子弹""" pygame.draw.rect(self.screen, self.color, self.rect)
首先咱们须要导入pygame模块以及其中的Sprite
类(直译的话叫作“精灵类”,然而这名字叫的真的很尴尬),它可让咱们在后面方便批量处理相同类型的同一操做,子弹类继承自Sprite
类。该子弹类并无使用图片,而是直接在screen上绘制矩形用于表示子弹。update()
方法用于更新子弹的位置。pygame.draw.rect()
用于在screen
上绘制子弹。
在该模块中添加子弹类的参数:
class Settings: def __init__(self): -- snip -- # 子弹设置 self.bullet_speed_factor = 1 self.bullet_width = 3 self.bullet_height = 15 self.bullet_color = 60, 60, 60 # 表示窗口中最多容许存在的子弹数,固然你也能够将其去掉 self.bullets_allowed = 3
游戏中咱们按空格键发射子弹,并发射子弹的过程单独写在一个函数fire_bullet()
中。为了响应空格键,须要修改check_event()
函数和check_keydown_event()
函数,前者只修改了参数,后者在判断结构中添加了一个判断。有了子弹类,那咱们还须要在screen
中绘制子弹,因此还须要修改update_screen()
函数,而子弹自身信息(好比子弹的移动)的修改则放在了一个新的函数update_bullets()
中。
import sys import pygame from Bullet import Bullet # 新增函数! def fire_bullet(ai_settings, screen, ship, bullets): """若是尚未到达限制,就发射一颗子弹""" # 建立新子弹,并将其加入到编组bullets中 # 若是你想让飞船能无限发射子弹,能够将判断语句删除 if len(bullets) < ai_settings.bullets_allowed: new_bullet = Bullet(ai_settings, screen, ship) bullets.add(new_bullet) # 修改了参数! def check_keydown_event(event, ship, ai_settings, screen, bullets): -- snip -- # 按空格键发射子弹 elif event.key == pygame.K_SPACE: fire_bullet(ai_settings, screen, ship, bullets) # 修改了参数! def check_events(ai_settings, screen, ship, bullets): """响应按键和鼠标事件""" for event in pygame.event.get(): -- snip -- elif event.type == pygame.KEYDOWN: # 增长了参数 check_keydown_event(event, ship, ai_settings, screen, bullets) -- snip -- # 修改了函数! def update_screen(ai_settings, screen, ship, bullets): -- snip -- # 绘制子弹 for bullet in bullets.sprites(): bullet.draw_bullet() -- snip -- # 新增函数 def update_bullets(bullets): """更新子弹的位置,并删除已消失的子弹""" # 更新子弹的位置 # bullets是个Group对象,调用一次update()就会调用其中全部Sprite对象的update() # 至关于你不用本身写for循环了 bullets.update() # 删除已消失的子弹 for bullet in bullets.copy(): if bullet.rect.bottom <= 0: bullets.remove(bullet)
当子弹从窗口中消失时,它并无从内存中消失,若是对于已经从屏幕中消失的子弹不作处理的话,时间一长,子弹数一多,光子弹一项的内存占用就会愈来愈多(土豪请忽略),虽然只是线性增加,但做为一个合格的程序员,应该避免这种无谓的浪费。
最后,咱们修改主程序,在其中添加一个pygame.sprite
中的Group
对象用于表示子弹集合,以及对该对象的操做代码。
import pygame from pygame.sprite import Group # 导入一个新类 import game_functions as gf from settings import Settings from ship import Ship def run_game(): -- snip -- bullets = Group() # 开始游戏的主循环 while True: gf.check_events(ai_settings, screen, ship, bullets) ship.update() gf.update_bullets(bullets) gf.update_screen(ai_settings, screen, ship, bullets)
如下是运行截图:
自此,咱们建立了一个能开火的小飞机,在下一篇文章中咱们将向其中添加外星人。
本篇中的代码都是通过了重构后的代码,可是,当咱们本身在编程时,若是对某一框架仍是小白,搞不清楚该如何组织代码,那就把全部代码都写在一个或几个文件里(虽然这种习惯很很差),也暂时不用考虑代码结构之类的问题,由于你的任务是造东西,而不是写漂亮代码,用精巧结构,用别人没看过的语法。二者能兼备固然更好,但每一个人都有当小白的时期,有必定熟练度后,再来考虑代码重构的问题。
迎你们关注个人微信公众号"代码港" & 我的网站 www.vpointer.net ~