跳转至

第三章:PyGame基础知识

上一章我们编写的井字棋小游戏,是基于终端窗口来输出游戏信息的,整体感觉比较简陋。真正的游戏需要创建一个窗口,有各种可交互的图形元素,有音效有互动。要达到这个目标,我们需要依赖一些已有的“轮子”或“框架”来编写更好玩的游戏。本章将学习PyGame的基础知识。PyGame是用于编写电脑视频游戏的Python模块。 它是基于历史悠久的C语言SDL库之上实现的功能模块。PyGame在2000年发布为一个社区项目,人们可以免费使用它来创建开源、免费软件、共享软件和商业游戏。在2020年,PyGame诞生二十周年之际,版本升级到了2.0版。

PyGame有诸多优点。简单易用且久经考验。使用PyGame只需要少量的代码,很多学校在使用PyGame进行计算机课程的教学。它已经被数百万用户很好地测试过了。它能兼容各类操作系统,开发的游戏软件能很好的在不同系统上运行。PyGame的安装很简单,保持联网条件下,我们打开一个终端,调用pip命令安装即可。安装完成后,可以运行stars示例来检查是否安装成功。

pip install pygame
python -m pygame.examples.stars

一、PyGame的Hello World

我们先试着基于PyGame写一个简短的程序。它的功能就是创建一个窗口,并在窗口标题上显示Hello World。

import pygame

#Initiailze pygame
pygame.init()

#Create a display surface and set its caption
WINDOW_WIDTH = 600
WINDOW_HEIGHT = 300
display_surface = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("Hello World")

#The main game loop
running = True
while running:
    #Loop through a list of Event objects that have occured
    for event in pygame.event.get():
        print(event)
        if event.type == pygame.QUIT:
            running = False

#End the game
pygame.quit()

首先使用import来加载pygame模块,然后使用pygame.init()函数来初始化,以做好所有的准备工作。这两行代码基本上都会写在程序的最前面。之后用pygame.display.set_model来定义将要创建窗口的大小,它会返回一个窗口对象,它就类似于画家的画布,后续所有的内容都会描绘在这张画布上。set_caption函数则定义了窗口标题。你可以尝试运行到目前为止介绍的几行代码。会发现它们做的事很少,就是打开一个窗口,然后随之关闭,因为代码运行到此就结束了。为了让窗口一直开启并和用户产生交互,我们需要一个循环,让代码一直运行,直到用户主动关闭窗口。这个循环我们称之为游戏主循环。

在主循环前我们定义一个逻辑变量running,用于表示运行状态,使用while语句来定义主循环,为了和用户进行交互,程序需要监听各种用户行为,这种行为在pygame中称之为事件(event)。pygame会使用pygame.event.get()函数来获取所有监听到的事件。就好像人类会使用各种感官来收集外界信息一样。pygame也会收集各种事件,例如用户敲了一下键盘,或者移动了一下鼠标。所以需要使用一个for循环来遍历当前监听到的事件。为了初学者熟悉这些事件,我们可以用print函数把这些事件打印出来观察,大家可以尝试点击或移动鼠标看看会打印出来什么信息。当然监听事件最重要的功能是和用户交互,在这个简单程序里,我们只关心一个事件,就是用户点击窗口右上角来关闭窗口的行为。这种行为称之为QUIT事件,所以后续使用一个条件判断,判断当监听到的事件中只要有一个事件类型是QUIT,则将running变量设置为False,此时我们的主循环会退出。然后代码执行到pygame.quit()以结束整个程序。

完整代码参见ch03_01.py文件。代码运行的效果如下图。

ch03-01

二、显示图形

前面这个只会创建窗口的程序太简单了,让我们加点功能,让它能显示一些图形吧。这只需要添加几行代码就可以了。下面是新增的代码片断。

#Define colors as RGB tuples
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)

#Give a background color to the display
display_surface.fill(WHITE)

