pygame

大家好,我是一名西安电子科技大学的大一学生,刚刚接触到编程,现在在也处于学习阶段,因此我会在学习的同时,去用 markdown 去写一些笔记,然后会分享给大家,但是由于时间有限,可能很久才会更新一次,一般来说是不会带图片的。

而且都是全程干货!
希望能帮助到大家!
大家有意见可以评论或是留言,我有时间一定会回复!

关于 pygame:

pygame 是一组功能强大的有趣的模块。

可用于图形管理、动画、乃至声音,可轻松的开发出复杂的游戏。

导入 pygame 模块:

import sys
import pygame
from pygame.locals import *

通常会配合sys一起导入,sys中含有使程序退出的函数,sys.exit()。

pygame.locals中包含一系列可能用到的常量,QUIT,K_ESCAPE。

可以直接去调用他们。

在 pygame 中,是 font 和 surface 模块,在这些模块中,是 Font 和 Surface 数据类型。

pygame 程序员用小写字母打头表示一个模块名,用大写字母打头表示一个数据类型,使得我们能够加以区分。

Rect 对象表示一个矩形空间区域的位置和大小。

位置可以通过 Rect 对象的属性来确定。这些属性是表示 X 坐标和 Y 坐标的一个整数值的元组。

矩形的大小可通过 width 属性和 height 属性来确定。这些属性决定矩形的宽和高是多少个像素。

Rect 对象有一个 colliderect() 方法,用来检查矩形是否和其他 Rect 对象有碰撞。

Surface 对象是一个带颜色的像素区域。

Surface 对象表示一个矩形图像,而 Rect 对象表示一个矩形的空间和位置。

Surface 对象有一个 blit() 方法,它可以将一个 Surface 对象绘制到另一个 Surface 对象之上。

由 pygame.display.set_mode() 函数返回的 Surface 对象是特殊的,在该 Surface 对象上绘制的任何物体都会显示到用户的屏幕上。

当用户提供鼠标、键盘和其他类型的输入时,pygame.event 模块会创建一个 Event 对象。

pygame.event.get() 函数返回这些 Event 对象的一个列表。

可通过查看 Event 对象的 type 属性来查看事件的类型:QUIT, KEYDOWN, KEYUP。

pygame.font 模块拥有一个 Font 数据类型,用于表示 pygame 中的文本字体。

传递给 pygame.font.SysFont() 函数的参数时表示字体名称的一个字符串以及表示字体大小的一个整数。

传递 None 作为字体名称以获取系统默认字体。

pygame.time 模块中的 Clock 对象有助于避免程序运行得过快。

Clock 对象有一个 tick() 方法,它接收的参数表示想要游戏运行的速度时多少帧 (FPS)。

FPS 越高,游戏运行速度越快。

初始化 pygame:


pygame.init()

在调用其他pygame函数之前都要先调用pygame.init()函数初始化。

在文件的末尾,可以通过创建游戏的实例并调用 run_game()。

if __name__ == '__main__':
    ai = my_game
    ai = run_game()
pygame.mouse.set_visible(boolen)

可向其传入一个布尔值,表示鼠标是否可见。

设置 pygame 窗口:


windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('hello world!')

调用pygame中的display模块中的set_mode()方法去创建图形化用户界面(graphical user interface, GUI)。pygame模块中也有自己的模块。

去生成一个窗口,而surface 变量 windowSurface 就表示那个窗口。

向set_mode()传递一个元组,表示窗口的宽度和高度。

pygame.display.set_mode() 是个特殊函数,生成的 Surface 是个特殊的对象,能代表整个屏幕。

pygame.display.set_mode() 函数的第二个参数是可选的。

我们可以传递 pygame.FULLSCREEN 常量,使得窗口占据整个屏幕,而不是显示一个小窗口。

像素是计算机屏幕上的最小的点。

生成的窗口是以像素为单位的。

屏幕上的单个像素可以以任何颜色显示。

屏幕上的所有像素一起工作显示出你所看到的照片。

set_mode()函数返回一个 pygame.Surface 对象。

对象(object)是对拥有方法的数据类型的值的另一种叫法。

Surface 对象就表示这个窗口。

对象 == 数据 + 方法。

