总体目录
- 1.1、项目准备
- 1.2、使用
pygame
创建图形窗口 - 1.3、理解 图像 并实现图像绘制
- 1.4、理解 游戏循环 和 游戏时钟
- 1.5、理解 精灵 和 精灵组
一、项目准备
- 1.1、新建 飞机大战 项目
- 1.2、新建一个
JK_01_pygame入门.py
的模块 - 1.3、导入 游戏素材图片
- 1.4、游戏的本质
- 把一些 静止的图像 绘制到 游戏窗口 中
- 根据 用户的交互 或其他情况,移动 这些图像,产生动画效果
- 根据 图像之间 是否发生重叠,判断 敌机是否被摧毁 等其他情况
二、使用 pygame 创建图形窗口
-
2.1、总体内容
- (1)、游戏的初始化和退出
- (2)、理解游戏中的坐标系
- (3)、创建游戏主窗口
- (4)、简单的游戏循环
可以将图片素材 绘制 到 游戏的窗口 上,开发游戏之前需要先知道 如何建立游戏窗口
-
2.2、游戏的初始化和退出
- 先导入pygame :
import pygame
- 要使用
pygame
提供的所有功能之前,需要调用init
方法 - 在游戏结束前需要调用一下
quit
方法
方法 说明 pygame.init() 导入初始化所有 pygame 模块,使用其他模块之前,必须先调用 init 方法 pygame.quit() 卸载所有 pygame 模块,在游戏结束之前调用! - 先导入pygame :
-
2.3、理解游戏中的坐标系
- 坐标系
-
原点 在左上角
(0,0)
- x轴 水平方向 右,逐渐增大
-
x、y轴 垂直方向 下,逐渐增大
在游戏中,所有可见的元素 都是以 矩形区域 来描述位置的 ,要描述一个矩形区域有四个要素:
(x,y) (width,height)
-
pygame
专门提供了一个类pygame.Rect
用于描述 矩形区域语法:
Rect(x,y,width,height) -> rect
,使用如下import pygame hero_rect = pygame.Rect(200,300,50,50) print("%d %d"%hero_rect.size) print(hero_rect)
- 提示:
pygame.Rect
是一个比较特殊的类,内部只是封装了一些数字计算 - 不执行
pygame.init()
方法同样能够直接使用
- 提示:
-
原点 在左上角
- 坐标系
-
2.4、创建游戏主窗口
-
pygame
专门提供了一个 模块pygame.display
用于创建、管理 游戏窗口方法 说明 pygame.display.set_mode() 初始化游戏显示窗口 pygame.display.update() 刷新屏幕内容显示,稍后使用 -
set_mode 方法
set_mode(resolution=(0,0),flags=0,depth=0) -> Surface
作用:创建游戏显示窗口
-
参数:
-
resolution
指定屏幕的宽
和高
,默认创建的窗口大小和屏幕大小一致(是一个宽高的元组
) -
flag
参数指定屏幕的附加选项,例如是否全屏等等,默认不需要传递 -
depth
参数表示颜色的位数,默认自动匹配
-
返回值:暂时可以理解为 游戏的屏幕,游戏的元素 都需要被绘制到 游戏的屏幕 上
-
注意: 必须使用变量记录
set_mode
方法的返回结果!因为:后续所有的图像绘制都需要基于这个返回结果# 创建游戏主窗口(宽高设置为480*700) # screen = pygame.display.set_mode((480,700))
-
-
2.5、简单的游戏循环
为了做到游戏程序启动后,不会立即退出,通常会在游戏程序中增加一个 游戏循环
所谓的 游戏循环 就是一个 无限循环
在 创建游戏窗口 代码下方,增加一个无限循环
-
提示:游戏的窗口不需要重复创建
# 创建游戏主窗口 宽度:480 高度:700 screen = pygame.display.set_mode((480,700)) # 游戏循环 while True: pass
三、理解 图像 并实现图像绘制
-
3.1、在游戏中,能够看到的 游戏元素 大多数是 图像
- 图像文件 初始是保存在磁盘上的,如果需要使用,第一步 就需要 被加载到内存
-
3.2、要在屏幕上 看到某一个图像的内容,需要按照三个步骤:
- (1)、使用
pygame.image.load('图片路径')
加载图像的数据 - (2)、使用 游戏屏幕 对象,调用
blit
方法 将图像绘制到指定位置 - (3)、调用
pygame.display.update()
方法更新整个屏幕的显示
提示:想要在屏幕上看到绘制的结果,就一定要调用
pygame.display.update()
方法import pygame # 1、初始化游戏模块 pygame.init() # 2、创建游戏窗口 宽度:480 高度:700 screen = pygame.display.set_mode((480,700)) # 绘制背景图像 # 2.1、加载图像数据(.代表当前目录) bg = pygame.image.load("./images/background.png") # 2.2、blit 绘制图像到屏幕 screen.blit(bg,(0, 0)) # 2.3、update 更新屏幕显示 pygame.display.update() while True: pass # 3、退出游戏,卸载加载的所有模块, pygame.quit()
- (1)、使用
-
3.3、理解
update()
方法的作用可以在
screen
对象完成 所有bilt
方法之后,统一调用一次display.update
方法,同样可以在屏幕上 看到最终绘制的结果使用
display.set_mode()
创建的screen
对象 是一个 内存中的屏幕数据对象(可以理解为 油画的画布)-
screen.blit
方法可以在 画布 上绘制很多 图形- 例如: 英雄、敌机、子弹。。。
- 这些图像 有可能 会彼此 重叠或者覆盖
display.update()
会将 画布 的 最终结果 绘制在屏幕上,这样可以 提高屏幕绘制效率,增加游戏的流畅度
四、理解 游戏循环 和 游戏时钟
-
4.1、游戏中的动画实现原理
- 跟 电影 的实现原理类似,游戏中的动画效果,本质上是 快速 的在屏幕上绘制 图像
- 电影 是将多张 静止的电影胶片 连续、快速 的播放,产生连贯的视觉效果!
- 一般在电脑上 每秒绘制 60 次,就能达到非常 连续 高品质 的动画效果
- 每次绘制的结果被称为 帧 Frame
- 跟 电影 的实现原理类似,游戏中的动画效果,本质上是 快速 的在屏幕上绘制 图像
-
4.2、游戏循环 (意味着游戏的正式开始)
- 游戏的两个组成部分:游戏初始化 和 游戏循环
-
游戏循环 的作用
(1)、保证游戏 不会直接退出
-
(2)、变化图像位置 -- 动画效果
- 每隔
1/60 秒
移动一下所有图像的位置 - 调用
pygame.display.update()
更新屏幕显示
- 每隔
(3)、检测用户交互 -- 按键、鼠标等
- 游戏的两个组成部分:游戏初始化 和 游戏循环
-
4.3、游戏时钟
pygame
专门提供了一个类pygame.time.Clock
可以非常方便的设置屏幕绘制速度--刷新帧率要使用 时钟对象 需要两步:
- (1)、在 游戏初始化 创建一个 时钟对象
- (2)、在 游戏循环 中让时钟对象调用tick(帧率)
方法-
tick
方法会根据 上次调用的时间,自动设置 游戏循环 中的延时# 设置时钟对象 clock = pygame.time.Clock() # 游戏循环意味着游戏的开始 while True: # 可以指定循环体内部代码执行的帧率 clock.tick(60) pass
-
4.4、英雄的简单动画实现
需求:
- (1)、在 游戏初始化 定义一个
pygame.Rect
的变量记录英雄的初始位置 - (2)、在 游戏循环 中每次让 英雄的 y 减 1 向上移动
- (3)、
y <= 0
将英雄移动到屏幕的底部
提示:
- 每一次调用
update()
方法之前,需要把 所有的游戏图像都重新绘制一遍 - 而且应该 最先 重新绘制 背景图像
import pygame # 1、初始化游戏,加载所有的模块 pygame.init() # 2、创建游戏窗口 宽度:480 高度:700 screen = pygame.display.set_mode((480, 700)) # 绘制背景图像 # 2.1、加载图像数据 bg = pygame.image.load("./images/background.png") # 2.2、blit 绘制图像 screen.blit(bg, (0, 0)) # 加载英雄图像数据 hero = pygame.image.load("./images/me1.png") # blit 绘制英雄图像 screen.blit(hero, (150,500)) # 2.3、update 更新屏幕显示 pygame.display.update() # 定义 英雄的初始位置 hero_rect = pygame.Rect(150,500,102,126) # 2.4、设置时钟对象 clock = pygame.time.Clock() # 游戏循环意味着游戏的正式开始 while True: # 可以指定循环体内部的代码执行的频率 clock.tick(60) # 修改飞机的位置 hero_rect.y -= 1 # 调用blit方法绘制图像 screen.blit(bg,(0,0)) screen.blit(hero,hero_rect) # 调用 update() 方法更新显示 pygame.display.update() # 3、退出游戏,卸载加载的所有模块, pygame.quit()
- (1)、在 游戏初始化 定义一个
-
4.5、在游戏循环中 监听 事件
-
事件 event
- 就是游戏启动后,用户针对游戏所坐的操作
- 例如:点击关闭按钮,点击鼠标,按下键盘...
-
监听
- 在游戏循环中,判断用户 具体的操作
只有 捕获 到用户具体的操作,才能有针对性的做出相应
- 在游戏循环中,判断用户 具体的操作
-
代码实现:
-
pygame
中通过pygame.event.get()
可以获得 用户当前所做动作 的 事件列表 (用户在同一事件做很多的事情)
-
-
提示:这段代码非常的固定,很多
pygame
游戏都 大同小异
# 游戏循环 while True: # 设置屏幕刷新的帧率 clock.tick(60) # 事件的监听(捕获事件) for event in pygame.event.get(): # 判断用户是否点击了关闭按钮 if event.type == pygame.QUIT: print("退出游戏") # 卸载所有的模块 pygame.quit() # 直接退出系统(终止当前正在执行的程序) exit()
-
五、 精灵 和 精灵组
-
5.1、精灵 和 精灵组**
- 在刚刚完成的案例中,图像加载、位置变化、绘制图像 都需要程序员编写代码分别处理
- 为了简化开发的步骤,
pygame
提供了两个类-
pygame.sprite.Spring
-- 存储 图像数据 image 和 位置 rect 的 对象 -
pygame.sprite.Group
-
提示:有了精灵 和 精灵组,仍然要调用
pygame.display.update()
才能在屏幕看到最终的结果 -
5.2、派生精灵子类
- (1)、新建
plan_sprites.py
文件 - (2)、定义
GameSprite
继承于pygame.sprite.Sprite
- 注意:
- 如果一个类的 父类 不是
object
,在重写 初始化方法 的时候,一定要先super()
一下父类的__init__
方法 -
在保证父类中实现的
__init__
代码能够正常执行
- 如果一个类的 父类 不是
- 属性
-
image
精灵图像,使用image_name
加载 -
rect
精灵大小,默认使用图像大小 -
speed
精灵移动速度,默认为1
-
- 方法
-
update
每次更新屏幕时在游戏循环内调用(让精灵的self.rect.y += self.speed
)
-
- 提示:
-
image
的get_rect()
方法,可以返回pygame.Rect(0,0,图像宽,图像高)
的对象import pygame class GameSprites(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
-
- (1)、新建
-
5.3、使用 游戏精灵 和 精灵组 创建敌机
需求:使用刚刚派生的 游戏精灵 和 精灵组 创建敌机并且实现敌机动画
-
步骤:
-
(1)、使用
from
导入plan_sprites
模块-
from
导入的模块可以 直接使用 -
import
导入的模块需要通过 模块名. 来使用
-
(2)、在 游戏初始化 创建 精灵对象 和 精灵组对象
(3)、在 游戏循环中 让 精灵组 分别调用
update()
和draw(screen)
方法
-
-
职责:
- 精灵:
- 封装 图像 image、位置 rect 和 速度 speed
- 提供
upate()
方法,根据游戏需求,更新位置 rect
- 精灵组:
- 包含 多个 精灵对象
-
update
方法,然精灵组中的所有精灵调用update
方法更新位置 -
draw(screen)
方法,在 screen 上绘制精灵组中所有精灵
- 精灵:
-
代码:
# 创建敌机的精灵对象 enemy1 = GameSprite("./images/enemy1.png") enemy2 = GameSprite("./images/enemy1.png") # 创建敌机的精灵组 enemy_group = pygame.sprite.Group(enemy1, enemy2) # 让精灵组调用两个方法 # update - 让组中的所有精灵更新位置 enemy_group.update() # draw - 在screen上绘制所有的精灵 enemy_group.draw(screen)
-
5.4、到目前位置所有的代码
-
plan_main.py 文件
import pygame from plan_sprites import * # 1、初始化游戏,加载所有的模块 pygame.init() # 2、创建游戏窗口 宽度:480 高度:700 screen = pygame.display.set_mode((480, 700)) # 绘制背景图像 # 2.1、加载图像数据 bg = pygame.image.load("./images/background.png") # 2.2、blit 绘制图像 screen.blit(bg, (0, 0)) # 加载英雄图像数据 hero = pygame.image.load("./images/me1.png") # blit 绘制英雄图像 screen.blit(hero, (150,500)) # 2.3、update 更新屏幕显示 pygame.display.update() # 定义 英雄的初始位置 hero_rect = pygame.Rect(150,500,102,126) # 2.4、设置时钟对象 clock = pygame.time.Clock() # 创建敌机的精灵对象 enemy1 = GameSprites("./images/enemy1.png") enemy2 = GameSprites("./images/enemy1.png",2) # 创建敌机的精灵组 enemy_group = pygame.sprite.Group(enemy1, enemy2) while True: # 可以指定循环体内部的代码执行的频率 clock.tick(60) # 事件的监听 for event in pygame.event.get(): # 判断用户是否点击了关闭按钮 if event.type == pygame.QUIT: print("退出游戏") # 卸载所有的模块 pygame.quit() # 直接退出系统 exit() # 判断飞机的位置 if hero_rect.y <= 0: hero_rect.y = 700 # 修改飞机的位置 hero_rect.y -= 1 # 调用blit方法绘制图像 screen.blit(bg,(0,0)) screen.blit(hero,hero_rect) # 让精灵组调用两个方法 # update - 让组中的所有精灵更新位置 enemy_group.update() # draw - 在screen上绘制所有的精灵 enemy_group.draw(screen) # 调用 update() 方法更新显示 pygame.display.update() # 3、退出游戏,卸载加载的所有模块, pygame.quit()
-
plan_sprites.py
import pygame class GameSprites(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
-