Pygame 官方文档 - Tutorials - 逐行的黑猩猩教程(Line By Line Chimp)

这篇教程详细解读了Pygame的逐行黑猩猩教程,介绍了如何加载图像和声音资源,以及游戏对象类的设计。文章涵盖导入模块、资源加载、游戏对象和主循环等关键部分,适合初学者了解和学习Pygame编程。
摘要由CSDN通过智能技术生成
逐行的黑猩猩教程(Line By Line Chimp)

作者: Pete Shinners
联系方式: pete@shinners.org

介绍(Introduction)
       在pygame示例中,有一个名为“黑猩猩”的简单示例。这个例子模拟了一只可被打的猴子在一个小屏幕上走动,并且承诺有财富和回报。示例本身非常简单,并且在错误检查代码上有点单薄。这个示例程序演示了许多pygame的功能,如创建图形窗口,加载图像和声音文件,渲染TTF文本以及基本事件和鼠标处理。
       这个程序和图像可以在pygame的标准源代码发布中找到。对于pygame的1.3版本,此示例已完全重写,以添加更多功能并更正错误检查。这大约是原始示例大小的两倍,但现在给我们提供了更多可查看的内容,以及我可以建议您自己的项目可以重用的代码。
本教程将逐块浏览代码。解释代码的工作原理。还将提到如何改进代码以及错误检查可以提供哪些帮助。
       这是一个很好的教程,特别对于第一次看到pygame代码的人。一旦完全安装了pygame,您就可以在examples目录中找到并运行自己的黑猩猩演示。

(不,这不是横幅广告,而是截图)

导入模块(Import Modules)
这是将所有需要的模块导入程序的代码。 它还检查一些可选的pygame模块的可用性。

import os, sys
import pygame
from pygame.locals import *
if not pygame.font: print 'Warning, fonts disabled'
if not pygame.mixer: print 'Warning, sound disabled'

       首先,我们导入标准的“os”和“sys”python模块。这些允许我们做一些事情,比如创建独立于平台的文件路径
       在下一行中,我们导入pygame包。导入pygame时,它会导入属于pygame的所有模块。一些pygame模块是可选的,如果找不到它们,它们的值将设置为“None”。
       有一个名为“locals”的特殊pygame模块。该模块包含pygame的子集。该模块的成员是常用的常量和函数,已证明将其放入程序的全局命名空间中是有用的。这个locals模块包括像“Rect”这样的函数来创建一个矩形对象,以及许多常量,如“QUIT,HWSURFACE”,用于与其余的pygame交互。像这样将locals模块导入全局命名空间是完全可选的。如果您选择不导入它,则局部作用域下的所有成员始终在pygame模块中可用。
       最后,如果pygame中的字体或声音模块不可用,我们决定打印一条很棒的警告消息。

加载资源(Loading Resource)
这里我们有两个函数可以用来加载图像和声音。 我们将在本节中单独查看每个功能。

def load_image(name, colorkey=None):
    fullname = os.path.join('data', name)
    try:
        image = pygame.image.load(fullname)
    except pygame.error, message:
        print 'Cannot load image:', name
        raise SystemExit, message
    image = image.convert()
    if colorkey is not None:
        if colorkey is -1:
            colorkey = image.get_at((0,0))
        image.set_colorkey(colorkey, RLEACCEL)
return image, image.get_rect()

       此函数可以传入要加载的图像的名称。它还可以选择使用一个参数来设置图像的颜色键。在图形中使用颜色键来表示透明图像的颜色。
此函数的第一件事是创建文件的完整路径名。在此示例中,所有资源都位于“data”子目录中。通过使用os.path.join函数,将创建一个路径名,该路径名适用于运行游戏的任何平台。
       接下来,我们使用pygame.image.load函数加载图像。我们将此函数包装在try / except块中,因此如果加载图像时出现问题,我们可以正常退出。加载图像后,我们对convert()函数进行了重要调用。这将生成Surface的新副本并转换其颜色格式和深度以匹配显示。这意味着将图像blitting 到屏幕将尽快的发生。
       最后,我们为图像设置了colorkey。如果用户为colorkey参数提供了参数,我们将该值用作图像的颜色键。这通常只是一个颜色RGB值,如白色(255,255,255)。您还可以传递值-1作为颜色键。在这种情况下,该函数将在图像的左上角像素处查找颜色,并将该颜色用于colorkey。

def load_sound(name):
    class NoneSound:
        def play(self): pass
    if not pygame.mixer:
        return NoneSound()
    fullname = os.path.join('data', name)
    try:
        sound = pygame.mixer.Sound(fullname)
    except pygame.error, message:
        print 'Cannot load sound:', wav
        raise SystemExit, message