向其传入一个字符串表示窗口的标题。

设置颜色变量:


BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

光主要有三种颜色:红、绿、蓝。通过这三种颜色可以组合出任意颜色。

python 中,颜色数据就是3个整数的元组,叫RGB颜色。

第一个值代表有多少红色,0表示没用红色,255代表红色达到了最大值。

第二个值表示绿色,第三值表示蓝色。

由于我们不希望每次都要在程序中用一个具体的颜色时都要重新编写3个整数的元组。

所有我们可以创建一系列颜色常量。

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 窗口上:


basicFont = pygame.font.SysFont(None, 48)

text = basicFont.render('hello world!', True, White, Blue)

textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery

字体是有统一风格绘制的一整套字母,文字,数字,符号和字符。

python 程序不能修改字体,但是 pygame 可以以任何字体来绘制文本。

使用两个参数来调用 pygame.font.SysFont() 函数创建一个 pygame.font.Font对象。

  • 第一个参数是字体的名称,传递 None 表示使用系统默认字体。
  • 第二个参数是字体的大小(以点为单位)。

将字符串 ‘hello world!’ 绘制到窗口上形成一幅图像,叫做渲染(rendering)。

储存在变量 basicFont 中的 Font 对象有一个叫做 render() 的方法。

这个方法将返回一个 Surface 对象,文本就绘制其上。

方法 render() 有多个参数:

  • 第一个参数表示要绘制的文本字符串。
  • 第二个参数指定是否想要抗锯齿的一个 Boolean 值,为文本抗锯齿,会使文本看起来更平滑些。
  • 第三个参数和第四个参数都是一个 RGB 元组。
  • 第三个参数是文本的颜色,第四个参数是文本背后的背景颜色。

我们将这个 Font 对象赋值给 text 。

一旦设置了 Font 对象,需要将其放置到窗口的一个位置上。

pygame.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)

pygame.Rect() 函数和 pygame.Rect 数据类型的名称相同。

与其数据类型的名称形同并且创建其数据类型的对象或值的函数,叫做构造函数。

用一种颜色填充整个 Surface 对象:

pygame 的初始化背景是一个黑框框。


windowSurface.fill(White)

fill() 函数会使用一种颜色来填充整个窗口屏幕。

当调用 fill() 或是其他函数时,屏幕上的窗口不会改变。

但是他们会改变 Surface 对象。

需要调用 pygame.display.update() 函数来更新画面。

因为计算机内存中修改 Surface 对象比在屏幕上修改图像要快很多。

pygame 的绘图函数:

在 pygame 中,每一种图形都有自己的函数,可以将这些形状组合到不同的照片中,以便于图形化游戏。

pygame.draw.polygon() 函数可以绘制指定的任意多边形。

多边形是由多条直线边组成的形状,圆和椭圆不是多边形。


pygame.draw.polyon(windowSurface, Green, ((146, 0), (291, 106), (56, 277)
                                         (236, 277), (0, 106)))

该函数的参数是:

  • 要在其上绘制多边形的 Surface 对象。
  • 多边形的颜色。
  • 由要依次绘制的点的XY坐标的元组的元组所构成的一个元组。最后一个元组将自动连接到第一个元组的点。
  • 可选项:表示多边形的线条宽度的整数值。如果没有这个选项,多边形将会填充。

从屏幕上一点到另一点绘制一条直线:


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(windowSurface, Blue, (300, 50), 20, 0)

pygame.draw.circle() 函数的参数依次是:

  • 要在其上绘制圆的 Surface 对象。
  • 圆的颜色。
  • 表示圆心的 XY 坐标的两个整数的元组。
  • 表示圆的半径(大小)的整数值。
  • 可选项:线条宽度的整数值。宽度为0,表示填充圆。

通过传入椭圆的外接矩形绘制:


pygame.draw.ellipse(windowSurface, Red, (300, 250, 40, 80), 1)

pygame.draw.ellipse() 函数的参数依次是:

  • 要在其上绘制椭圆的 Surface 对象。
  • 椭圆的颜色。
  • 传递分别表示椭圆左上角的 X 和 Y 坐标以及椭圆的宽和高的4个整数的一个元组。
  • 可选项:表示线条宽度的整数值。宽度为0表示填充椭圆。

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 对象,而不是 四个整数。

