大家好,我是一名西安电子科技大学的大一学生,刚刚接触到编程,现在在也处于学习阶段,因此我会在学习的同时,去用 markdown 去写一些笔记,然后会分享给大家,但是由于时间有限,可能很久才会更新一次,一般来说是不会带图片的。
而且都是全程干货!
希望能帮助到大家!
大家有意见可以评论或是留言,我有时间一定会回复!
文章目录
pygame:
关于 pygame:
pygame 是一组功能强大的有趣的模块。
可用于图形管理、动画、乃至声音,可轻松的开发出复杂的游戏。
导入 pygame 模块:
import sys
import pygame
from pygame.locals import *
关于 sys 的导入:
通常会配合sys一起导入,sys中含有使程序退出的函数,sys.exit()。
pygame.locals 中的常量:
pygame.locals中包含一系列可能用到的常量,QUIT,K_ESCAPE。
可以直接去调用他们。
pygame 中关于方法、模块和数据类型:
在 pygame 中,是 font 和 surface 模块,在这些模块中,是 Font 和 Surface 数据类型。
pygame 程序员用小写字母打头表示一个模块名,用大写字母打头表示一个数据类型,使得我们能够加以区分。
pygame.Rect:
Rect 对象表示一个矩形空间区域的位置和大小。
位置可以通过 Rect 对象的属性来确定。这些属性是表示 X 坐标和 Y 坐标的一个整数值的元组。
矩形的大小可通过 width 属性和 height 属性来确定。这些属性决定矩形的宽和高是多少个像素。
Rect 对象有一个 colliderect() 方法,用来检查矩形是否和其他 Rect 对象有碰撞。
pygame.Surface:
Surface 对象是一个带颜色的像素区域。
Surface 对象表示一个矩形图像,而 Rect 对象表示一个矩形的空间和位置。
Surface 对象有一个 blit() 方法,它可以将一个 Surface 对象绘制到另一个 Surface 对象之上。
由 pygame.display.set_mode() 函数返回的 Surface 对象是特殊的,在该 Surface 对象上绘制的任何物体都会显示到用户的屏幕上。
pygame.event.Event:
当用户提供鼠标、键盘和其他类型的输入时,pygame.event 模块会创建一个 Event 对象。
pygame.event.get() 函数返回这些 Event 对象的一个列表。
可通过查看 Event 对象的 type 属性来查看事件的类型:QUIT, KEYDOWN, KEYUP。
pygame.font.Font:
pygame.font 模块拥有一个 Font 数据类型,用于表示 pygame 中的文本字体。
传递给 pygame.font.SysFont() 函数的参数时表示字体名称的一个字符串以及表示字体大小的一个整数。
传递 None 作为字体名称以获取系统默认字体。
pygame.time.Clock:
pygame.time 模块中的 Clock 对象有助于避免程序运行得过快。
Clock 对象有一个 tick() 方法,它接收的参数表示想要游戏运行的速度时多少帧 (FPS)。
FPS 越高,游戏运行速度越快。
初始化 pygame:
# set up pygame
pygame.init()
方法 init():
在调用其他pygame函数之前都要先调用pygame.init()函数初始化。
通过创建游戏实例运行:
在文件的末尾,可以通过创建游戏的实例并调用 run_game()。
if __name__ == '__main__':
ai = my_game
ai = run_game()
设置鼠标的可见度:
pygame.mouse.set_visible(boolen)
可向其传入一个布尔值,表示鼠标是否可见。
设置 pygame 窗口:
# set up the window.
windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('hello world!')
方法 set_mode() 和 GUI:
调用pygame中的display模块中的set_mode()方法去创建图形化用户界面(graphical user interface, GUI)。pygame模块中也有自己的模块。
去生成一个窗口,而surface 变量 windowSurface 就表示那个窗口。
向set_mode()传递一个元组,表示窗口的宽度和高度。
pygame.display.set_mode() 是个特殊函数,生成的 Surface 是个特殊的对象,能代表整个屏幕。
全屏游戏:
pygame.display.set_mode() 函数的第二个参数是可选的。
我们可以传递 pygame.FULLSCREEN 常量,使得窗口占据整个屏幕,而不是显示一个小窗口。
像素:
像素是计算机屏幕上的最小的点。
生成的窗口是以像素为单位的。
屏幕上的单个像素可以以任何颜色显示。
屏幕上的所有像素一起工作显示出你所看到的照片。
Surface 对象:
set_mode()函数返回一个 pygame.Surface 对象。
对象(object)是对拥有方法的数据类型的值的另一种叫法。
Surface 对象就表示这个窗口。
对象 == 数据 + 方法。
使用函数 pygame.set_caption() 设置窗口标题:
向其传入一个字符串表示窗口的标题。
设置颜色变量:
# set up the color.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
RGB 颜色:
光主要有三种颜色:红、绿、蓝。通过这三种颜色可以组合出任意颜色。
在 python 中,颜色数据就是3个整数的元组,叫RGB颜色。
第一个值代表有多少红色,0表示没用红色,255代表红色达到了最大值。
第二个值表示绿色,第三值表示蓝色。
定义颜色常量:
由于我们不希望每次都要在程序中用一个具体的颜色时都要重新编写3个整数的元组。
所有我们可以创建一系列颜色常量。
颜色 | RGB值 |
---|---|
Black | (0, 0, 0) |
Blue | (0, 0, 255) |
Gray | (128, 128, 128) |
Green | (0, 255, 0) |
Lime | (0, 128, 0) |
Purple | (128, 0, 128) |
Red | (255, 0, 0) |
Teal | (0, 128, 128) |
White | (255, 255, 255) |
Yellow | (255, 255, 0) |
将文本写到 pygame 窗口上:
# set up the fonts.
basicFont = pygame.font.SysFont(None, 48)
# set up the text.
text = basicFont.render('hello world!', True, White, Blue)
# set up the location.
textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery
使用字体来样式化文本:
字体是有统一风格绘制的一整套字母,文字,数字,符号和字符。
python 程序不能修改字体,但是 pygame 可以以任何字体来绘制文本。
Font对象:
使用两个参数来调用 pygame.font.SysFont() 函数创建一个 pygame.font.Font对象。
-
第一个参数是字体的名称,传递 None 表示使用系统默认字体。
-
第二个参数是字体的大小(以点为单位)。
将字符串 ‘hello world!‘ 绘制到窗口上形成一幅图像,叫做渲染(rendering)。
渲染一个 Font 对象:
储存在变量 basicFont 中的 Font 对象有一个叫做 render() 的方法。
这个方法将返回一个 Surface 对象,文本就绘制其上。
方法 render():
方法 render() 有多个参数:
-
第一个参数表示要绘制的文本字符串。
-
第二个参数指定是否想要抗锯齿的一个 Boolean 值,为文本抗锯齿,会使文本看起来更平滑些。
-
第三个参数和第四个参数都是一个 RGB 元组。
-
第三个参数是文本的颜色,第四个参数是文本背后的背景颜色。
我们将这个 Font 对象赋值给 text 。
使用 Rect 属性(attribute)设置文本位置:
一旦设置了 Font 对象,需要将其放置到窗口的一个位置上。
pygame.Rect 数据类型表示特定大小和位置的矩形区域。我们使用它来设置窗口中对象的位置。
各种 Rect 属性:
使用函数 get_rect() 获取某一个 Font 对象的 Rect 属性。
Rect 数据类型有多个属性:.centerx centery。
我们需要访问这些属性,并为这些属性赋值。
pygame.Rect 属性 | 描述 |
---|---|
myRect.centerx | 矩形的中央的X坐标的整数值 |
myRect.centery | 矩形的中央的Y坐标的整数值 |
myRect.left | 矩形的左边的X坐标的整数值 |
myRect.right | 矩形的右边的X坐标的整数值 |
myRect.top | 矩形的顶部的Y坐标的整数值 |
myRect.bottom | 矩形的底部的Y坐标的整数值 |
myRect.width | 矩形宽度的整数值 |
myRect.height | 矩形高度的整数值 |
myRect.size | 两个整数的一个元组:(width, height) |
myRect.topleft | 两个整数的一个元组:(left, top) |
myRect.topright | 两个整数的一个元组:(right, top) |
myRect.bottomleft | 两个整数的一个元组:(left, bottom) |
myRect.bottomright | 两个整数的一个元组:(right, bottom) |
myRect.midleft | 两个整数的一个元组:(left, centery) |
myRect.midright | 两个整数的一个元组:(right, centery) |
myRect.midtop | 两个整数的一个元组:(centery, top) |
myRect.midbottom | 两个整数的一个元组:(centery, bottom) |
构造函数 constructor functions:
pygame.Rect() 函数和 pygame.Rect 数据类型的名称相同。
与其数据类型的名称形同并且创建其数据类型的对象或值的函数,叫做构造函数。
用一种颜色填充整个 Surface 对象:
pygame 的初始化背景是一个黑框框。
# Draw the white background onto the surface.
windowSurface.fill(White)
fill() 函数:
fill() 函数会使用一种颜色来填充整个窗口屏幕。
pygame.display.update() 函数:
当调用 fill() 或是其他函数时,屏幕上的窗口不会改变。
但是他们会改变 Surface 对象。
需要调用 pygame.display.update() 函数来更新画面。
因为计算机内存中修改 Surface 对象比在屏幕上修改图像要快很多。
pygame 的绘图函数:
在 pygame 中,每一种图形都有自己的函数,可以将这些形状组合到不同的照片中,以便于图形化游戏。
绘制一个多边形 pygame.draw.polygon() 函数:
pygame.draw.polygon() 函数可以绘制指定的任意多边形。
多边形是由多条直线边组成的形状,圆和椭圆不是多边形。
# Draw a green polygon onto the surface.
pygame.draw.polyon(windowSurface, Green, ((146, 0), (291, 106), (56, 277)
(236, 277), (0, 106)))
该函数的参数是:
-
要在其上绘制多边形的 Surface 对象。
-
多边形的颜色。
-
由要依次绘制的点的XY坐标的元组的元组所构成的一个元组。最后一个元组将自动连接到第一个元组的点。
-
可选项:表示多边形的线条宽度的整数值。如果没有这个选项,多边形将会填充。
绘制直线 pygame.draw.line() 函数:
从屏幕上一点到另一点绘制一条直线:
# Draw some blue line onto the surface.
pygame.draw.line(windowSurface, Blue, (60, 60), (120, 60), 4)
pygame.draw.line(windowSurface, Blue, (120, 60), (60, 120))
pygame.draw.line(windowSurface, Blue, (60, 120), (120, 120), 4)
pygame.draw.line() 函数的参数依次是:
-
要在其上绘制直线的 Surface 对象。
-
直线的颜色。
-
直线起点和终点分别的 XY 元组。
-
可选项: 直线宽度的整数值。
绘制圆形 pygame.draw.circle() 函数:
# Draw a blue circle onto the surface.
pygame.draw.circle(windowSurface, Blue, (300, 50), 20, 0)
pygame.draw.circle() 函数的参数依次是:
-
要在其上绘制圆的 Surface 对象。
-
圆的颜色。
-
表示圆心的 XY 坐标的两个整数的元组。
-
表示圆的半径(大小)的整数值。
-
可选项:线条宽度的整数值。宽度为0,表示填充圆。
绘制椭圆 pygame.draw.ellipse() 函数:
通过传入椭圆的外接矩形绘制:
# Draw a red ellipse onto the surface.
pygame.draw.ellipse(windowSurface, Red, (300, 250, 40, 80), 1)
pygame.draw.ellipse() 函数的参数依次是:
-
要在其上绘制椭圆的 Surface 对象。
-
椭圆的颜色。
-
传递分别表示椭圆左上角的 X 和 Y 坐标以及椭圆的宽和高的4个整数的一个元组。
-
可选项:表示线条宽度的整数值。宽度为0表示填充椭圆。
绘制矩形 pygame.draw.rect() 函数:
# Draw the text's background rectangle onto the surface.
pygame.draw.rect(windowSurface, Red, (textRect.left - 20, textRect.top - 20
text.Rect.width + 40, textRect.height + 40))
pygame.draw.rect() 函数的参数依次是:
-
要在其上绘制矩形的 Surface 对象。
-
矩形的颜色。
-
包含了矩形的左上角的 X 和 Y 的坐标,以及矩形的宽和高的四个整数的一个元组。也可以给第三个对象传递一个 Rect 对象,而不是 四个整数。
给像素着色 PixelArray 对象:
为单独像素设定颜色。
创建一个 pygame.PixelArray 对象,是颜色元组的列表的一个列表,这些颜色元组传递给它的 Surface 对象。
PinxArray 对象能让我们进行跟高的像素级别的控制,因此,如果需要在屏幕上绘制非常详细或是定制化图像,而不是较大的图形是,可以选择 PixelArray 对象。
# Get a pixel array of the surface.
pixArray = pygame.PixelArray(windowSurface)
pixArray[480][380] = Black
使用 PixArray 对象让屏幕上的一个像素点变成黑色。
需要把 Surface 对象作为参数传递给 pygame.PixelArray() 函数调用。
PixelArray 对象的第一个索引是 X 的坐标,第二个索引是 Y 的坐标。
删除 PixelArray 对象:
从一个 Surface 对象创建一个 PixelArray 对象,将会锁定这个 Surface 对象。
锁定意味着该 Surface 对象不能调用 blit() 函数。
要解锁这个 Surface 对象,必须用 del 操作符删除 PixelArray 对象。
del pixelArray
如果忘记删除 PixelArray 对象,就会产生异常。
pygame.error : Surfaces must not be locked during blit.
Surface 对象的 blit() 方法:
blit() 方法是将一个 Surface 对象的内容绘制到另一个 Surface 对象之上。
render() 方法所创建的所有文本对象,都存在于其自己的 Surface 对象上,我们的文本储存在 text 变量中而不是绘制到 windowSurface 上。为了将 text 绘制到我们想要让其出现的 Surface 上,必须使用 blit() 方法:
# Draw the text onto the surface.
windowSurface.blit(text, textRect)
blit() 方法的两个参数:
-
第一个参数是要加入在其上的 Surface 对象。
-
第二个参数指定了应该将 text surface 绘制到 windowSurface 上的何处。
text 的Rect 对象通过调用 text.get_rect() 获取。
将 Surface 对象绘制到屏幕上:
在调用 pygame.display.update() 函数之前,并不会真的将任何内容绘制到屏幕上。
# Draw the window onto the screen.
pygame.display.update()
为了节省内存,我们并不想在每次调用了绘图函数之和都更新到屏幕上。
只有在所有绘制函数都调用完后,才想要一次性更新屏幕。
事件和游戏循环:
pygame 程序会不断地运行叫做游戏循环 ( game loop ) 的一个循环。在这个程序中,游戏循环的所有代码行每秒钟都会执行一百次左右。
游戏循环是不断的查看新事件、更新窗口的状态并在屏幕上绘制窗口的一个循环。
任何时候,当用户按下一个按键、点击或移动鼠标或使得其他一些事件发生的时候,pygame 都会创建该对象。
获取事件对象和退出程序:
事件 ( event ) 是 pygame.event.Event 数据类型的对象。
调用 pygame.event.get() 函数检索自从上次调用 pygame.event.get() 后所生成的任何新的 pygame.event.Event 对象。这些事件会以 Event 对象的一个列表的形式返回。
所有的 Event 对象都有一个叫做 type 的属性,它会告诉我们事件是什么类型。
# Run the game loop.
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
使用一个 for 循环遍历了 pygame.event.get() 所返回的列表中的每一个 Event 对象,如果事件的 type 属性等于常量 QUIT,那么我们就知道了产生了 QUIT 事件。
当用户关闭程序窗口,或者当计算机关闭并尝试终止所有运行程序的时候,pygame 模块会产生 QUIT 事件。
pygame.quit() 函数时和 pygame.init() 函数相对的一种函数。在退出函数之前,需要调用它。
pygame hello world! 创建图形示例:
import pygame, sys
from pygame.locals import *
# Set up pygame.
pygame.init()
# Set up the window.
windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('Hello world!')
# Set up the colors.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# Set up fonts.
basicFont = pygame.font.SysFont(None, 48)
# Set up the text.
text = basicFont.render('Hello world!', True, WHITE, BLUE)
textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery
# Draw the white background onto the surface.
windowSurface.fill(WHITE)
# Draw a green polygon onto the surface.
pygame.draw.polygon(windowSurface, GREEN, ((146, 0), (291, 106), (236, 277), (56, 277), (0, 106)))
# Draw some blue lines onto the surface.
pygame.draw.line(windowSurface, BLUE, (60, 60), (120, 60), 4)
pygame.draw.line(windowSurface, BLUE, (120, 60), (60, 120))
pygame.draw.line(windowSurface, BLUE, (60, 120), (120, 120), 4)
# draw a blue circle onto the surface.
pygame.draw.circle(windowSurface, BLUE, (300, 50), 20, 0)
# Draw a red ellipse onto the surface.
pygame.draw.ellipse(windowSurface, RED, (300, 250, 40, 80), 1)
# Draw the text's background rectangle onto the surface.
pygame.draw.rect(windowSurface, RED, (textRect.left - 20, textRect.top - 20, textRect.width + 40, textRect.height + 40))
# Get a pixel array of the surface.
pixArray = pygame.PixelArray(windowSurface)
pixArray[480][380] = BLACK
del pixArray
# Draw the text onto the surface.
windowSurface.blit(text, textRect)
# Draw the window onto the screen.
pygame.display.update()
# Run the game loop.
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
创建动画图像:
为了让物体有动画效果,我们将在游戏循环的每一次迭代中,让这些物体移动一些像素,这会使得物体看上去像是在屏幕上移动一样。
-
通过游戏循环,实现物体动画。
-
改变一个物体的运动方向。
设置变量常量:
# Set up the window.
WINDOWWIDTH = 400
WINDOWHEIGHT = 400
pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption('Animation')
为窗口的高度和宽度设置常量变量。
用于方向的常量变量:
# Set up the direction variables.
DOWNLEFT = 'downleft'
DOWNRIGTH = 'downright'
UPLEFT = 'upleft'
UPRIGHT = 'upright'
# Set up the moving speed.
MOVESPEED = 4
我们也可以不使用常量,而使用任何想要的值来表示这些方向。
设置移动速度:告诉程序,每一次迭代中每个物体应该移动多少像素。
设置物体结构数据:
要定义积木:创建一个字典来表示每个移动的积木的数据结构。
这个字典有 ‘rect’ 键、‘color’ 键和 ‘dir’ 键,分别控制 物体的 位置形状、颜色和移动方向。
# Set up the box data structure.
b1 = {'rect':pygame.Rect(300, 80, 50, 100), 'color':RED, 'dir':UPRIGHT}
b2 = {'rect':pygame.Rect(200, 200, 20, 20), 'color':GREEN, 'dir':UPLEFT}
b3 = {'rect':pygame.Rect(100, 150, 60, 60), 'color':BLUE, 'dir':DOWNLEFT}
boxes = [b1, b2, b3]
需要从列表中访问一个值,可以通过索引和键值,及两层方括号来进行。
游戏循环的任务:
游戏循环负责实现物体移动的动画:
- 绘制一系列不同的图像,这些图像一个接一个的显示。
- 在每一个图像中,积木都会移动4个像素位置。
- 图像显示的如此之快,以至于积木看上去就像是在屏幕上平滑的移动。
游戏循环的每一次迭代,代码都会重新绘制整个窗口,使得新的积木位置随时间而改变几个像素。
它会重新绘制一个 Rect 而不会擦去旧的 Rect 绘制,如果我们只是让游戏循环不断的在屏幕上绘制 Rect 对象,最终将会得到一条 Rect 对象的一条轨迹,而不是一个平滑的动画。
为了避免这种情况,在游戏循环的每一次迭代中,我们都需要清除窗口。
# Draw the white background onto the surface.
windowSurface.fill(White)
在屏幕表面绘制一层白色去覆盖原本的绘制图案。
移动物体:
为了更新每一个物体,我们需要在游戏循环中加入遍历每一个列表:
-
我们需要根据每个物体已经移动的方向来修改每个积木,以便使用 if 语句来检查物体数据结构的 dir 键,从而判断积木的移动方向。
-
然后,我们将根据积木在该方向上的移动来修改积木的位置。
for b in boxes:
# Move the box data structure.
if b['dir'] == DOWNLEFT:
b['rect'].left -= MOVESPEED
b['rect'].top += MOVESPEED
if b['dir'] == DOWNRIGHT:
b['rect'].left += MOVESPEED
b['rect'].top += MOVESPEED
if b['dir'] == UPLEFT:
b['rect'].left -= MOVESPEED
b['rect'].top -= MOVESPEED
if b['dir'] == UPRIGHT:
b['rect'].left += MOVESPEED
b['rect'].top -= MOVESPEED
为 left 属性和 top 属性设置新值,取决于积木的方向。
-
如果积木的方向是 DOWNLEFT 或 DOWNRIGHT,那么就要增加 top 属性。
-
如果积木的方向是 UPLEFT 或 UPRIGHT,那么就要减少 top 属性。
-
如果积木的方向是 DOWNLEFT 或 UPLEFT,那么就要增加 left 属性。
-
如果积木的方向是 DOWNRIGHT 或 UPRIGHT,那么就要减少 left 属性。
通过 MOVESPEED 中储存的整数(每次迭代中物体将要移动的像素数目)来修改这些属性的值。
物体的边缘反弹:
判断一个物体是否越过了窗口的边缘,如果越过了,就要把物体“弹回”。
在代码中,就意味着该 for 循环将把积木的 ’dir‘ 键设置成一个新的值,物体将在下一次游戏迭代中朝着新的方向移动,使得物体看上去就像是从窗口边缘弹回一样。
# Check whether the box has moved out of the window.
if b['rect'].top < 0:
# The box has moved past the top.
if b['dir'] == UPLEFT:
b['dir'] = DOWNLEFT
if b['dir'] == UPRIGHT:
b['dir'] = DOWNRIGHT
if b['rect'].bottom > WINDOWHEIGHT:
# The box has moved past the bottom.
if b['dir'] == DOWNLEFT:
b['dir'] = UPLEFT
if b['dir'] == DOWNRIGHT:
b['dir'] = UPRIGHT
if b['rect'].left < 0:
# The box has moved past the left side.
if b['dir'] == DOWNLEFT:
b['dir'] = DOWNRIGHT
if b['dir'] == UPLEFT:
b['dir'] = UPRIGHT
if b['rect'].right > WINDOWWIDTH:
# The box has moved past the right side.
if b['dir'] == DOWNRIGHT:
b['dir'] = DOWNLEFT
if b['dir'] == UPRIGHT:
b['dir'] = UPLEFT
通过物体的 Rect 对象的 left, right, top, bottom 属性来判断物体是否到达了边缘。
将积木绘制到窗口中的新位置:
每次积木移动的时候,我们都需要调用 pygame.draw.rect() 函数,在窗口中新的位置绘制他们。
# Draw the box onto the surface.
pygame.draw.rect(windowSurface, b['color'], b['rect'])
函数参数依次是:
Surface 对象:windowSurface。
颜色。
决定位置和大小的 Rect 对象。
看得起物体,处理一团‘模糊’:
调用 pygame.display.update() 在屏幕上绘制 windowSurface。
# Draw the window onto the screen.
pygame.display.update()
time.sleep(0.02)
如果程序全速运行的话,计算机能够很快的移动、弹跳和绘制物体,物体看上去就会是一团模糊。
为了让程序运行的足够慢,以便我们能够看清物体。
当添加函数 time.sleep() 时,会在物体每一次移动之间,暂停0.02秒。
动画图形实例:Animation:
import pygame, sys, time
from pygame.locals import *
# Set up pygame.
pygame.init()
# Set up the window.
WINDOWWIDTH = 1000
WINDOWHEIGHT = 800
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption('Animation')
# Set up direction variables.
DOWNLEFT = 'downleft'
DOWNRIGHT = 'downright'
UPLEFT = 'upleft'
UPRIGHT = 'upright'
MOVESPEED = 4
# Set up the colors.
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# Set up the box data structure.
b1 = {'rect':pygame.Rect(300, 80, 50, 100), 'color':RED, 'dir':UPRIGHT}
b2 = {'rect':pygame.Rect(200, 200, 20, 20), 'color':GREEN, 'dir':UPLEFT}
b3 = {'rect':pygame.Rect(100, 150, 60, 60), 'color':BLUE, 'dir':DOWNLEFT}
boxes = [b1, b2, b3]
# Run the game loop.
while True:
# Check for the QUIT event.
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
# Draw the white background onto the surface.
windowSurface.fill(WHITE)
for b in boxes:
# Move the box data structure.
if b['dir'] == DOWNLEFT:
b['rect'].left -= MOVESPEED
b['rect'].top += MOVESPEED
if b['dir'] == DOWNRIGHT:
b['rect'].left += MOVESPEED
b['rect'].top += MOVESPEED
if b['dir'] == UPLEFT:
b['rect'].left -= MOVESPEED
b['rect'].top -= MOVESPEED
if b['dir'] == UPRIGHT:
b['rect'].left += MOVESPEED
b['rect'].top -= MOVESPEED
# Check whether the box has moved out of the window.
if b['rect'].top < 0:
# The box has moved past the top.
if b['dir'] == UPLEFT:
b['dir'] = DOWNLEFT
if b['dir'] == UPRIGHT:
b['dir'] = DOWNRIGHT
if b['rect'].bottom > WINDOWHEIGHT:
# The box has moved past the bottom.
if b['dir'] == DOWNLEFT:
b['dir'] = UPLEFT
if b['dir'] == DOWNRIGHT:
b['dir'] = UPRIGHT
if b['rect'].left < 0:
# The box has moved past the left side.
if b['dir'] == DOWNLEFT:
b['dir'] = DOWNRIGHT
if b['dir'] == UPLEFT:
b['dir'] = UPRIGHT
if b['rect'].right > WINDOWWIDTH:
# The box has moved past the right side.
if b['dir'] == DOWNRIGHT:
b['dir'] = DOWNLEFT
if b['dir'] == UPRIGHT:
b['dir'] = UPLEFT
# Draw the box onto the surface.
pygame.draw.rect(windowSurface, b['color'], b['rect'])
# Draw the window onto the screen.
pygame.display.update()
time.sleep(0.02)
碰撞检测:
碰撞检测(collision detection)负责计算机屏幕上的两个物体何时发生彼此接触(也就是发生碰撞)。
在 pygame 中,我们将添加的图像都按照矩形来处理,因此更容易去检测碰撞。
在游戏中,碰撞检测将判断两个矩形是否发生彼此重叠。
使用一个时钟来同步程序:
通过调用 time.sleep() 函数可以减缓程序的执行速度,使得程序不会运行的太快。
而 time.sleep() 函数的问题是:这会使所有计算机上都延迟一会,而程序剩下的部分的速度则取决于计算机有多快。
如果想在任何的计算机上都以相同的速度运行,我们需要一个函数能够对于较快的计算机暂停的时间长一点,而较慢的计算机暂停的时间短一点。
pygame.time.Clock 对象可以对于任何计算机都暂停适当的时间。
# Set up the Clock.
mainClock = pygame.time.Clock()
mainClock.tick(40)
用 pygame.time.Clock() 函数创建一个 Clock 对象。
通过调用 tick() 方法,使无论计算机有多块,都是每秒迭代40次,确保游戏速度不会超过预期。
在游戏循环中只能调用一次 tick() 。
关于帧:
帧 ( frame ) 是游戏循环单次迭代中所绘制的一个屏幕,也可以为帧去设置其常量。
检测碰撞:
所有的 Rect 对象都拥有碰撞的检测方法 colliderect():
# Check whether the player has intersected with any food squares.
for food in foods[:]:
if player.colliderect(food):
foods.remove(food)
pygame.Rect 对象的 colliderect() 方法,作为一个参数传递给玩家的矩形的 pygame.Rect 对象。
如果两个矩形不碰撞的话,将会返回 False,碰撞将会返回 True。
处理事件:
pygame 模块可以产生事件以相应来自鼠标和键盘的用户输入。
如下是 pygame.event.get() 能够返回的事件:
-
QUIT 当用户关闭窗口时触发的事件。
-
KEYDOWN / KEYUP 当用户按下或释放一个键盘时触发的事件。有一个 key 属性来识别按下的时哪个键,一个 mod 属性来表示是否有 Shift、Ctrl、Alt或其他键同时按下。
-
MOUSEMOTION 任何时候,当鼠标移动经过窗口时都会触发该事件。有一个 pos 属性,它返回鼠标在窗口中的坐标的元组 (x, y)。rel 属性也返回一个元组,但是是相对于上一次的事件的坐标。buttons 属性返回包含3个整数的一个元组,第一个整数是鼠标左键,第二个整数是鼠标中间键,第三个整数是鼠标右键,当鼠标移动时,如果按下了这些键,这些整数值为1,如果没有按下,这些整数值为0。
-
MOUSEBUTTONDOWN / MOUSEBUTTONUP 当窗口中按下或释放鼠标按键时触发的事件。这个事件有一个 pos 属性,它是当按下或释放鼠标时,鼠标所在位置的坐标的 (x, y) 元组。也有一个botton 属性,用 1 ~ 5 表示触发了哪个键。
键值 鼠标键 1 左键 2 中间键 3 右键 4 向上滑轮 5 向下滑轮 我们将使用这些事件来让玩家通过 KEYDOWN 事件和鼠标按钮点击来控制游戏。
处理 KEYDOWN / KEYUP 事件:
事件对象有一个 key 属性来识别按下的是哪个键。
我们用 if 语句来检查按键。
# Check for events.
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
# Change the keyboard variables.
if event.key == K_LEFT or event.key == K_a:
moveRight = False
moveLeft = True
if event.key == K_RIGHT or event.key == K_d:
moveLeft = False
moveRight = True
if event.key == K_UP or event.key == K_w:
moveDown = False
moveUp = True
if event.key == K_DOWN or event.key == K_s:
moveUp = False
moveDown = True
if event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_LEFT or event.key == K_a:
moveLeft = False
if event.key == K_RIGHT or event.key == K_d:
moveRight = False
if event.key == K_UP or event.key == K_w:
moveUp = False
if event.key == K_DOWN or event.key == K_s:
moveDown = False
if event.key == K_x:
player.top = random.randint(0, WINDOWHEIGHT - player.height)
player.left = random.randint(0, WINDOWWIDTH - player.width)
以下是键盘按键常量:
pygame 常量变量 | 键盘按键 | pygame 常量变量 | 键盘按键 |
---|---|---|---|
K_LEFT | 左方向键 | K_PAGEUP | Page Up 键 |
K_RIGHT | 右方向键 | K_PAGEDOWN | Page Down 键 |
K_UP | 上方向键 | K_a | A 键 |
K_DOWN | 下方向键 | K_w | W 键 |
K_ESCAPE | ESC 键 | K_s | S 键 |
K_BACKSPAVE | Backspace 键 | K_d | D 键 |
K_TAB | TAB 键 | K_F1 | F1 键 |
K_RETURN | 回车键 | K_F2 | F2 键 |
K_SPACE | 空格键 | K_F3 | F3 键 |
K_DELETE | Delete 键 | K_F4 | F4 键 |
K_LSHIFT | 左 Shift 键 | K_F5 | F5 键 |
K_RSHIFT | 右 Shift 键 | K_F6 | F6 键 |
K_LCTRL | 左 Ctrl 键 | K_F7 | F7 键 |
K_RCTRL | 右 Ctrl 键 | K_F8 | F8 键 |
K_LALT | 左 Atl 键 | K_F9 | F9 键 |
K_RALT | 右 Atl 键 | K_F10 | F10 键 |
K_HOME | HOME 键 | K_F11 | F11 键 |
K_END | END 键 | K_F12 | F12 键 |
如果用户按下 Esc 键,游戏就会终止。
在调用 sys.exit() 函数之前,必须先调用 pygame.quit() 函数。
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
碰撞与按键实例:Collision Detection。
import pygame, sys, random
from pygame.locals import *
# Set up pygame.
pygame.init()
mainClock = pygame.time.Clock()
# Set up the window.
WINDOWWIDTH = 400
WINDOWHEIGHT = 400
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption('Input')
# Set up the colors.
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
WHITE = (255, 255, 255)
# Set up the player and food data structure.
foodCounter = 0
NEWFOOD = 40
FOODSIZE = 20
player = pygame.Rect(300, 100, 50, 50)
foods = []
for i in range(20):
foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))
# Set up movement variables.
moveLeft = False
moveRight = False
moveUp = False
moveDown = False
MOVESPEED = 6
# Run the game loop.
while True:
# Check for events.
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
# Change the keyboard variables.
if event.key == K_LEFT or event.key == K_a:
moveRight = False
moveLeft = True
if event.key == K_RIGHT or event.key == K_d:
moveLeft = False
moveRight = True
if event.key == K_UP or event.key == K_w:
moveDown = False
moveUp = True
if event.key == K_DOWN or event.key == K_s:
moveUp = False
moveDown = True
if event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_LEFT or event.key == K_a:
moveLeft = False
if event.key == K_RIGHT or event.key == K_d:
moveRight = False
if event.key == K_UP or event.key == K_w:
moveUp = False
if event.key == K_DOWN or event.key == K_s:
moveDown = False
if event.key == K_x:
player.top = random.randint(0, WINDOWHEIGHT - player.height)
player.left = random.randint(0, WINDOWWIDTH - player.width)
if event.type == MOUSEBUTTONUP:
foods.append(pygame.Rect(event.pos[0], event.pos[1], FOODSIZE, FOODSIZE))
foodCounter += 1
if foodCounter >= NEWFOOD:
# Add new food.
foodCounter = 0
foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))
# Draw the white background onto the surface.
windowSurface.fill(WHITE)
# Move the player.
if moveDown and player.bottom < WINDOWHEIGHT:
player.top += MOVESPEED
if moveUp and player.top > 0:
player.top -= MOVESPEED
if moveLeft and player.left > 0:
player.left -= MOVESPEED
if moveRight and player.right < WINDOWWIDTH:
player.right += MOVESPEED
# Draw the player onto the surface.
pygame.draw.rect(windowSurface, BLACK, player)
# Check if the player has intersected with any food squares.
for food in foods[:]:
if player.colliderect(food):
foods.remove(food)
# Draw the food.
for i in range(len(foods)):
pygame.draw.rect(windowSurface, GREEN, foods[i])
# Draw the window onto the screen.
pygame.display.update()
mainClock.tick(40 )
在屏幕上移动玩家:
移动玩家:
根据用户的按键,设置了移动变量 (moveDown、moveUp、moveLeft、moveRight)。
通过调整玩家的 X 和 Y 坐标 (储存在 pygame.Rect 对象中),来移动玩家。
# Move the player.
if moveDown and player.bottom < WINDOWHEIGHT:
player.top += MOVESPEED
if moveUp and player.top > 0:
player.top -= MOVESPEED
if moveLeft and player.left > 0:
player.left -= MOVESPEED
if moveRight and player.right < WINDOWWIDTH:
player.right += MOVESPEED
转移玩家:
也可以添加转移按键。
比如,当玩家按下 X 键时,将会随机传送玩家。
if event.key == K_x:
player.top = random.randint(0, WINDOWHEIGHT - player.height)
player.left = random.randint(0, WINDOWWIDTH - player.width)
将玩家绘制到屏幕上:
# Draw the player onto the surface.
pygame.draw.rect(windowSurface, BLACK, player)
处理鼠标移动事件:
通过鼠标移动这一事件,改变玩家的 Rect 属性,让玩家随鼠标移动。
if event.type == MOUSEMOTTION:
# if the mouse moves, move the player to the cursor.
player.centerx = event.pos[0]
player.centery = event.pos[1]
声音和图像:
主要内容:
- 声音文件和图像文件。
- 绘制精灵。
- 添加音乐和声音。
- 打开和关闭声音。
使用精灵添加图像:
精灵 (sprite) 是指用作屏幕上图像的一部分的一个单独的二位图像。
把精灵图像绘制到背景之上。
我们可以水平方向反转精灵图像,以使得精灵面朝别的方向。
可以在相同的窗口中多次绘制相同的精灵图像。
也可以重新调整精灵的大小,使其比初始的精灵图像更大或更小。
可以把背景图看作是很大的精灵。
声音和图像文件:
精灵是存储在计算机上的图像文件。 图片文件格式即图像文件存放的格式,通常有JPEG、TIFF、RAW、BMP、GIF、PNG等 。
pygame 可以使用几种不同的图像格式。pygame 支持的图像格式包含 BMP, PNG, JPG, GIF。
而 BMP 是最简单最适用的类型。
类: | 区别: |
---|---|
JPG | JPEG图像格式是最常见也最常用的。它能够将图像压缩在很小的储存空间,但是会丢失一些图像数据,尤其是在压缩比例越高的情况下图像质量更低。但是JPEG的压缩技术也同时是它的应用优势。它用有损压缩方式去除冗余的图像数据,在获得极高的压缩率的同时能展现十分丰富生动的图像,换句话说,就是可以用最少的磁盘空间得到较好的图像品质。同时 JPEG是一种很灵活的格式,具有调节图像质量的功能,允许用不同的压缩比例对文件进行压缩,支持多种压缩级别,应用环境会更多,也更加方便。 |
BMP | BMP图片格式使用范围非常广。我们经常在收藏好文章里的图片时,会发现保存下来的图片格式为BMP,占用空间较大。因为它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息,可以和其他 Microsoft Windows 程序兼容,最适合做墙纸。 |
PNG | PNG图像文件存储格式,其设计目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。PNG的名称来源于“可移植网络图形格式(Portable Network Graphic Format,PNG)”,也有一个非官方解释“PNG’s Not GIF”,是一种位图文件(bitmap file)存储格式,读作“ping”。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的α通道数据。PNG使用从LZ77派生的无损数据压缩算法,一般应用于JAVA程序、网页或S60程序中,原因是它压缩比高,生成文件体积小。 |
GIF | GIF(Graphics Interchange Format)的原义是“图像互换格式”,是CompuServe公司在 1987年开发的图像文件格式。GIF文件的数据,是一种基于LZW算法的连续色调的无损压缩格式。其压缩率一般在50%左右,它不属于任何应用程序。GIF格式可以存多幅彩色图像,如果把存于一个文件中的多幅图像数据逐幅读出并显示到屏幕上,就可构成一种最简单的动画。GIF格式自1987年由CompuServe公司引入后,因其体积小、成像相对清晰,特别适合于初期慢速的互联网,而大受欢迎。 |
PSD和AI | PSD格式是最适合调整的。它可以支持图层、通道、蒙板和不同色彩模式的各种图像特征,是一种非压缩的原始文件保存格式。扫描仪不能直接生成该种格式的文件。PSD文件可以保留所有的原始操作信息,所以占用空间会很大,但是对于后期修改、继续操作更有保存意义。如果你在制作需要反复推敲修改的图像时,选保存为PSD格式是非常有必要的。在这里顺便提一下AI格式。它也是一种分层文件,是一种矢量图形文件。每个对象都是独立的,他们具有各自的属性,如大小、形状、轮廓、颜色、位置等。以这种格式保存的文件便于修改,这种格式文件可以在任何尺寸大小下按最高分辨率输出。 |
TIF | TIF图像格式大多数被用作LOGO设计。它支持多种编码方法,其中包括RGB无压缩、RLE压缩、JPEG压缩等。TIFF可以说是比较复杂的一种,具有扩展性、方便性、可改性,可以提供给IBMPC等环境中运行、图像编辑程序。 |
EPS | EPS格式主要用于矢量图像和光栅图像的存储。EPS格式采用 PostScript语言进行描述,并且可以保存其他一些类型信息,例如多色调曲线、Alpha通道、分色、剪辑路径、挂网信息和色调曲线等,因此EPS 格式常用于印刷或打印输出。 |
RAW | RAW格式图片细节更严密,更方便调整。RAW文件大多是通过相机直接输出的,照片的锐度、白平衡、色阶和颜色的调节都可以进行调整。所以,为了更加方便查阅和后期处理照片,摄影师通常采用同时记录JPEG和RAW格式照片的办法,既可以让用户使用常规的图像处理软件组织和编辑照片(JPEG);当需要获得处理精细的照片或需要改善照片缺憾(如白平衡不正确和高光/暗部细节缺失 )的时候, 用户可以使用RAW解决问题。不过RAW格式照片目前还存在软件兼容性问题,你可以下载专门的RAW处理软件来进行。 |
pygame 支持的声音文件格式有 MID, WAV, MP3。
添加一个精灵:
我们将使用3个不同的变量来表示玩家,而不是像以前只有一个变量。
# Set up the block data structure.
player = pygame.Rect(300, 100, 40, 40)
playerImage = pygame.image.load('player.png')
player 变量没有包含玩家的图像,只有玩家的大小和位置。
变量 playerImage 用于储存玩家的精灵图像。
函数 pygame.image.load() 接收一个字符串参数,这是要加载的图像的文件名,返回一个 Surface 对象,把图像文件中的图形绘制到这个对象上。我们把这个 Surface 对象保存到 playerImage 中。
改变一个精灵的大小:
我们使用 pygame.transform 模块中的一个新的函数。
playerStretchedImage = pygame.transform.scale(playerImage, (40, 40))
pygame.transform.scale() 函数可以缩小和放大一个精灵,第一个参数是在其上绘制了图像的 Surface 对象,第二个参数是一个二元元组,表示新的图像的宽度和高度。
添加声音文件:
在 pygame 中有两个声音模块:
- pygame.mixer 模块可以在游戏中播放简短音效。
- pygame.mixer 模块可以播放背景音乐。
调用 pygame.mixer.Sound() 函数来创建一个 pygame.mixer.Sound 对象。
这个对象有一个 play() 方法,调用该方法可以播放音效。
调用 pygame.mixer.music.load() 函数来加载背景音乐。
调用 pygame.mixer.music.play() 函数开始播放背景音乐:
- 第一个参数告诉 pygame 播放完一次后还要播放多少次背景音乐 (传入5,会播放6次),-1 是一个特殊值,传入-1会一直循环播放。
- 第二个参数是开始播放声音文件的位置,传入 0.0 将从头开始播放,2.5 会从 2.5 秒处开始播放。
# Set up the music.
pickUpSound = pygame.mixer.Sound('pickup.wav')
pygame.mixer.music.load('background.mid')
pygame.mixer.music.play(-1, 0.0)
musicPlaying = True
最后,musicPlaying 变量有一个 Boolean 值,告诉程序是否要播放背景音效,给玩家一个选项,让他们自己决定是否要播放。
切换和关闭声音:
我们设置 M 键可以打开和关闭背景音乐。
如果把 musicPlaying 设置为 True,那么现在正在播放背景音乐,可以通过 pygame.mixer.music.stop() 来停止音乐。
如果把 musicPlaying 设置为 Flase,那么现在没有播放背景音乐,可以通过 pygame.mixer.music.play() 来开始播放音乐。
可以通过 if 语句来做到这一点:
if event.key == K_m:
if musicPlaying:
pygame.mixer.music.stop()
else:
pygame.mixer.music.play(-1, 0.0)
musicPlaying = not musicPlaying
无论打开还是关闭背景音乐,我们都想切换 musicPlaying 中的值。
Sprits and Sounds 程序的运行示例:
import pygame, sys, time, random
from pygame.locals import *
# Set up pygame.
pygame.init()
mainClock = pygame.time.Clock()
# Set up the window.
WINDOWWIDTH = 400
WINDOWHEIGHT = 400
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption('Sprites and Sounds')
# Set up the colors.
WHITE = (255, 255, 255)
# Set up the block data structure.
player = pygame.Rect(300, 100, 40, 40)
playerImage = pygame.image.load('player.png')
playerStretchedImage = pygame.transform.scale(playerImage, (40, 40))
foodImage = pygame.image.load('cherry.png')
foods = []
for i in range(20):
foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - 20), random.randint(0, WINDOWHEIGHT - 20), 20, 20))
foodCounter = 0
NEWFOOD = 40
# Set up keyboard variables.
moveLeft = False
moveRight = False
moveUp = False
moveDown = False
MOVESPEED = 6
# Set up the music.
pickUpSound = pygame.mixer.Sound('pickup.wav')
pygame.mixer.music.load('background.mid')
pygame.mixer.music.play(-1, 0.0)
musicPlaying = True
# Run the game loop.
while True:
# Check for the QUIT event.
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
# Change the keyboard variables.
if event.key == K_LEFT or event.key == K_a:
moveRight = False
moveLeft = True
if event.key == K_RIGHT or event.key == K_d:
moveLeft = False
moveRight = True
if event.key == K_UP or event.key == K_w:
moveDown = False
moveUp = True
if event.key == K_DOWN or event.key == K_s:
moveUp = False
moveDown = True
if event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == K_LEFT or event.key == K_a:
moveLeft = False
if event.key == K_RIGHT or event.key == K_d:
moveRight = False
if event.key == K_UP or event.key == K_w:
moveUp = False
if event.key == K_DOWN or event.key == K_s:
moveDown = False
if event.key == K_x:
player.top = random.randint(0, WINDOWHEIGHT - player.height)
player.left = random.randint(0, WINDOWWIDTH - player.width)
if event.key == K_m:
if musicPlaying:
pygame.mixer.music.stop()
else:
pygame.mixer.music.play(-1, 0.0)
musicPlaying = not musicPlaying
if event.type == MOUSEBUTTONUP:
foods.append(pygame.Rect(event.pos[0] - 10, event.pos[1] - 10, 20, 20))
foodCounter += 1
if foodCounter >= NEWFOOD:
# Add new food.
foodCounter = 0
foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - 20), random.randint(0, WINDOWHEIGHT - 20), 20, 20))
# Draw the white background onto the surface.
windowSurface.fill(WHITE)
# Move the player.
if moveDown and player.bottom < WINDOWHEIGHT:
player.top += MOVESPEED
if moveUp and player.top > 0:
player.top -= MOVESPEED
if moveLeft and player.left > 0:
player.left -= MOVESPEED
if moveRight and player.right < WINDOWWIDTH:
player.right += MOVESPEED
# Draw the block onto the surface.
windowSurface.blit(playerStretchedImage, player)
# Check whether the block has intersected with any food squares.
for food in foods[:]:
if player.colliderect(food):
foods.remove(food)
player = pygame.Rect(player.left, player.top, player.width + 2, player.height + 2)
playerStretchedImage = pygame.transform.scale(playerImage, (player.width, player.height))
if musicPlaying:
pickUpSound.play()
# Draw the food.
for food in foods:
windowSurface.blit(foodImage, food)
# Draw the window onto the screen.
pygame.display.update()
mainClock.tick(40 )
多文件项目 AlienInvasion:
多文件项目:
alieninvasion.py
import sys
from time import sleep
import pygame
from settings import Settings
from ship import Ship
from bullet import Bullet
from alien import Alien
from game_stats import GameStats
from button import Button
from scoreboard import Scoreboard
class AlienInvasin:
"""管理游戏资源和行为"""
def __init__(self):
"""初始化游戏并创建游戏资源。"""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
# 创建一个用于储存游戏统计信息的实例。
# 创建记分牌。
self.stats = GameStats(self)
self.sb = Scoreboard(self)
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
self._create_fleet()
# 创建play按钮。
self.play_button = Button(self, "Play! ")
# 设置背景色
self.screen.fill(self.settings.bg_color)
def run_game(self):
"""开始游戏的主循环"""
while True:
self._check_events()
if self.stats.game_active:
self.ship.update()
self._update_bullets()
self._update_aliens()
self._update_screen()
def _check_events(self):
"""相应按键和鼠标事件。"""
# 监视键盘和鼠标事件。
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
self._check_play_button(mouse_pos)
def _check_play_button(self, mouse_pos):
"""在玩家点击play时开始游戏"""
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and not self.stats.game_active:
# 重置游戏设置。
self.settings.initialize_dynamic_settings()
# 重置游戏统计信息。
self.stats.reset_stats()
self.stats.game_active = True
self.sb.prep_score() # 上一局记分清0。
self.sb.prep_level() # 上一局等级清0.
self.sb.prep_ships() # 重置飞船数目
# 清空剩余的外星人和子弹。
self.aliens.empty()
self.bullets.empty()
# 创建一群新外星人并让飞船居中。
self._create_fleet()
self.ship.center_ship()
# 隐藏鼠标光标。
pygame.mouse.set_visible(False)
def _check_keydown_events(self, event):
"""响应按键"""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
# 按 Q 键退出游戏。
elif event.key == pygame.K_q:
sys.exit()
# 定义按 空格 键开火发射子弹。
elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_keyup_events(self, event):
"""响应松开"""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
"""创建一颗子弹,并将其加入编组bullets中。"""
# 限制子弹数目, 最大为4。
if len(self.bullets) < self.settings.bullet_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _create_fleet(self):
"""创建外星人群。"""
# 创建一个外星人并计算一行可以容纳多少个外星人。
# 外星人的间距为外星人的宽度。
alien = Alien(self)
alien_width, alien_height = alien.rect.size
available_space_x = self.settings.screen_width - (2 * alien_width)
number_aliens_x = available_space_x // (2 * alien_width)
# 计算屏幕课容纳多少行外星人。
ship_height = self.ship.rect.height
available_space_y = (self.settings.screen_height - (3 * alien_height) - (2 * ship_height))
number_rows = available_space_y // (2 * alien_height)
# 创建外星人群。
for row_number in range(number_rows):
for alien_number in range(number_aliens_x):
self._create_alien(alien_number, row_number)
def _create_alien(self, alien_number, row_number):
"""创建一个外星人并将其放在当前行"""
alien = Alien(self)
alien_width, alien_height = alien.rect.size
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
self.aliens.add(alien)
def _update_bullets(self):
"""更新子弹位置并删除消失的子弹。"""
# 更新子弹的位置。
self.bullets.update()
# 删除消失的子弹。
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
self._check_bullet_alien_collisions()
def _check_bullet_alien_collisions(self):
"""相应子弹和外星人碰撞。"""
# 检查是否有子弹击中外星人。
# 如果是,就删除相应的子弹和外星人。
collisions = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)
if collisions:
for aliens in collisions.values():
self.stats.score += self.settings.alien_points * len(aliens)
self.sb.prep_score()
self.sb.check_high_score()
if not self.aliens:
# 删除现有子弹并创建新的外星人群。
self.bullets.empty()
self._create_fleet()
self.settings.increase_speed()
# 提高等级:
self.stats.level += 1
self.sb.prep_level()
def _update_aliens(self):
"""检查外星人是否到屏幕边缘,并更新外星人群中所有外星人的位置。"""
self._check_fleet_edges()
self.aliens.update()
# 检测外星人和飞船之间的碰撞。
if pygame.sprite.spritecollideany(self.ship, self.aliens):
self._ship_hit()
# 检查是否有外星人到达了屏幕底端。
self._check_aliens_bottom()
def _update_screen(self):
"""更新屏幕上的图像,并切换到新屏幕。"""
# 让每次循环都重新绘制屏幕。
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
self.aliens.draw(self.screen)
# 显示得分。
self.sb.show_score()
# 如果游戏处于非活跃状态就会绘制play标签。
if not self.stats.game_active:
self.play_button.draw_button()
# 让最近绘制的屏幕可见。
pygame.display.flip()
def _check_fleet_edges(self):
"""有外星人到达边缘时采取的措施。"""
for alien in self.aliens.sprites():
if alien.check_edges():
self._change_fleet_direction()
break
def _change_fleet_direction(self):
"""将整个外星人下移,并改变他们的方向。"""
for alien in self.aliens.sprites():
alien.rect.y += self.settings.fleet_drop_speed
self.settings.fleet_direction *= -1
def _ship_hit(self):
"""响应飞船被外星人撞到。"""
if self.stats.ships_left > 0:
# 将ship_left减1并更新记分牌:
self.stats.ships_left -= 1
self.sb.prep_ships()
# 清空余下的外星人群和子弹。
self.aliens.empty()
self.bullets.empty()
# 创建一个新的外星人群,并将飞船放到屏幕中央。
self._create_fleet()
self.ship.center_ship()
# 暂停0.5秒。
sleep(0.5)
else:
self.stats.game_active = False
pygame.mouse.set_visible(True)
def _check_aliens_bottom(self):
"""检查外星人是否到达了屏幕底端。"""
screen_rect = self.screen.get_rect()
for alien in self.aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
# 像飞船被撞一样处理。
self._ship_hit()
break
if __name__ == '__main__':
# 创建游戏实例并运行游戏。
ai = AlienInvasin()
ai.run_game()
Settings.py
class Settings:
"""储存游戏《外星人入侵》中的所有设置的类"""
def __init__(self):
"""初始化游戏的静态设置"""
# 屏幕设置:
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230) # 屏幕显示颜色
# 飞船设置:
self.ship_speed = 0.5
self.ship_limit = 3 # 玩家的飞船数量。
# 子弹设置:
self.bullet_speed = 1.5
self.bullet_width = 5
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
# 限制子弹数目,最大为4
self.bullet_allowed = 4
# 外星人设置:
self.alien_speed = 0.3
self.fleet_drop_speed = 5
# fleet_direction为1表示向右移动,为-1表示向移动。
self.fleet_direction = 1
# 加快游戏的节奏。
self.speedup_scale = 1.1
# 外星人分数提高速度:
self.score_scale = 1.5
self.initialize_dynamic_settings()
def initialize_dynamic_settings(self):
"""初始化随游戏进度变化的设置。"""
self.ship_speed = 0.5
self.bullet_speed = 1.5
self.alien_speed = 0.3
# fleet_direction为1表示向右,-1表示向左。
self.fleet_direction = 1
# 记分:
self.alien_points = 10
def increase_speed(self):
"""提高游戏速度和外星人分数的设置"""
self.ship_speed *= self.speedup_scale
self.bullet_speed *= self.speedup_scale
self.alien_speed *= self.speedup_scale
self.alien_points = int(self.alien_points * self.score_scale)
ship.py
import pygame
from pygame.sprite import Sprite
class Ship(Sprite):
"""管理飞船的类"""
def __init__(self, ai_game):
"""初始化飞船并设置其初始化位置。"""
super().__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
self.screen_rect = ai_game.screen.get_rect()
# 加载飞船图像并获取其外借矩形。
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
# 对于每艘飞船,都将其放置在屏幕底部中央。
self.rect.midbottom = self.screen_rect.midbottom
# 在飞船属性x中储存小数点值。
self.x = float(self.rect.x)
# 移动标志
self.moving_right = False
self.moving_left = False
def update(self):
"""根据移动标志调整飞船位置。"""
# 更新飞船而不是rect的对象x值。
if self.moving_right and self.rect.right < self.screen_rect.right:
self.x += self.settings.ship_speed
elif self.moving_left and self.rect.left > 0:
self.x -= self.settings.ship_speed
# 根据 self.x更新rect对象。
self.rect.x = self.x
def blitme(self):
"""在指定位置绘制飞船。"""
self.screen.blit(self.image, self.rect)
def center_ship(self):
"""让碰撞后的飞船回到屏幕中部。"""
self.rect.midbottom = self.screen_rect.midbottom
self.x = float(self.rect.x)
alien.py
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""表示单个外星人的类"""
def __init__(self, ai_game):
"""初始化外星人并设置其起始位置。"""
super().__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
# 加载外星人图像并设置其rect属性。
self.image = pygame.image.load('images/ufo.bmp')
self.rect = self.image.get_rect()
# 每个外星人最初都在屏幕左上角附近。
self.rect.x = self.rect.width
self.rect.y = self.rect.height
# 储存外星人的精确水平坐标位置。
self.x = float(self.rect.x)
def check_edges(self):
"""如果外星人位于屏幕边缘,就返回True。"""
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right or self.rect.left <= 0:
return True
def update(self):
"""向左或右移动外星人。"""
self.x += (self.settings.alien_speed * self.settings.fleet_direction)
self.rect.x = self.x
bulle.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""管理发射子弹的类"""
def __init__(self, ai_game):
"""在飞船当前位置创建一个子弹对象"""
super().__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
self.color = self.settings.bullet_color
# 在(0, 0)位置创建一个表示在子弹的矩形,在设置正确的位置。
self.rect = pygame.Rect(0, 0, self.settings.bullet_width, self.settings.bullet_height)
self.rect.midtop = ai_game.ship.rect.midtop
# 储存用小数表示的子弹位置
self.y = float(self.rect.y)
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(self.screen, self.color, self.rect)
def update(self):
"""向上移动子弹"""
# 更新表示子弹位置的小数值。
self.y -= self.settings.bullet_speed
# 更新表示子弹的rect位置。
self.rect.y = self.y
button.py
import pygame.font
class Button:
def __init__(self, ai_game, msg):
"""初始化按钮的属性。"""
self.screen = ai_game.screen
self.screen_rect = self.screen.get_rect()
# 设置按钮的尺寸和其他属性。
self.width, self.height = 200, 50
self.button_color = (0, 255, 0)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)
# 创建按钮的rect对象,并使其居中。
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center
# 按钮的标签只需创建一次。
self._prep_msg(msg)
def _prep_msg(self, msg):
"""将msg渲染为图像,并使其在按钮上居中。"""
self.msg_image = self.font.render(msg, True, self.text_color, self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
# 绘制一个用颜色填充的按钮,再绘制文本。
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
game_stats.py
class GameStats:
"""跟踪游戏的统计信息。"""
def __init__(self, ai_game):
"""初始化统计信息。"""
self.settings = ai_game.settings
self.reset_stats()
# 游戏刚启动时处于非活跃状态。
self.game_active = False
# 在任何情况下都不重置最高得分:
self.high_score = 0
def reset_stats(self):
"""初始化在游戏运行期间可能变化的统计信息。"""
self.ships_left = self.settings.ship_limit
self.score = 0
self.level = 1
scoreboard.py
import pygame.font
from pygame.sprite import Group
from ship import Ship
class Scoreboard:
"""显示得分信息的类。"""
def __init__(self, ai_game):
"""初始化显示得分涉及的属性。"""
self.ai_game = ai_game
self.screen = ai_game.screen
self.screen_rect = self.screen.get_rect()
self.settings = ai_game.settings
self.stats = ai_game.stats
# 显示得分信息时使用的字体设置:
self.text_color = (30, 30, 30)
self.font = pygame.font.SysFont(None, 48)
# 准备初始化得分图像和最高分图像:
self.prep_score()
self.prep_high_score()
self.prep_level()
self.prep_ships()
def prep_score(self):
"""将得分转化为一幅渲染的图像"""
rounded_score = round(self.stats.score, -1)
score_str = "{:,}".format(rounded_score)
self.score_image = self.font.render(score_str, True, self.text_color, self.settings.bg_color)
# 在屏幕右上角显示得分:
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right - 20
self.score_rect.top = 20
def show_score(self):
"""屏幕上显示得分和的等级和剩余飞船数目。"""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
self.screen.blit(self.level_image, self.level_rect)
self.ships.draw(self.screen)
def prep_high_score(self):
"""将最高分转化为渲染图像"""
high_score = round(self.stats.high_score, -1)
high_score_str = "{:,}".format(high_score)
self.high_score_image = self.font.render(high_score_str, True, self.text_color, self.settings.bg_color)
# 将最高分放在屏幕顶部中央:
self.high_score_rect = self.high_score_image.get_rect()
self.high_score_rect.centerx = self.screen_rect.centerx
self.high_score_rect.top = self.score_rect.top
def check_high_score(self):
"""检查是否诞生新的最高分。"""
if self.stats.score > self.stats.high_score:
self.stats.high_score = self.stats.score
self.prep_high_score()
def prep_level(self):
level_str = str(self.stats.level)
self.level_image = self.font.render(level_str, True, self.text_color, self.settings.bg_color)
# 将等级分数放在得分下方:
self.level_rect = self.level_image.get_rect()
self.level_rect.right = self.score_rect.right
self.level_rect.top = self.score_rect.bottom + 10
def prep_ships(self):
"""显示还剩下多少飞船:"""
self.ships = Group()
for ship_number in range(self.stats.ships_left):
ship = Ship(self.ai_game)
ship.rect.x = 10 + ship_number * ship.rect.width
ship.rect.y = 10
self.ships.add(ship)
ounded_score = round(self.stats.score, -1)
score_str = “{:,}”.format(rounded_score)
self.score_image = self.font.render(score_str, True, self.text_color, self.settings.bg_color)
# 在屏幕右上角显示得分:
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right - 20
self.score_rect.top = 20
def show_score(self):
"""屏幕上显示得分和的等级和剩余飞船数目。"""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
self.screen.blit(self.level_image, self.level_rect)
self.ships.draw(self.screen)
def prep_high_score(self):
"""将最高分转化为渲染图像"""
high_score = round(self.stats.high_score, -1)
high_score_str = "{:,}".format(high_score)
self.high_score_image = self.font.render(high_score_str, True, self.text_color, self.settings.bg_color)
# 将最高分放在屏幕顶部中央:
self.high_score_rect = self.high_score_image.get_rect()
self.high_score_rect.centerx = self.screen_rect.centerx
self.high_score_rect.top = self.score_rect.top
def check_high_score(self):
"""检查是否诞生新的最高分。"""
if self.stats.score > self.stats.high_score:
self.stats.high_score = self.stats.score
self.prep_high_score()
def prep_level(self):
level_str = str(self.stats.level)
self.level_image = self.font.render(level_str, True, self.text_color, self.settings.bg_color)
# 将等级分数放在得分下方:
self.level_rect = self.level_image.get_rect()
self.level_rect.right = self.score_rect.right
self.level_rect.top = self.score_rect.bottom + 10
def prep_ships(self):
"""显示还剩下多少飞船:"""
self.ships = Group()
for ship_number in range(self.stats.ships_left):
ship = Ship(self.ai_game)
ship.rect.x = 10 + ship_number * ship.rect.width
ship.rect.y = 10
self.ships.add(ship)