一、基础知识
pygame安装
pygame 就是一个Python模块,专为店子游戏设计
安装pygame(Windows):
pip install pygame
验证安装
python -m pygame.examples.aliens
会出现一个游戏界面,则表示安装成功。
pygame快速入门
1、使用pygame创建图形窗口
1.1游戏的初始化和退出
要使用pygame提供的所有功能之前,需要调用init方法 在游戏结束前需要调用quit方法
方法 | 说明 |
---|
pygame.init() | 导入并初始化所有pygame模块,使用其他模块之前,必须先调用init方法 | pygame.quit() | 卸载所有pygame模块,在游戏结束之前调用! |
1.2理解游戏中的坐标
坐标系: ?原点在左上角(0,0) ?x轴水平方向向右,逐渐增加 ?y轴垂直方向向下,逐渐增加 ?在游戏中,所有可见的元素都是以矩形区域来描述位置的: ??要描述一个矩形区域有四个要素:(x,y)(width,height)
pygame 专门提供了一个类pygame.Rect 用于描述矩形区域
Rect(x, y , width , height ) --->Rect
提示: ?pygame.Rect 是一个比较特殊的类,内部只是封装了一些数字计算 ?不执行pygame.init() 方法同样能够直接使用
例子:
import pygame
first_rect = pygame.Rect(100,500,120,125)
print(' 第一个矩形区域的原点为: %d %d' %(first_rect.x , first_rect.y))
print(' 第一个矩形区域的尺寸为:%d %d' %(first_rect.width , first_rect.height))
print('尺寸:',first_rect.size)
1.3创建游戏主窗口
pygame专 门提供了一个模块pygame.display 用于创建、管理游戏窗口
方法 | 说明 |
---|
pygame.display.set_mode() | 初始化游戏显示窗口 | pygame.display.update() | 刷新屏幕内容显示 |
set_mode 方法:
set_mode(resolution=(0,0),flags=0) -->Surface
- 作用 —创建游戏显示窗口
- 参数:
? resolution 指定屏幕的宽和高,默认创建的窗口大小和屏幕大小一致 ? flags 参数指定屏幕的附加选项,例如是否全屏等等,默认不需要传递 ? depth 参数表示颜色的位数,默认自动匹配 - 注意:必须使用变量记录set_mode方法的返回结果。因为:后续所有的图像绘制都基于这个返回结果
screen = pygame.display.set_mode((480,700))
1.4简单的游戏循环
- 为了做到游戏程序启动后,不会立即退出,通常会在游戏程序中添加一个游戏循环
- 游戏循环就是一个无限循环
- 在创建游戏串口代码下方,增加一个无限循环(使用 while true)
- ? 注意:游戏窗口不需要重复创建
2、理解图像并实现图像绘制
- 在游戏中,能够看到的游戏元素大多都是图像
- ? 图像文件初始是保存在磁盘上的,如果需要使用,第一步就需要被加载到内存中。
- 要在屏幕上看到某一个图像的内容,需要按照三个步骤:
- 1、使用
pygame.image.load() 加载图像的数据 - 2、使用游戏屏幕对象,调用
blit 方法,将图像绘制到指定位置 - 3、调用
pygame.display.update() 方法更新整个屏幕的显示 提示:要想在屏幕上看到绘制的结果,就一定要调用pygame.display.update()方法 例子:
bg=pygame.image.load("./images/background.png")
hero=pygame.image.load("./images/me1.png")
screen.blit(bg,(0,0))
screen.blit(hero,(200,500))
pygame.display.update()
理解update()方法的作用
- 使用display.set_mode()创建的screen对象是一个内存中的屏幕数据对象
- screen.blit方法可以在画布上绘制很多图像
- display.update()会将画布的最终结果绘制在屏幕上,这样可以提高屏幕绘制效率,增加游戏的流畅度。
3、理解游戏循环和游戏时钟
3.1游戏中动画实现原理
- 通过在电脑上每秒绘制60次,就能够到大非常连续高品质的动画效果。每次绘制的结果被成为帧Frame
3.2游戏循环
游戏的两个组成部分:
游戏循环的开始就意味着游戏的正式开始
游戏循环的作用: 1、保证游戏不会直接退出 2、变化图像位置—动画效果 ?每个1/60 秒移动一下所有图像的位置 ?调用pygame.display.update() 更新屏幕显示 3、检测用户交互 —按键、鼠标等
3.3游戏时钟
- pygame专门提供了一个类
pygame.time.Clock 可以非常方便的设置屏幕绘制速度—刷新帧率。 - 要使用时钟对象需要两步:
- 1)在游戏初始化创建一个时钟对象
- 2)在游戏循环中让时钟对象调用tick(帧率)方法
tick 方法会根据上次被调用的时间,自动设置游戏循环中的延时
clock = pygame.time.Clock()
clock.tick(60)
3.4简单动画实现
需求: ?1、在游戏初始化定义一个pygame.Rect的变量记录英雄的初始位置 ?2、在游戏循环中每次让英雄的y-1 ----向上移动 ?3、当y<=0时,将英雄丕东到屏幕的底部
提示: 每一次调用update()方法之前,需要把所有的游戏图像都重新绘制一边 而且应该最先重新绘制背景图像
3.5在游戏循环中监听事件
事件event
-
就是启动游戏后,用户针对游戏所做的操作 -
例如:点击关闭按钮,点击鼠标,按下键盘… 监听 -
在游戏循环中,判断用户具体操作 -
只有捕获到用户具体的操作,才能针对性的做出响应
pygame中通过pygame.event.get()可以获得用户当前所做动作的事件列表。 ?用户可以同一时间做很多事情 提示:这段代码非常固定,几乎所有的pygame游戏都大同小异。
例子:
for event in pygame.event.get():
if event.type == pygame.QUIT:
print("退出游戏...")
pygame.quit()
exit()
理解精灵和精灵组
在之前写的案列中,图像加载、位置变化、绘制图像都需要程序员编写代码分别处理。
为了简化开发步骤,pygame提供了两个类
- pygame.sprite.Sprite —存储图像数据image和位置rect对象
- pygame.sprite.Group
精灵(需要派生子类 |
---|
image 记录图像数据 rect 记录在屏幕上的位置 | update(*args):更新精灵位置 kill():从所有组中删除 |
精灵组 |
---|
init(self , *精灵): add(*sprites):向组中添加精灵 update(*args):让组中所有精灵调用update方法 sprites():返回所有精灵列表 draw(Surface):将组中所有精灵的image,绘制到Surface的rect位置 |
4.1创建游戏精灵:
新建一个plane_sprites.py文件 定义GameSprite类继承自pygame.sprite.Sprite
注意:
- 如果一个类的父类不是object
- 在重写初始化方法时,一定要先super()一下父类的__init__方法
- 保证父类中实现的__init__代码能够被正常执行
属性: - image精灵图像,使用image_name加载
- rect精灵大小,默认使用图像大小
- speed精灵移动速度,默认为1
方法: update每次更新屏幕时在游戏循环内调用 让精灵的self.rect.x+=self.speed 提示: image的get_rect()方法,可以返回pygame.Rect(0,0,图像宽,图像高)的对象
实现代码:
class GameSprite(pygame.sprite.Sprite):
"""飞机大战游戏的精灵"""
def __init__(self,image_name,speed=1):
super().__init__()
self.image = pygame.image.load(image_name)
self.rect = self.image.get_rect()
self.speed = speed
def update(self):
self.rect.y += self.speed
4.2使用游戏精灵和精灵组创建敌机
步骤 1、使用from导入plane_sprites模块
- from导入的模块可以直接使用
- import 导入的模块需要通过模块名来使用
2、在游戏初始化创建精灵对象和精灵组对象 3、在游戏循环中让精灵组分别调用update()和draw(screen)方法 职责 - 精灵
- 封装图像image、位置rect和速度speed
- 提供update()方法,根据游戏需求,更新位置rect
- 精灵组
- 包含多个精灵对象
- update方法,让精灵组中的所有精灵调用update方法更新位置
- draw(screen)方法,在screen上绘制精灵组中的所有精灵
代码
enemy = GameSprite("./images/enmey1.png")
enemy_group = pygame.sprite.Group(enemy)
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
print('游戏退出...')
pygame.quit()
exit()
enemy_group.update()
enemy_group.draw(screen)
游戏框架搭建
目标—使用面向对象设计飞机大战游戏类
目标:
01.明确主程序职责
一个游戏程序的职责可以分为两个部分:
- 游戏初始化
- 游戏循环
根据明确的职责,设计PlaneGame类如下: |PlaneGame| |:–| |screen |
PlaneGame |
---|
screen
clock 精灵或精灵组… | __init__(self) : __create_sprites(self):
start_game(self): __event_handler(self): __update_sprites(self): __game_over(): |
提示:根据职责封装私有方法,可以避免某一个方法的代码写得太过冗长,如果某一个方法编写的太长,及不好阅读,也不好维护
方法 | 职责 |
---|
__event_handler(self) | 事件监听 | __check_collide(self) | 碰撞检测—子弹销毁敌机、敌机撞毁英雄 | __update_sprites(self) | 精灵组更新和绘制 | __game_over() | 游戏结束 |
02.明确文件职责
plane_main : 1、封装主游戏类 2、创建游戏对象 3、启动游戏plane_sprites : 1、封装游戏中所有需要使用的精灵子类 2、提供游戏的相关工具 代码实现- 新建plane_main.py文件,并设置为可执行
- 编写基础代码
import pygame
from plane_sprites import *
class PlaneGame(object):
"""飞机大战主游戏"""
def __init__(self)
print("游戏初始化")
def start_game(self):
print("游戏开始")
while True:
pass
if __name__=='__main__':
game = PlaneGame()
game.start_game()
在游戏初始化中需要设置游戏窗口、创建游戏时钟、创建精灵、精灵组。 在PlaneGame类中的__init__函数中进行这些操作:
def __init__(self):
print("游戏初始化")
self.screen = pygame.display.set_mode(SCREEN_RECT.size)
self.clock = pygame.time.Clock()
self.__create_sprites()
def __create_sprites(self):
pass
在游戏循环中需要:设置刷新帧率、事件监听、碰撞检测、更新/绘制精灵组,更新屏幕显示。 代码如下:
def start_game(self):
print("游戏开始")
while True:
self.clock.tick(FRAME_PRE_SEC)
self.__event_handle()
self.__check_collide()
self.__update_sprites()
pygame.display.update()
def __event_handle(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
PlaneGame.__game_over()
elif:
pass
def __check_collide(self):
pass
def __update_sprites(self):
pass
@staticmethod
def __game_over():
print("游戏结束")
pygame.quit()
exit()
03.设计背景类
背景交替滚动的思路
游戏启动后,背景图像会连续不断地向下方移动 在视觉上产生飞机不断向上飞行的错觉: ?游戏背景不断变化 ?游戏的主角位置保持不变
解决方法: 1.创建两张背景图像精灵 ??第1张完全和屏幕重合 ??第2张在屏幕的正上方 2.两张图像一起向下方运动 ??self.rect.y +=self.speed 3.当任意背景精灵的 rect.y >= 屏幕的高度,说明移动到屏幕下方 4.将移动到屏幕下方的这张图像设置到屏幕的正上方 ??rect.y = -rect.height 设计一个背景类Background,继承于GameSprite类,并且有自己的方法。
提示: 继承如果父类提供的方法,不能满足子类的需求: 派生一个子类 在子类中针对特有的需求,重写父类方法,并且进行扩展
实现代码 plane_sprites.py
import pygame
SCREEN_RECT = pygame.Rect(0,0,480,700)
FRAME_PRE_SEC = 60
class GameSprite(pygame.sprite.Sprite):
"""飞机大战游戏精灵"""
def __init__(self,image_name,speed=1):
super().__init__()
self.image = pygame.image.load(image_name)
self.rect = self.image.get_rect()
self.speed = speed
def update(self):
self.rect.y += self.speed
class Background(GameSprite):
"""游戏背景精灵"""
def __init__(self,is_alt=Flase):
super().__init__("./images/background.png")
if is_alt:
self.rect.y = self.rect.height
def update(self):
super().update()
if self.rect.y>=SCREEN_RECT.height:
self.rect.y = -self.rect.height
在plane_main.py中显示背景精灵
1、在__create_sprites 方法中创建精灵和精灵组 2、在__update_sprites 方法中,让精灵组调用update() 和draw() 方法
class PlaneGame(object):
"""飞机大战主游戏"""
def __create_sprites(self):
bg1 = Background()
bf2 = Background(True)
self.back_group = pygame.sprite.Group(bg1,bg2)
def __update_sprites(self):
self.back_group.update()
self.back_group.draw(self.screen)
04敌机出场
01使用定时器添加敌机
1、游戏启动后,每隔1s会出现一架敌机 2、每架敌机向屏幕下方飞行,飞行速度各不相同 3、每架敌机出现的水平位置也不尽相同 4、当敌机从屏幕下方飞出,不会再飞回到屏幕中
1.1定时器
- 在pygame中可以使用pygame.time.set_time()来添加定时器
- 所谓定时器,就是每隔一段事件,去指定一些动作
set_timer(eventid,milliseconds) --->None
- set_timer可以创建一个事件
- 可以在游戏循环的事件监听方法中捕获到该事件
- 第1个参数事件代号需要基于常量pygame.USEREVENT来指定
USEREVENT是一个整数,再增加的事件可以使用USEREVENT+1指定,一次类推… - 第二个参数是事件触发间隔的毫秒值
定时器事件的监听
- 通过pygame.event.get()可以获取当前时刻所有的事件列表
- 遍历列表并且判断event.type是否等于eventid,如果相等,表示定时器事件发生。
1.2定义并监听创建敌机事件
pygame的定时器使用套路非常固定: 1、定义定时器常量—eventid 2、在初始化方法中,调用set_timer方法设置定时器事件 3、在游戏循环中,监听定时器事件
1)定义事件 在plane_sprites.py的顶部定义事件常量
CREATE_ENEMY_EVENT = pygame.USEREVENT
2)在初始化方法中,调用set_timer方法设置定时器事件
class PlaneGame(object):
def __ini__(self):
pygame.time.set_timer(CREATE_ENEMY_EVENT,1000)
3)在游戏循环中,监听定时器事件
def __event_handler(self):
for event in pygame.event.get():
if event.type == pygame.QUIT():
PlaneGame.__game_over()
elif event.type == CREATE_ENEMY_EVENT:
enemy = Enemy()
self.enemy_group.add(enemy)
02设计Enemy类
- 初始化方法
指定敌机图片 随机敌机的初始位置和初始速度 - 重写update()方法
判断是否飞出屏幕,如果是,从精灵组删除 代码
class Enemy(GameSprite):
"""敌机精灵"""
def __init__(self):
super().__init__("./images/enemy1.png")
self.spedd = random.randint(1,3)
self.rect.bottom = 0
self.rect.x = random.randint(0,SCREEN_RECT.width-self.rect.width)
def update(self):
super().update()
if self.rect.y >= SCREEN_RECT.height:
self.kill()
def __del__(self):
print("敌机挂了 %s"%self.rect)
创建好敌机类后,在plane_main.py文件中创建精灵并加入到精灵组。创建精灵在监听定时器事件中已完成 加入精灵组
def __create_sprites(self):
self.enemy_group = pygame.sprite.Gruop()
|