return sound

       接下来是加载声音文件的功能。 这个函数做的第一件事是检查是否正确导入了pygame.mixer模块。 如果没有,它返回一个虚拟播放方法(dummy play method)的小类实例。 这将足够像一个正常的声音对象使这个游戏运行,没有任何额外的错误检查。
       此函数类似于图像加载功能,但处理一些不同的问题。 首先,我们创建声音图像的完整路径,并将声音文件加载到try / except语句块中。 然后我们只简单地返回加载的Sound对象。

游戏对象的类(Game Object Classes)
       这里我们创建两个类来表示游戏中的对象。 几乎游戏的所有逻辑都进入了这两个类。 我们将在这里一次一个地查看它们。

class Fist(pygame.sprite.Sprite):
    """按下鼠标,在屏幕上握紧拳头"""
    def __init__(self):
        pygame.sprite.Sprite.__init__(self) #call Sprite initializer
        self.image, self.rect = load_image('fist.bmp', -1)
        self.punching = 0

    def update(self):
        "根据鼠标位置移动拳头"
        pos = pygame.mouse.get_pos()
        self.rect.midtop = pos
        if self.punching:
            self.rect.move_ip(5, 10)

    def punch(self, target):
        "如果拳头与目标发生碰撞,则返回True"
        if not self.punching:
            self.punching = 1
            hitbox = self.rect.inflate(-5, -5)
            return hitbox.colliderect(target.rect)

    def unpunch(self):
        "called to pull the fist back"
        "调用函数拉回拳头"
        self.punching = 0

       在这里我们创建一个代表玩家拳头的类。它源生自pygame.sprite模块中包含的Sprite类。创建此类的新实例时,将调用__init__函数。我们要做的第一件事是确保为我们的基类调用__init__函数。这允许Sprite的__init__函数准备我们的对象以用作精灵。这个游戏使用一个精灵绘图组类(sprite drawing Group classes)。这些类可以绘制具有“image”和“rect”属性的精灵。通过简单地更改这两个属性,渲染器(renderer)将在当前位置绘制当前图像。
       所有精灵都有一个update()方法。此功能通常每帧调用一次。在这里你应该放置代码来移动和更新sprite的变量。用于拳头的update()方法将拳头移动到鼠标指针的位置。如果拳头处于“出拳”状态,它也会略微抵消拳头位置。
       以下两个函数punch()和unpunch()改变拳头的出拳状态。如果拳头与给定的目标精灵碰撞,则punch()方法也会返回true值。

class Chimp(pygame.sprite.Sprite):
    """
    在屏幕上移动猴子生物。 它被打中时旋转猴子。
    """
    def __init__(self):
        pygame.sprite.Sprite.__init__(self) #call Sprite intializer
        self.image, self.rect = load_image('chimp.bmp', -1)
        screen = pygame.display.get_surface()
        self.area = screen.get_rect()
        self.rect.topleft = 10, 10
        self.move = 9
        self.dizzy = 0

    def update(self):
        "走路或旋转,取决于猴子的状态"
        if self.dizzy:
            self._spin()
        else:
            self._walk()

    def _walk(self):
        "将猴子移过屏幕,然后转向两端"
		newpos = self.rect.move((self.move, 0))
        if self.rect.left < self.area.left or \
            self.rect.right > self.area.right:
            self.move = -self.move
            newpos = self.rect.move((self.move, 0))
            self.image = pygame.transform.flip(self.image, 1, 0)
        self.rect = newpos

    def _spin(self):
        "旋转猴子的图像"
        center = self.rect.center
        self.dizzy += 12
        if self.dizzy >= 360:
            self.dizzy = 0
            self.image = self.original
        else:
            rotate = pygame.transform.rotate
            self.image = rotate(self.original, self.dizzy)
        self.rect = self.image.get_rect(center=center)

    def punched(self):
        "这将导致猴子开始旋转"
        if not self.dizzy:
            self.dizzy = 1
            self.original = self.image

       黑猩猩类的要做的工作比拳头多一点,但并没有更加复杂。这个类将在屏幕上来回移动黑猩猩。当猴子被拳头打中时,他会旋转到令人兴奋的效果。这个类也源生自基础的Sprite类,并初始化与拳头类相同。在初始化时,类还将属性“area”设置为显示屏幕的大小。
       黑猩猩的update函数只是查看当前的“头晕”状态,当猴子被拳击中旋转时返回True。它调用_spin或_walk方法。这些函数以下划线为前缀。这只是一个标准的python习语,它表明这些方法只能由Chimp类使用。我们甚至可以给它们一个双下划线,这将告诉python真正尝试使它们成为私有方法,但我们不需要这样的保护。 ?
       walk方法通过将当前rect移动给定的偏移量来为猴子创建一个新位置。如果此新位置越过屏幕的显示区域,则会反转移动偏移量。它还使用pygame.transform.flip函数镜像图像。这是一种粗糙的效果,使猴子看起来像是在转动他正在移动的方向。
       当猴子目前“头晕”时调用spin方法。 dizzy属性被用于存储当前的旋转量。当猴子一直旋转(360度)时,它会将猴子图像重置回原始的非旋转版本。在调用transform.rotate函数之前,您将看到代码对名为“rotate”的函数进行局部引用。对于这个例子没有必要这样做,这里只是为了保持以下行的长度更短。请注意,调用旋转功能时,我们始终从原始猴子图像旋转。旋转时,质量略有下降。反复旋转相同的图像,每次质量都会变差。此外,旋转图像时,图像的大小实际上会发生变化。这是因为图像的角将旋转出来,使图像更大。我们确保新图像的中心与旧图像的中心匹配,因此它不会移动而旋转。
       最后一个方法是punched(),它告诉精灵进入其晕眩状态。这将导致图像开始旋转。它还制作了名为“original”的当前图像的副本。