为单独像素设定颜色。

创建一个 pygame.PixelArray 对象,是颜色元组的列表的一个列表,这些颜色元组传递给它的 Surface 对象。

PinxArray 对象能让我们进行跟高的像素级别的控制,因此,如果需要在屏幕上绘制非常详细或是定制化图像,而不是较大的图形是,可以选择 PixelArray 对象。


pixArray = pygame.PixelArray(windowSurface)
pixArray[480][380] = Black

使用 PixArray 对象让屏幕上的一个像素点变成黑色。

需要把 Surface 对象作为参数传递给 pygame.PixelArray() 函数调用。

PixelArray 对象的第一个索引是 X 的坐标,第二个索引是 Y 的坐标。

从一个 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() 方法:


windowSurface.blit(text, textRect)

blit() 方法的两个参数:

  • 第一个参数是要加入在其上的 Surface 对象。
  • 第二个参数指定了应该将 text surface 绘制到 windowSurface 上的何处。

text 的Rect 对象通过调用 text.get_rect() 获取。

将 Surface 对象绘制到屏幕上:

在调用 pygame.display.update() 函数之前,并不会真的将任何内容绘制到屏幕上。


pygame.display.update()

为了节省内存,我们并不想在每次调用了绘图函数之和都更新到屏幕上。

只有在所有绘制函数都调用完后,才想要一次性更新屏幕。

事件和游戏循环:

pygame 程序会不断地运行叫做游戏循环 ( game loop ) 的一个循环。在这个程序中,游戏循环的所有代码行每秒钟都会执行一百次左右。

游戏循环是不断的查看新事件、更新窗口的状态并在屏幕上绘制窗口的一个循环。

任何时候,当用户按下一个按键、点击或移动鼠标或使得其他一些事件发生的时候,pygame 都会创建该对象。

事件 ( event ) 是 pygame.event.Event 数据类型的对象。

调用 pygame.event.get() 函数检索自从上次调用 pygame.event.get() 后所生成的任何新的 pygame.event.Event 对象。这些事件会以 Event 对象的一个列表的形式返回。

所有的 Event 对象都有一个叫做 type 的属性,它会告诉我们事件是什么类型。


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 *

pygame.init()

windowSurface = pygame.display.set_mode((500, 400), 0, 32)
pygame.display.set_caption('Hello world!')

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

basicFont = pygame.font.SysFont(None, 48)

text = basicFont.render('Hello world!', True, WHITE, BLUE)
textRect = text.get_rect()
textRect.centerx = windowSurface.get_rect().centerx
textRect.centery = windowSurface.get_rect().centery

windowSurface.fill(WHITE)

pygame.draw.polygon(windowSurface, GREEN, ((146, 0), (291, 106), (236, 277), (56, 277), (0, 106)))

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.circle(windowSurface, BLUE, (300, 50), 20, 0)

pygame.draw.ellipse(windowSurface, RED, (300, 250, 40, 80), 1)

pygame.draw.rect(windowSurface, RED, (textRect.left - 20, textRect.top - 20, textRect.width + 40, textRect.height + 40))

pixArray = pygame.PixelArray(windowSurface)
pixArray[480][380] = BLACK
del pixArray

windowSurface.blit(text, textRect)

pygame.display.update()

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

创建动画图像:

为了让物体有动画效果,我们将在游戏循环的每一次迭代中,让这些物体移动一些像素,这会使得物体看上去像是在屏幕上移动一样。

  • 通过游戏循环,实现物体动画。
  • 改变一个物体的运动方向。

WINDOWWIDTH = 400
WINDOWHEIGHT = 400
pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption('Animation')

为窗口的高度和宽度设置常量变量。


DOWNLEFT = 'downleft'
DOWNRIGTH = 'downright'
UPLEFT = 'upleft'
UPRIGHT = 'upright'

MOVESPEED = 4

我们也可以不使用常量,而使用任何想要的值来表示这些方向。

设置移动速度:告诉程序,每一次迭代中每个物体应该移动多少像素。

要定义积木:创建一个字典来表示每个移动的积木的数据结构。