#Circle(surface, color, center, radius, thickness...0 for fill)
pygame.draw.circle(display_surface, BLUE, (WINDOW_WIDTH//2, WINDOW_HEIGHT//2), 150)

#Rectangle(surface, color, (top-left x, top-left y, width, height))
pygame.draw.rect(display_surface, BLUE, (0, 0, 100, 100))

#Update the display
pygame.display.update()

#The main game loop
running = True

头尾两端部分的代码没有变化,只是中间增加了几行代码。先是定义了两个常量WHITE和BLUE,它们分别保存了两个元组,每个元组里有三个数字,这三个数字分别表示了红黄蓝三元色信息,最小值为0,最大值为255。三元色的不同组合可以表示所有的颜色,这两个常量就是定义了白色和蓝色的组合。

然后我们使用display_surface.fill()函数将窗口的背景色定义为白色,如果不定义的话,窗口默认背景色将是黑色。之后使用draw函数绘制了两个图形,分别是circle圆形和rect矩形,绘制的参数包括窗口对象,颜色,以及在什么位置绘制。绘制圆形的位置是定义圆心所在,这里使用了窗口的中心位置,也就是长度和宽度的一半,半径参数150。绘制矩形的位置需要四个数字,分别是矩形的最左边坐标,最顶边坐标,矩形的宽度和高度。绘制这两个图形并不会马上出现在屏幕上。还需要调用pygame.display.update()函数。因为所有绘制的命令都只是将绘制对象放置在内存缓冲区中,调用update才会将缓冲区的对象真正显示在屏幕上。这种缓冲机制是为了让显示过程更为流畅,就像是我们看话剧的时候,幕布关闭时,后台人员忙着摆放场景道具。一起放置妥当后,幕布拉起,将场景展示给了观众。这就是我们在第一章提到过的缓冲机制。

完整代码参见ch03_02.py文件。显示效果如下图。

ch03-02

三、键盘和鼠标交互

前面的示例还只是静态的绘图,让我们再加一点功能,让图形颜色能随着用户交互进行变换。用户按下鼠标则更改矩形颜色,用户输入不同的字母,则更改圆形的颜色。让我们看看下面的代码片段。

#Define colors as RGB tuples
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GRAY = (127, 127, 127)

#Give a background color to the display
display_surface.fill(WHITE)
circle_color = rect_color = BLUE

#The main game loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_b:
                circle_color = BLUE
            elif event.key == pygame.K_g:
                circle_color = GRAY

        if event.type == pygame.MOUSEBUTTONDOWN:
            if rect_color == BLUE:
                rect_color = GRAY
            else:
                rect_color = BLUE

    #Circle(surface, color, center, radius, thickness...0 for fill)
    pygame.draw.circle(display_surface, circle_color, (WINDOW_WIDTH//2, WINDOW_HEIGHT//2), 150)

    #Rectangle(surface, color, (top-left x, top-left y, width, height))
    pygame.draw.rect(display_surface, rect_color, (0, 0, 100, 100))

    #Update the display
    pygame.display.update()

这里我们增加一种颜色,即GRAY灰色。将circle_color和rect_color定义为初始的蓝色。在监听事件的for循环中,增加两个条件判断,分别是判断键盘是否输入了b或g,如果键盘输入b,也就是当条件event.key == pygame.K_b满足,则将circle_color赋值为蓝色,如果键盘输入g,也就是当条件event.key == pygame.K_g满足,则赋值为灰色。另一个条件判断是判断鼠标点击。当事件类型为pygame.MOUSEBUTTONDOWN的时候,也就发生了鼠标点击,则将rect_color进行开关操作。

因为这种交互操作会实时改变图形颜色,所以要将绘图函数也放在主循环之内,update函数也紧跟其后。这样每一次循环时,都会监听事件,判断事件是否满足条件,并重新绘制图形,更新屏幕显示。这样一个循环,我们称之为一帧。现在的计算机速度都非常快,运行一帧只需要几毫秒即可完成。完整代码参见ch03_03.py文件。

四、加载图片和文字资源

前面的例子里,我们绘制了不同的图形,并且学习如何处理用户交互操作。在这一小节中我们希望能使用自定义的图片,这些图片将成为游戏中有趣的角色。我们还要在窗口中增加文字。让我们看看下面的代码片断。

#We can then get the rect of the surface and use the rect to position the image.
dragon_image = pygame.image.load("dragon_right.png")
dragon_rect = dragon_image.get_rect()
dragon_rect.center = (WINDOW_WIDTH//2, WINDOW_HEIGHT//2)

font = pygame.font.Font('WenQuan.ttf', 32)
text = font.render("飞龙在天", True, GRAY, WHITE)
text_rect = text.get_rect()
text_rect.center = (WINDOW_WIDTH//2, text_rect.height//2)

#The main game loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    #Blit (copy) a surface object at the given coordinates to our display
    display_surface.blit(dragon_image, dragon_rect)
    pygame.draw.rect(display_surface, GRAY,dragon_rect, 4)
    display_surface.blit(text, text_rect)

    #Update the display
    pygame.display.update()

图片需要用到图片文件,文字需要用到字体文件,所以这两个对象的处理都需要用到外部资源。基本思路就是先导入外部资源,然后在主循环中绘制。我们先看看图片是如何处理的,首先使用pygame.image.load()函数加载图片资源,dragon_right.png文件需要放在代码文件的同一个目录下。图片加载后我们得到一个Surface对象,Surface对象代表了图形,也就是我们将要绘制在画布上的东西,将它保存为变量dragon_image。我们还需要设置它绘制在画布上的位置。它有一个特别的函数可以调用,就是get_rect()。通过这个函数可以获取到图片周边矩形框大小和位置。我们一般通过定义矩形框的位置来定义图片的位置。在这里将矩形框的中心点位置定义为屏幕中心。要注意由于缓冲机制,它并不会马上直接显示在屏幕上。

我们再来看看文字是如何处理的,首先使用pygame.font.Font()函数来加载字体资源,这里事先已经下载了文泉驿开源字体,和图片文件一样放到代码文件的同一目录下。字体加载后得到一个字体对象,再使用render()函数来把它渲染成一个Surface对象。render函数里面,设置要显示的文字内容,以及颜色等信息。和图片处理一样,我们通过文字周边的矩形框来设置其位置。

最后我们在主循环内部使用display_surface.blit()函数来将图片和文字显示出来。为了让大家对图片周边的矩形框更有了解,这里使用了draw.rect函数把这个框显示了出来。如果只是单独显示图片的话,这行代码并不是必要的。

完整的代码可以参考ch03_04.py文件。显示效果参见下图。

ch03-03

五、增加音效和运动

游戏中怎么能缺少动态图片和音效呢。我们的目标是让这条龙可以在窗口内自由漂移,如果碰到边界能改变方向反弹回去,并且能发出声音。看看代码应该怎么写吧。

text_rect.center = (WINDOW_WIDTH//2, text_rect.height//2)

sound_1 = pygame.mixer.Sound('sound_1.wav')
speed =  [2,2]
fpsClock = pygame.time.Clock()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    dragon_rect.x += speed[0]
    dragon_rect.y += speed[1]

    if dragon_rect.left < 0 or dragon_rect.right > WINDOW_WIDTH:
        speed[0] = -speed[0]
        sound_1.play()
    if dragon_rect.top < 0 or dragon_rect.bottom > WINDOW_HEIGHT:
        speed[1] = -speed[1]
        sound_1.play()

    display_surface.fill(WHITE)
    display_surface.blit(dragon_image, dragon_rect)
    display_surface.blit(text, text_rect)

    pygame.display.update()
    fpsClock.tick(60)

前面加载图片资源和字体资源的部分和之前一样,由于新增了声音,所以用pygame.mixer.Sound()函数来定义声音资源。之后定义一个列表变量,以设置移动的速度。也就是每个循环增加2个像素的距离。pygame.time.Clock用于创建一个时钟对象来监控程序运行的时间,其用处我们会在后面看到。

我们希望龙头能每个循环移动一下,或者说每帧移动一步,所以在主循环内部,对图片边框的坐标进行了修改,以控制图片移动。dragon_rect.x += speed[0]的作用就是每帧将其横坐标增加2个像素。之后如果龙头碰到了边界,则改变速度的方向,同时来播放声音。注意在运动场景下,需要把背景色定义也放到主循环。然后显示图片和文字。因为图片边框的坐标每次循环时都会变化,图片显示的位置也会变化,这样就看起来是一个运动的龙头。

最后fpsClock.tick(60)的作用是控制主循环的速度,大家可以尝试将这行代码注释掉看看效果。因为计算机速度非常快,每帧运行只需要几毫秒,那么一秒钟时间内会运行几百次循环,龙头移动将会非常快。为了达到一个合适的游戏速度,在函数参数中定义60,也就意味着一秒钟时间内只会运行60次循环,也就是60帧的速度。完整的代码可以参考ch03_05.py文件。

六、连续的键盘控制

前面的示例中,我们让龙头在窗口内飘移起来了,本节希望增加的功能是用键盘来控制龙头的移动。通常有两种键盘控制方式,一种是离散键盘控制,一种是连续键盘控制。离散键盘控制是指用户只按下一次键盘即放开的动作,类似于我们打字那样,连续键盘控制是指用户按住某个按键不松开的动作。这种控制方式对应的代码不一样。有些场景下需要连续键盘控制,例如控制角色在场景中连续移动,有些场景下需要离散键盘控制,例如控制角色跳跃。我们看一下代码上的区别。

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        # 离散键盘控制
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                dragon_rect.x -= speed[0]
            if event.key == pygame.K_RIGHT:
                dragon_rect.x += speed[0]
            if event.key == pygame.K_UP:
                dragon_rect.y -= speed[1]
            if event.key == pygame.K_DOWN:
                dragon_rect.y += speed[1]

    # 连续键盘控制
    keys = pygame.key.get_pressed()
    if keys[pygame.K_a]:
        dragon_rect.x -= speed[0]
    if keys[pygame.K_d]:
        dragon_rect.x += speed[0]
    if keys[pygame.K_w]:
        dragon_rect.y -= speed[1]
    if keys[pygame.K_s]:
        dragon_rect.y += speed[1]

    display_surface.fill(WHITE)

离散键盘控制角色移动时,代码写法是利用事件监听来判断,在本例中,如果四个方向键之一被按下,角色就会发生移动,如果持续按住方向键,角色并不会持续移动。需要持续移动时,需要使用pygame.key.get_pressed()函数,它会返回一个字典,字典中的key值是不同按键,value值是逻辑值,判断某个按键是否被按住。在本例中,我们是把键盘上adws这四个按键来对应到四个方向。大家可以运行代码来尝试了解两种键盘控制的区别。完整代码可以参考ch03_06.py文件。

七、碰撞检测

目前我们的窗口中只有一个角色,正常的游戏里会有多个角色相互作用。例如射击游戏中子弹会和角色产生交互,子弹消失,角色死亡。如何判断子弹击中了某个角色,这就需要利用碰撞检测了。基本的原理就是利用图片的边框信息,当两个角色的边框产生重叠时,就判断发生了碰撞。我们用一个代码例子来介绍一下。这个例子里面,除了龙头的角色以外,新增了一个硬币的角色,我们希望龙头去吃掉硬币,然后在随机的位置产生新的硬币。

coin_image = pygame.image.load("coin.png")
coin_rect = coin_image.get_rect()
coin_rect.x = random.randint(0, WINDOW_WIDTH - 32)
coin_rect.y = random.randint(0, WINDOW_HEIGHT - 32)

在加载龙头的图片之后,我们加载硬币的图片资源,和之前一样,获取对应的边框对象,并将边框的x和y坐标分别定义为随机值,这样硬币的位置将会出现在窗口的某个随机位置。

    if dragon_rect.colliderect(coin_rect):
        print("HIT")
        coin_rect.x = random.randint(0, WINDOW_WIDTH - 32)
        coin_rect.y = random.randint(0, WINDOW_HEIGHT - 32)

    display_surface.fill(WHITE)
    display_surface.blit(text, text_rect)
    display_surface.blit(dragon_image, dragon_rect)
    display_surface.blit(coin_image, coin_rect)
    pygame.draw.rect(display_surface, GRAY, dragon_rect, 1)
    pygame.draw.rect(display_surface, GRAY, coin_rect, 1)

在主循环内部,增加碰撞检测的部分,这里就是龙头的边框对象dragon_rect和硬币的边框对象coin_rect这二者之间检查,使用colliderect()函数来进行检查,如果两个边框发生了重叠,则函数返回逻辑真值。碰撞发生后,重新设置硬币边框的位置,就像是重新出现了一个新硬币。后续的代码就是绘制背景,绘制文字,绘制图片。代码中也绘制了边框,以便大家体会碰撞。大家可以使用adws四个按键来控制角色尝试去吃硬币。

完整的代码可以参见ch_03_07.py文件。显示效果如下图。

ch03-04

八、一个完整的游戏

通过前面的例子,我们学习了pygame的基础知识,我们把这些东西结合起来做一个完整的小游戏,这个小游戏叫做吃金币的龙。游戏功能如下:

  • 玩家需要控制一个龙头的角色,但是龙头只能在屏幕的左侧区域进行上下移动。
  • 一个金币从屏幕的右侧飞入,玩家需要接住这个金币。
  • 如果成功接住,则玩家增加1分,如果失败,则玩家失去1条生命。
  • 需要在屏幕上方显示当前的分数、生命值、游戏名称等信息。
  • 当生命值为0时,需要暂停游戏,让玩家确认是否继续。

在这个游戏设计里,我们采用面向对象的方法来编写。下图是这个游戏的类图设计,我们会设计三个类,包括龙头类、金币类和游戏主体类。因为类中元素很多,这个类图中只显示了部分属性和方法。

classDiagram

    Game *-- Dragon : Composition
    Game *-- Coin : Composition

    class Game {
    dragon
    coin
    ...
    }

    class Dragon {
    image
    rect
    update()
    draw()
    }

    class Coin {
    image
    rect
    move()
    draw()
    }

首先来编写龙头类Dragon。这个类很简单,初始化方法里加载图片资源,根据外部形参来设置角色边框的位置。定义速度变量。然后定义了update方法来处理输入和位置移动,定义了绘图方法。

class Dragon:
    def __init__(self,x,y):
        self.image = pygame.image.load("dragon_right.png")
        self.rect = self.image.get_rect()
        self.rect.left = x
        self.rect.centery = y
        self.speed = 10

    def update(self,direction):
        self.rect.y += direction * self.speed    

    def draw(self,screen):
        screen.blit(self.image,self.rect) 

然后定义一个金币类Coin。初始化方法和前面的类似,增加了reset方法用于重置角色边框的坐标。

class Coin:
    def __init__(self,x,y):
        self.image = pygame.image.load("coin.png")
        self.rect = self.image.get_rect()
        self.speed = 10
        self.reset(x,y)

    def reset(self,x,y):
        self.rect.x = x
        self.rect.y = y

    def move(self):
        self.rect.x -= self.speed 

    def draw(self,screen):
        screen.blit(self.image,self.rect) 

最重要的类是编写游戏的主体类,即Game类。初始化方法里,我们将主窗口进行了定义,同时设置了时钟,加载了声音文件和字体,然后将Dragon和Coin两个类进行实例化。

class Game:
    GREEN = (0, 255, 0)
    WHITE = (255, 255, 255)
    BLACK = (0, 0, 0)

    def __init__(self,width=1000, height=500):
        pygame.init()
        self.win_width = width 
        self.win_height = height 
        self.display_surface = pygame.display.set_mode((self.win_width, self.win_height))
        pygame.display.set_caption("Feed the Dragon")
        self.fps = 60
        self.clock = pygame.time.Clock()
        self.lives = 5
        self.buffer_distance = 100
        self.score = 0
        self.sound = pygame.mixer.Sound("sound_1.wav")
        self.font = pygame.font.Font('WenQuan.ttf', 32)
        self.dragon = Dragon(32,self.win_height//2)
        self.coin = Coin(x=self.win_width+self.buffer_distance, 
                         y=random.randint(64, self.win_height - 32))
        self.running = True 
        self.is_paused = False

然后我们来定义类中的方法,其中为了显示文字方便,我们先定义一个显示函数,它的输入是文字内容、颜色、坐标等信息,功能是直接在主窗口中显示相应的文字内容。

    def draw_text(self,text,color,x,y):
        image = self.font.render(text, True, color)
        rect = image.get_rect()
        rect.centerx = x
        rect.centery = y
        self.display_surface.blit(image,rect)

我们还需要处理玩家的键盘输入,需要一个对应的函数handle_input来完成。这里采用的是连续键盘控制。

    def handle_input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_UP] and self.dragon.rect.top > 64:
            self.dragon.update(-1)
        if keys[pygame.K_DOWN] and self.dragon.rect.bottom < self.win_height:
            self.dragon.update(1)

接着还要考虑金币的移动问题。如果金币掉出窗口之外,则生命值减1,将金币位置重新放在右侧,否则要将金币位置向左侧移动。

    def coin_reset(self):
        self.coin.reset(x=self.win_width+self.buffer_distance,
                        y=random.randint(64, self.win_height - 32))

    def handle_coin(self):
        if self.coin.rect.x < 0:
            self.lives -= 1
            self.coin_reset()
        else:
            self.coin.move()

接着是处理碰撞的问题,看看龙头有没有接着金币。如果成功接住,播放声音效果,加分且重置金币位置,而且对金币的速度变量进行递增,以增加难度。

    def handle_collision(self):
        if self.dragon.rect.colliderect(self.coin.rect):
            self.score += 1
            self.sound.play()
            self.coin.speed += 0.5
            self.coin_reset()

代码最多的函数是如何处理游戏结束的情况。如果生命值为0则进入游戏结束的状态,显示相应文字,进行暂停循环中等待玩家输入,是重启游戏,还是离开游戏。

    def check_gameover(self):
        if self.lives == 0:
            self.draw_text("GAME OVER",Game.GREEN,self.win_width//2,self.win_height//2)
            self.draw_text("Press any key to play again",Game.GREEN,self.win_width//2,self.win_height//2+50)
            pygame.display.update()
            self.is_paused = True
            while self.is_paused:
                for event in pygame.event.get():
                    if event.type == pygame.KEYDOWN:
                        self.score = 0
                        self.lives = 5
                        self.coin_reset()
                        self.coin.speed = 10
                        self.is_paused = False

                    if event.type == pygame.QUIT:
                        self.is_paused = False
                        self.running = False

此外别忘记了,还需要编写绘图函数。需要刷新背景,绘制上方的文字,绘制龙头以及金币。

    def draw(self):
        self.display_surface.fill(Game.BLACK)

        self.draw_text("Score: " + str(self.score),Game.GREEN,100,20)
        self.draw_text("吃金币的龙" ,Game.GREEN,self.win_width//2,20)
        self.draw_text("Lives: " + str(self.lives),Game.GREEN,self.win_width-100,20)

        pygame.draw.line(self.display_surface, Game.WHITE, (0, 64), (self.win_width, 64), 2)
        self.dragon.draw(self.display_surface)
        self.coin.draw(self.display_surface)
        pygame.display.update()

最后,我们用一个play()函数,把整个游戏的流程逻辑组装进游戏主循环中。

    def play(self):
        while self.running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.running = False

            self.handle_input()
            self.handle_coin()
            self.handle_collision()
            self.draw()
            self.check_gameover()
            self.clock.tick(self.fps)

        pygame.quit()

在代码文件最后,我们对类进行实例化,并使用play()函数开始运行游戏。

if __name__ == '__main__':
    game = Game()
    game.play()

完整代码可以参见ch03_08.py文件。游戏效果显示如下。

ch03-05

整体游戏的逻辑可以用下面的流程来表示。基本上所有游戏编程都会遵循这个流程逻辑。

graph LR
    游戏初始化-->游戏资源加载
    游戏资源加载-->检测输入
    subgraph 游戏主循环
    检测输入--继续游戏-->更新位置
    更新位置-->检测碰撞
    检测碰撞-->绘图
    绘图-->检测输入
    end   
    检测输入--中止游戏-->退出

本章小结

本章介绍了PyGame模块的基本功能,介绍如何创建一个窗口,如何在窗口中显示图片和文字,如何与键盘鼠标进行交互。游戏需要丰富的用户体验,我们又学习了如何让图片运动起来,如何增加声音效果。介绍了两种不同的键盘控制方法,以及必不可少的碰撞检测功能实现。最后我们基于已经学到的知识,实现了一个完整的小游戏。本章的知识点比较密集,也是必需要掌握的基本功,大家需要耐心理解。在下一章,我们将会学习用PyGame来完成贪吃蛇游戏的制作。这个游戏将更复杂更有趣。