初始化所有东西(Initialize Everything)
       在我们可以对pygame做很多事情之前,我们需要确保它的模块已初始化。 在这种情况下,我们还将打开一个简单的图形窗口。 现在我们在程序的main()函数中,它实际上运行了所有东西。

pygame.init()
screen = pygame.display.set_mode((468, 60))
pygame.display.set_caption('Monkey Fever')
pygame.mouse.set_visible(0)

       第一行的初始化pygame的为我们完成了一些工作。它检查导入的pygame模块并尝试初始化它们中的每一个。可以返回并检查模块是否未能初始化,但我们不会在这里费事。也可以采取更多控制并手动初始化每个特定模块。通常不需要这种类型的控制,但如果您愿意,可以使用。
       接下来我们设置显示图形模式。请注意,pygame.display模块用于控制所有显示设置。在这种情况下,我们要求一个简单的瘦窗口。有关于设置图形模式的完整单独教程,但如果我们真的不在乎, pygame会很好地为我们提供有用的东西。 Pygame将选择最佳颜色深度,因为我们没有提供。
       最后我们设置窗口标题并关闭窗口的鼠标光标。非常基本的事情,现在我们有一个小黑窗口准备做我们的吩咐的事。通常光标默认为可见,因此除非我们想隐藏它,否则无需真正设置状态。

创建背景(Create The Background)
       我们的程序在背景上有文本信息。 我们很高兴创建一个surface对象来表示背景并重复使用它。
第一步是创建surface对象。

background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((250, 250, 250))

       这为我们创建了一个与显示窗口大小相同的新surface对象。 注意创建Surface对象后额外调用convert()。 没有参数的转换将确保我们的背景与显示窗口的格式相同,这将为我们提供最快的结果。
       我们还将整个背景填充为纯白色。 填充采用RGB三元组作为颜色参数。

把文字放在背景上并居中(Put Text On The Background, Centered)

if pygame.font:
    font = pygame.font.Font(None, 36)
    text = font.render("Pummel The Chimp, And Win $$$", 1, (10, 10, 10))
    textpos = text.get_rect(centerx=background.get_width()/2)
background.blit(text, textpos)

       如您所见,完成此操作有几个步骤。首先,我们必须创建字体对象并将其渲染到新的surface对象。然后我们找到新surface对象的中心并将其blit(粘贴)到背景上。
       使用字体模块的Font()构造函数创建字体。通常,您会将TrueType字体文件的名称传递给此函数,但我们也可以传递None,它将使用默认字体。 Font构造函数还需要知道我们想要创建的字体大小。
       然后,我们将该字体渲染为新的surface对象。render函数创建一个适合文本大小的新surface对象。在这种情况下,我们还告诉reander创建抗锯齿文本(为了一个漂亮的光滑外观)和使用深灰色。
       接下来,我们需要在显示屏上找到文本的居中位置。我们从文本尺寸创建一个“Rect”对象,这使我们可以轻松地将其分配给屏幕中心。
最后我们将文本blit(blit就像复制或粘贴)到背景图像上。

设定完成后显示背景(Display The Background While Setup Finishes)
我们屏幕上仍然有一个黑色的窗口。让我们等待其他资源加载时显示我们的背景。