这个字典有 ‘rect’ 键、’color’ 键和 ‘dir’ 键,分别控制 物体的 位置形状、颜色和移动方向。


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 对象的一条轨迹,而不是一个平滑的动画。

为了避免这种情况,在游戏循环的每一次迭代中,我们都需要清除窗口。


windowSurface.fill(White)

在屏幕表面绘制一层白色去覆盖原本的绘制图案。

为了更新每一个物体,我们需要在游戏循环中加入遍历每一个列表:

  • 我们需要根据每个物体已经移动的方向来修改每个积木,以便使用 if 语句来检查物体数据结构的 dir 键,从而判断积木的移动方向。
  • 然后,我们将根据积木在该方向上的移动来修改积木的位置。
    for b in boxes:

        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 中储存的整数(每次迭代中物体将要移动的像素数目)来修改这些属性的值。

判断一个物体是否越过了窗口的边缘,如果越过了,就要把物体”弹回”。


        if b['rect'].top < 0:

            if b['dir'] == UPLEFT:
                b['dir'] = DOWNLEFT
            if b['dir'] == UPRIGHT:
                b['dir'] = DOWNRIGHT
        if b['rect'].bottom > WINDOWHEIGHT:

            if b['dir'] == DOWNLEFT:
                b['dir'] = UPLEFT
            if b['dir'] == DOWNRIGHT:
                b['dir'] = UPRIGHT
        if b['rect'].left < 0:

            if b['dir'] == DOWNLEFT:
                b['dir'] = DOWNRIGHT
            if b['dir'] == UPLEFT:
                b['dir'] = UPRIGHT
        if b['rect'].right > WINDOWWIDTH:

            if b['dir'] == DOWNRIGHT:
                b['dir'] = DOWNLEFT
            if b['dir'] == UPRIGHT:
                b['dir'] = UPLEFT

通过物体的 Rect 对象的 left, right, top, bottom 属性来判断物体是否到达了边缘。

每次积木移动的时候,我们都需要调用 pygame.draw.rect() 函数,在窗口中新的位置绘制他们。


pygame.draw.rect(windowSurface, b['color'], b['rect'])

函数参数依次是:

Surface 对象:windowSurface。

颜色。

决定位置和大小的 Rect 对象。

调用 pygame.display.update() 在屏幕上绘制 windowSurface。


pygame.display.update()
time.sleep(0.02)

如果程序全速运行的话,计算机能够很快的移动、弹跳和绘制物体,物体看上去就会是一团模糊。

为了让程序运行的足够慢,以便我们能够看清物体。

当添加函数 time.sleep() 时,会在物体每一次移动之间,暂停0.02秒。

import pygame, sys, time
from pygame.locals import *

pygame.init()

WINDOWWIDTH = 1000
WINDOWHEIGHT = 800
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption('Animation')

DOWNLEFT = 'downleft'
DOWNRIGHT = 'downright'
UPLEFT = 'upleft'
UPRIGHT = 'upright'

MOVESPEED = 4

WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

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]

while True:

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

    windowSurface.fill(WHITE)

    for b in boxes:

        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

        if b['rect'].top < 0:

            if b['dir'] == UPLEFT:
                b['dir'] = DOWNLEFT
            if b['dir'] == UPRIGHT:
                b['dir'] = DOWNRIGHT
        if b['rect'].bottom > WINDOWHEIGHT:

            if b['dir'] == DOWNLEFT:
                b['dir'] = UPLEFT
            if b['dir'] == DOWNRIGHT:
                b['dir'] = UPRIGHT
        if b['rect'].left < 0:

            if b['dir'] == DOWNLEFT:
                b['dir'] = DOWNRIGHT
            if b['dir'] == UPLEFT:
                b['dir'] = UPRIGHT
        if b['rect'].right > WINDOWWIDTH:

            if b['dir'] == DOWNRIGHT:
                b['dir'] = DOWNLEFT
            if b['dir'] == UPRIGHT:
                b['dir'] = UPLEFT

        pygame.draw.rect(windowSurface, b['color'], b['rect'])

    pygame.display.update()
    time.sleep(0.02)

碰撞检测:

