逐行的黑猩猩教程(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:', nameraise SystemExit, messageimage = 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): passif 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:', wavraise 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 initializerself.image, self.rect = load_image('fist.bmp', -1)self.punching = 0def update(self):"根据鼠标位置移动拳头"pos = pygame.mouse.get_pos()self.rect.midtop = posif self.punching:self.rect.move_ip(5, 10)def punch(self, target):"如果拳头与目标发生碰撞,则返回True"if not self.punching:self.punching = 1hitbox = 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 intializerself.image, self.rect = load_image('chimp.bmp', -1)screen = pygame.display.get_surface()self.area = screen.get_rect()self.rect.topleft = 10, 10self.move = 9self.dizzy = 0def 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.movenewpos = self.rect.move((self.move, 0))self.image = pygame.transform.flip(self.image, 1, 0)self.rect = newposdef _spin(self):"旋转猴子的图像"center = self.rect.centerself.dizzy += 12if self.dizzy >= 360:self.dizzy = 0self.image = self.originalelse:rotate = pygame.transform.rotateself.image = rotate(self.original, self.dizzy)self.rect = self.image.get_rect(center=center)def punched(self):"这将导致猴子开始旋转"if not self.dizzy:self.dizzy = 1self.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:returnelif event.type == KEYDOWN and event.key == K_ESCAPE:returnelif 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.

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

  1. Pygame 官方文档 - Tutorials - 游戏制作指南(Making Games Tutorial)- 正式开干(Kicking things off)

    游戏制作指南(Making Games Tutorial) 点我回总目录 ☚ 3.正式开干(Kicking things off) 代码的第一部分相对简单,一旦编写,通常可以在您制作的每个游戏中重复使 ...

  2. Pygame 官方文档 - Tutorials - Surfarray模块介绍(Surfarray Introduction)

    Surfarray模块介绍(Surfarray Introduction) Author: Pete Shinners Contact: pete@shinners.org 介绍(Introducti ...

  3. Pygame 官方文档 - Tutorials - 游戏制作指南(Making Games Tutorial)- 校订:基础(Revision: Pygame fundamentals)

    游戏制作指南(Making Games Tutorial) 点我回总目录 ☚ 2.校订:Pygame基础(Revision: Pygame fundamentals) 2.1.基本的Pygame游戏 ...

  4. Pygame 官方文档 - 中译版

    pygame 文档 Pygame之家[Pygame Home] 帮助内容[Help Contents] 指导索引[Reference Index] 最有用的东西 [Most useful stuff] ...

  5. Hyperledger Fabric 2.0 官方文档中文版 第6章 教程(上)

    Hyperledger Fabric 2.0 官方文档中文版第6章 教程上 总目录 6.教程(上) 将智能合约部署到通道 启动网络 Logspout设置 打包智能合约 安装链码包 批准链码定义 将链码 ...

  6. Hyperledger Fabric 2.0 官方文档中文版 第6章 教程(下)

    Hyperledger Fabric 2.0 官方文档中文版 第6章 教程下 总目录 6.教程(下) 使用CouchDB 为什么使用CouchDB? 在Hyperledger Fabric中启用Cou ...

  7. Pygame 官方文档 - pygame.display

    Pygame.display Pygame中用于控制窗口和屏幕显示的模块 pygame.display.init - 初始化display模块 pygame.display.quit - 取消初始化d ...

  8. Pygame 官方文档 - pygame.cursors

    pygame.cursors Pygame 中使用光标资源的模块. pygame.cursors.compile -- 由纯字符串创建二进制光标数据 pygame.cursors.load_xbm - ...

  9. Pygame 官方文档 - pygame.key

    pygame.key 与键盘相关的 Pygame 模块. pygame.key.get_focused() - 当窗口获得键盘的输入焦点时返回 True pygame.key.get_pressed( ...

最新文章

  1. Redis详解(八)------ 主从复制
  2. 集合之四:List接口
  3. asp.net内置对象
  4. 数学--数论--质数处理
  5. LeetCode 214. 最短回文串(字符串哈希)
  6. 哥德巴赫猜想(洛谷P1304题题解,Java语言描述)
  7. 防止API被恶意调用,一般有哪些方法?
  8. Android 系统广播
  9. c语言 graph,基于图(graph)的应用举例
  10. Django之Form组件补充
  11. element table 无数据时显示图片替换“暂无该数据“
  12. 二、Kubernetes (k8s) 内网集群的搭建详细图解
  13. 光功率 博科交换机_博科光纤交换机zone划分命令方法
  14. 网络七层协议结构分析图
  15. 工具类--生成随机姓名及指定长度随机字符串
  16. 【北亚数据恢复】MSSQL 2000 出现“823”报错信息的数据恢复案例
  17. 【Splay 总结】
  18. 让你的linux fedora 超过mac帅
  19. 关于Java性能调优的11个简单技巧
  20. html下拉菜单读取xml,基于XML的可配置Html下拉框的设计与实现

热门文章

  1. kubernetes集群安装
  2. 人类又被AI碾压,这次是星际争霸
  3. 反恐精英枪王对决 服务器维护6,反恐精英之枪王对决
  4. linux下lds链接脚本详解
  5. usb网卡在linux上能直接用吗,关于usb网卡如何在红旗linux6.0上使用的问题
  6. 如何包含鼠标指针(箭头)截图或者录屏?
  7. 成人想补充蛋白质,光吃鸡蛋就够了?鸡蛋和蛋白粉哪个好一点?
  8. IT人必看!2018年上半年云栖大会300份干货PPT免费开放!最前沿的技术都在这了!
  9. 【JVM】GC垃圾回收(三)——零落成泥碾作尘,只有香如故
  10. 社交系统ThinkSNS+ 性能简述