screen.blit(background, (0, 0))
pygame.display.flip()

       这会将我们的整个背景blit到显示窗口上。 这个blit是不言自明的,但是这个翻转(flip)行为呢?
       在pygame中,对用于显示surface对象的更改不会立即可见。 通常,必须在已更改的区域中更新显示以使其对用户可见。 使用双缓冲显示器时,必须交换(或翻转)显示以使更改变得可见。 在这种情况下,flip()函数可以很好地工作,因为它只处理整个窗口区域并处理单缓冲和双缓冲表面。

准备游戏对象(Prepare Game Object)
在这里,我们创建了游戏所需的所有对象。

whiff_sound = load_sound('whiff.wav')
punch_sound = load_sound('punch.wav')
chimp = Chimp()
fist = Fist()
allsprites = pygame.sprite.RenderPlain((fist, chimp))
clock = pygame.time.Clock()

       首先,我们使用上面定义的load_sound函数加载两个声音效果。 然后我们创建每个sprite类的实例。 最后我们创建了一个sprite Group,它将包含我们所有的sprite。
       我们实际上使用了一个名为RenderPlain的特殊精灵组。 该精灵组可以将它包含的所有精灵绘制到屏幕上。 它被称为RenderPlain,因为实际上有更高级的Render组。 但对于我们的游戏,我们只需要简单的绘图。 我们通过传递一个包含应该属于该组的所有精灵的列表来创建名为“allsprites”的组。 我们以后可以在这个组中添加或删除精灵,但在这个游戏中我们不需要。
       我们创建的clock对象将用于帮助控制我们游戏的帧速率。 我们将在游戏的主循环中使用它,以确保它不会运行得太快。

主循环(Main Loop)
这里没什么,只是一个无限循环。

while 1:
clock.tick(60)

       所有游戏都以某种循环方式运行。 通常的顺序是检查计算机的状态和用户输入,移动并更新所有对象的状态,然后将它们绘制到屏幕上。 你会看到这个例子并没有什么不同。
       我们还调用了我们的clock对象,这将确保我们的游戏运行速度不超过每秒60帧。

处理所有输入事件(Update the Sprite)
这是处理事件队列的极其简单的情况。

for event in pygame.event.get():
    if event.type == QUIT:
        return
    elif event.type == KEYDOWN and event.key == K_ESCAPE:
        return
    elif event.type == MOUSEBUTTONDOWN:
        if fist.punch(chimp):
            punch_sound.play() #出拳
            chimp.punched()
        else:
            whiff_sound.play() #躲闪
    elif event.type == MOUSEBUTTONUP:
        fist.unpunch()

       首先,我们从pygame获取所有可用的事件并循环遍历每个事件。 前两个测试查看用户是否已退出游戏,或按下了退出键。 在这些情况下,我们只是从main()函数返回,程序干净地结束。
       接下来,我们只是检查鼠标按钮是否被按下或释放。 如果按下按钮,我们询问第一个物体是否与黑猩猩相撞。 我们播放适当的声音效果,如果猴子被击中,我们告诉他开始旋转(通过调用它的punched()方法)。

更新Sprites(Update the Sprites)

allsprites.update()

       Sprite组有一个update()方法,它只调用它包含的所有sprite的update方法。 每个物体都会四处移动,具体取决于它们处于哪种状态。这是黑猩猩将一步一步向一侧移动的地方,或者如果他最近被打了一拳则旋转得更远。

绘制整个场景(Draw The Entire Scene)
既然所有物体都在正确的位置,那么是时候绘制它们了。

screen.blit(background, (0, 0))
allsprites.draw(screen)
pygame.display.flip()

       第一个blit调用将把背景绘制到整个屏幕上。 这会擦除我们从前一帧看到的所有内容(效率稍低,但对于此游戏来说足够好)。 接下来我们调用sprite容器的draw()方法。 由于这个精灵容器实际上是“DrawPlain”精灵组的一个实例,它知道如何绘制我们的精灵。 最后,我们将pygame的软件双缓冲区的内容flip()到屏幕上。 这使得我们绘制的所有内容都可以同时显示。

游戏结束(Game Over)
       用户已退出,是时候清理干净了。在pygame中清理正在运行的游戏非常简单。 事实上,由于所有变量都被自动破坏,我们真的不需要做任何事情。
 

以上内容,自己翻译,可能有误,可参考:Tutorials - 逐行的黑猩猩教程(Line By Line Chimp)

点我回顶部

 
 
 
 
 
 
 
Fin.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值