碰撞检测(collision detection)负责计算机屏幕上的两个物体何时发生彼此接触(也就是发生碰撞)。

在 pygame 中,我们将添加的图像都按照矩形来处理,因此更容易去检测碰撞。

在游戏中,碰撞检测将判断两个矩形是否发生彼此重叠。

通过调用 time.sleep() 函数可以减缓程序的执行速度,使得程序不会运行的太快。

而 time.sleep() 函数的问题是:这会使所有计算机上都延迟一会,而程序剩下的部分的速度则取决于计算机有多快。

如果想在任何的计算机上都以相同的速度运行,我们需要一个函数能够对于较快的计算机暂停的时间长一点,而较慢的计算机暂停的时间短一点。

pygame.time.Clock 对象可以对于任何计算机都暂停适当的时间。


mainClock = pygame.time.Clock()
mainClock.tick(40)

用 pygame.time.Clock() 函数创建一个 Clock 对象。

通过调用 tick() 方法,使无论计算机有多块,都是每秒迭代40次,确保游戏速度不会超过预期。

在游戏循环中只能调用一次 tick() 。

帧 ( frame ) 是游戏循环单次迭代中所绘制的一个屏幕,也可以为帧去设置其常量。

所有的 Rect 对象都拥有碰撞的检测方法 colliderect():


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 事件和鼠标按钮点击来控制游戏。

事件对象有一个 key 属性来识别按下的是哪个键。

我们用 if 语句来检查按键。


    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:

            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_PAGEUPPage Up 键K_RIGHT右方向键K_PAGEDOWNPage Down 键K_UP上方向键K_aA 键K_DOWN下方向键K_wW 键K_ESCAPEESC 键K_sS 键K_BACKSPAVEBackspace 键K_dD 键K_TABTAB 键K_F1F1 键K_RETURN回车键K_F2F2 键K_SPACE空格键K_F3F3 键K_DELETEDelete 键K_F4F4 键K_LSHIFT左 Shift 键K_F5F5 键K_RSHIFT右 Shift 键K_F6F6 键K_LCTRL左 Ctrl 键K_F7F7 键K_RCTRL右 Ctrl 键K_F8F8 键K_LALT左 Atl 键K_F9F9 键K_RALT右 Atl 键K_F10F10 键K_HOMEHOME 键K_F11F11 键K_ENDEND 键K_F12F12 键

如果用户按下 Esc 键,游戏就会终止。

在调用 sys.exit() 函数之前,必须先调用 pygame.quit() 函数。

if event.key == K_ESCAPE:
    pygame.quit()
    sys.exit()
import pygame, sys, random
from pygame.locals import *

pygame.init()
mainClock = pygame.time.Clock()

WINDOWWIDTH = 400
WINDOWHEIGHT = 400
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption('Input')

BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
WHITE = (255, 255, 255)

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))

moveLeft = False
moveRight = False
moveUp = False
moveDown = False

MOVESPEED = 6

while True:

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:

            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:

        foodCounter = 0
        foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - FOODSIZE), random.randint(0, WINDOWHEIGHT - FOODSIZE), FOODSIZE, FOODSIZE))

    windowSurface.fill(WHITE)

    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

    pygame.draw.rect(windowSurface, BLACK, player)

    for food in foods[:]:
        if player.colliderect(food):
            foods.remove(food)

    for i in range(len(foods)):
        pygame.draw.rect(windowSurface, GREEN, foods[i])

    pygame.display.update()
    mainClock.tick(40 )

在屏幕上移动玩家:

根据用户的按键,设置了移动变量 (moveDown、moveUp、moveLeft、moveRight)。

通过调整玩家的 X 和 Y 坐标 (储存在 pygame.Rect 对象中),来移动玩家。


    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)

    pygame.draw.rect(windowSurface, BLACK, player)

通过鼠标移动这一事件,改变玩家的 Rect 属性,让玩家随鼠标移动。

if event.type == MOUSEMOTTION:

    player.centerx = event.pos[0]
    player.centery = event.pos[1]

声音和图像:

主要内容:

  • 声音文件和图像文件。
  • 绘制精灵。
  • 添加音乐和声音。
  • 打开和关闭声音。

精灵 (sprite) 是指用作屏幕上图像的一部分的一个单独的二位图像。

把精灵图像绘制到背景之上。

我们可以水平方向反转精灵图像,以使得精灵面朝别的方向。

可以在相同的窗口中多次绘制相同的精灵图像。

也可以重新调整精灵的大小,使其比初始的精灵图像更大或更小。

可以把背景图看作是很大的精灵。

精灵是存储在计算机上的图像文件。 图片文件格式即图像文件存放的格式,通常有JPEG、TIFF、RAW、BMP、GIF、PNG等 。

pygame 可以使用几种不同的图像格式。pygame 支持的图像格式包含 BMP, PNG, JPG, GIF。

而 BMP 是最简单最适用的类型。

类:区别:JPGJPEG图像格式是最常见也最常用的。它能够将图像压缩在很小的储存空间,但是会丢失一些图像数据,尤其是在压缩比例越高的情况下图像质量更低。但是JPEG的压缩技术也同时是它的应用优势。它用有损压缩方式去除冗余的图像数据,在获得极高的压缩率的同时能展现十分丰富生动的图像,换句话说,就是可以用最少的磁盘空间得到较好的图像品质。同时 JPEG是一种很灵活的格式,具有调节图像质量的功能,允许用不同的压缩比例对文件进行压缩,支持多种压缩级别,应用环境会更多,也更加方便。BMPBMP图片格式使用范围非常广。我们经常在收藏好文章里的图片时,会发现保存下来的图片格式为BMP,占用空间较大。因为它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息,可以和其他 Microsoft Windows 程序兼容,最适合做墙纸。PNGPNG图像文件存储格式,其设计目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。PNG的名称来源于”可移植网络图形格式(Portable Network Graphic Format,PNG)”,也有一个非官方解释”PNG’s Not GIF”,是一种位图文件(bitmap file)存储格式,读作”ping”。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的α通道数据。PNG使用从LZ77派生的无损数据压缩算法,一般应用于JAVA程序、网页或S60程序中,原因是它压缩比高,生成文件体积小。GIFGIF(Graphics Interchange Format)的原义是”图像互换格式”,是CompuServe公司在 1987年开发的图像文件格式。GIF文件的数据,是一种基于LZW算法的连续色调的无损压缩格式。其压缩率一般在50%左右,它不属于任何应用程序。GIF格式可以存多幅彩色图像,如果把存于一个文件中的多幅图像数据逐幅读出并显示到屏幕上,就可构成一种最简单的动画。GIF格式自1987年由CompuServe公司引入后,因其体积小、成像相对清晰,特别适合于初期慢速的互联网,而大受欢迎。PSD和AIPSD格式是最适合调整的。它可以支持图层、通道、蒙板和不同色彩模式的各种图像特征,是一种非压缩的原始文件保存格式。扫描仪不能直接生成该种格式的文件。PSD文件可以保留所有的原始操作信息,所以占用空间会很大,但是对于后期修改、继续操作更有保存意义。如果你在制作需要反复推敲修改的图像时,选保存为PSD格式是非常有必要的。在这里顺便提一下AI格式。它也是一种分层文件,是一种矢量图形文件。每个对象都是独立的,他们具有各自的属性,如大小、形状、轮廓、颜色、位置等。以这种格式保存的文件便于修改,这种格式文件可以在任何尺寸大小下按最高分辨率输出。TIFTIF图像格式大多数被用作LOGO设计。它支持多种编码方法,其中包括RGB无压缩、RLE压缩、JPEG压缩等。TIFF可以说是比较复杂的一种,具有扩展性、方便性、可改性,可以提供给IBMPC等环境中运行、图像编辑程序。EPSEPS格式主要用于矢量图像和光栅图像的存储。EPS格式采用 PostScript语言进行描述,并且可以保存其他一些类型信息,例如多色调曲线、Alpha通道、分色、剪辑路径、挂网信息和色调曲线等,因此EPS 格式常用于印刷或打印输出。RAWRAW格式图片细节更严密,更方便调整。RAW文件大多是通过相机直接输出的,照片的锐度、白平衡、色阶和颜色的调节都可以进行调整。所以,为了更加方便查阅和后期处理照片,摄影师通常采用同时记录JPEG和RAW格式照片的办法,既可以让用户使用常规的图像处理软件组织和编辑照片(JPEG);当需要获得处理精细的照片或需要改善照片缺憾(如白平衡不正确和高光/暗部细节缺失 )的时候, 用户可以使用RAW解决问题。不过RAW格式照片目前还存在软件兼容性问题,你可以下载专门的RAW处理软件来进行。

pygame 支持的声音文件格式有 MID, WAV, MP3。

我们将使用3个不同的变量来表示玩家,而不是像以前只有一个变量。


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 秒处开始播放。

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 中的值。

import pygame, sys, time, random
from pygame.locals import *

pygame.init()
mainClock = pygame.time.Clock()

WINDOWWIDTH = 400
WINDOWHEIGHT = 400
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32)
pygame.display.set_caption('Sprites and Sounds')

WHITE = (255, 255, 255)

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

moveLeft = False
moveRight = False
moveUp = False
moveDown = False

MOVESPEED = 6

pickUpSound = pygame.mixer.Sound('pickup.wav')
pygame.mixer.music.load('background.mid')
pygame.mixer.music.play(-1, 0.0)
musicPlaying = True

while True:

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN:

            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:

        foodCounter = 0
        foods.append(pygame.Rect(random.randint(0, WINDOWWIDTH - 20), random.randint(0, WINDOWHEIGHT - 20), 20, 20))

    windowSurface.fill(WHITE)

    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

    windowSurface.blit(playerStretchedImage, player)

    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()

    for food in foods:
        windowSurface.blit(foodImage, food)

    pygame.display.update()
    mainClock.tick(40 )

多文件项目 AlienInvasion:

多文件项目:

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()

        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()
            self.sb.prep_level()
            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

        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中。"""

        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()

        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:

            self.stats.ships_left -= 1
            self.sb.prep_ships()

            self.aliens.empty()
            self.bullets.empty()

            self._create_fleet()
            self.ship.center_ship()

            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()
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)

        self.bullet_allowed = 4

        self.alien_speed = 0.3
        self.fleet_drop_speed = 5

        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

        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)
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

        self.x = float(self.rect.x)

        self.moving_right = False
        self.moving_left = False

    def update(self):
        """根据移动标志调整飞船位置。"""

        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.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)
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

        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
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

        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

        self.rect.y = self.y
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)

        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)
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
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)

    # &#x5728;&#x5C4F;&#x5E55;&#x53F3;&#x4E0A;&#x89D2;&#x663E;&#x793A;&#x5F97;&#x5206;&#xFF1A;
    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):
    """&#x5C4F;&#x5E55;&#x4E0A;&#x663E;&#x793A;&#x5F97;&#x5206;&#x548C;&#x7684;&#x7B49;&#x7EA7;&#x548C;&#x5269;&#x4F59;&#x98DE;&#x8239;&#x6570;&#x76EE;&#x3002;"""
    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):
    """&#x5C06;&#x6700;&#x9AD8;&#x5206;&#x8F6C;&#x5316;&#x4E3A;&#x6E32;&#x67D3;&#x56FE;&#x50CF;"""
    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)

    # &#x5C06;&#x6700;&#x9AD8;&#x5206;&#x653E;&#x5728;&#x5C4F;&#x5E55;&#x9876;&#x90E8;&#x4E2D;&#x592E;&#xFF1A;
    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):
    """&#x68C0;&#x67E5;&#x662F;&#x5426;&#x8BDE;&#x751F;&#x65B0;&#x7684;&#x6700;&#x9AD8;&#x5206;&#x3002;"""
    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)

    # &#x5C06;&#x7B49;&#x7EA7;&#x5206;&#x6570;&#x653E;&#x5728;&#x5F97;&#x5206;&#x4E0B;&#x65B9;&#xFF1A;
    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):
    """&#x663E;&#x793A;&#x8FD8;&#x5269;&#x4E0B;&#x591A;&#x5C11;&#x98DE;&#x8239;&#xFF1A;"""
    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)

Original: https://blog.csdn.net/wwx1239021388/article/details/123491261
Author: WhenXuan
Title: pygame

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/779578/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球