pygame新手指南

pygame新手指南

Pygame是一个用于SDL的python包装器,由Pete Shinners编写。
这意味着,使用pygame,您可以用Python编写游戏或其他多媒体应用程序,这些程序将在任何SDL支持的平台(Windows、Unix、Mac、BeOS和其他)上运行。

Pygame可能很容易学习,但是图形编程的世界可能会让新手感到困惑。
我写这篇文章是为了总结我在过去一年左右使用pygame及其前身PySDL所获得的实践知识。
我试着将这些建议按照重要性进行排序,但任何特定提示的相关性将取决于您自己的背景和项目的细节。

在Python中舒适地工作。
最重要的是要自信地使用python。
如果你不熟悉你正在使用的语言,那么学习像图形编程这样复杂的东西将是一件非常麻烦的事情。
用python编写一些规模可观的非图形化程序——解析一些文本文件,编写一个猜谜游戏或日志录入程序或其他东西。
熟悉字符串和列表操作——知道如何分割、切片和组合字符串和列表。
了解import是如何工作的——尝试编写一个跨多个源文件的程序。
编写自己的函数,并练习操作数字和字符;
知道如何在两者之间转换。
要知道,使用列表和字典的语法是第二天性——您不希望每次需要对列表进行切片或对一组键进行排序时都必须运行文档。
抵制住去一个邮件列表comp.lang的诱惑。
当你遇到麻烦时,你可以使用python或IRC。
相反,你可以让翻译人员先用几个小时的时间来解决这个问题。
打印出Python 2.0快速参考并保存在您的计算机上。

这可能听起来非常枯燥,但当你编写游戏时,你通过熟悉python而获得的信心将会发挥惊人的作用。
与编写真正的代码所节省的时间相比,你花在编写python代码的第二天性上的时间是微不足道的。

认识到pygame的哪些部分是您真正需要的。
查看pygame Documentation索引顶部的杂乱类可能会让人感到困惑。重要的是要认识到你可以用很小的函数子集做很多事情。许多类你可能永远不会用到——在一年中,我还没有接触过Channel、Joystick、cursor、Userrect、surfarray或版本函数。

知道什么是surfaces。
pygame最重要的部分是surfaces。
把一个surfaces想象成一张空白的纸。
你可以对一个surfaces做很多事情——你可以在它上面画线,用颜色填充它的部分,从它复制图像,在它上面设置或读取单独的像素颜色。
surfaces可以是任意大小(在合理范围内),并且您可以拥有任意数量的surfaces(同样,在合理范围内)。
有一个surfaces是特殊的——使用pygame.display.set_mode()创建的。
这个“显示面”代表屏幕;
无论你对它做什么,都会显示在用户的屏幕上。
你只能有其中一个——这是SDL限制,而不是pygame限制。

那么如何创建surfaces呢?
如上所述,使用pygame.display.set_mode()创建特殊的’显示surfaces’。
可以使用image.load()创建包含图像的surfaces,也可以使用font.render()创建包含文本的surfaces。
您甚至可以使用surface()创建一个不包含任何内容的surfaces。

大多数的surfaces函数都不是关键的。
只要学习blit()、fill()、set_at()和get_at()就可以了。

使用surface.convert()。
当我第一次阅读surface.convert()的文档时,我并不认为这是我需要担心的事情。
“我只使用png格式,因此我所做的一切都是相同的格式。
所以我不需要convert()’;
结果发现我大错特错了。

convert()所指的“格式”不是文件格式(如PNG, JPEG, GIF),而是所谓的“像素格式”。
这指的是一个surfaces在一个特定像素中记录单个颜色的特殊方式。
如果surfaces格式与显示格式不同,SDL将不得不为每一个比特实时转换它——这是一个相当耗时的过程。
不要太担心解释;
只要注意,如果您想从您的比特获得任何类型的速度,convert()是必要的。

如何使用convert?
在使用image.load()函数创建surfaces之后调用它。
不要只是这样做:

surface = pygame.image.load('foo.png')

正确的是这样做的:

surface = pygame.image.load('foo.png').convert()

它是那么容易。
当你从磁盘上加载一个图像时,你只需要在每个surfaces上调用它一次。
你会对结果感到满意;
我看到通过调用convert(),比特传输速度提高了6倍。

唯一一次你不想使用convert()是当你真正需要绝对控制图像的内部格式——说你写一个图像转换程序,你需要确保输出文件有相同的像素格式作为输入文件。
如果你正在编写一款游戏,你就需要速度。
使用convert()。

Dirty rect animation.(Dirty矩形动画)
pygame程序中帧速率不足的最常见原因是对pygame.display.update()函数的误解。使用pygame,仅仅将一些东西绘制到显示surfaces并不会导致它出现在屏幕上——您需要调用pygame.display.update()。有三种方法可以调用这个函数:
pygame.display.update() – 这将更新整个窗口(或全屏显示的整个屏幕)。
pygame.display.flip() --如果你使用的是双缓冲硬件加速,这也会做同样的事情,但你没有,等等……
pygame.display.update(a rectangle or some list of rectangles)–这只更新您指定的屏幕矩形区域。

大多数刚接触图形编程的人都会使用第一个选项——他们会在每一帧更新整个屏幕。
问题是,这对于大多数人来说是不可接受的慢。
在我的机器上调用update()需要35毫秒,这听起来并不多,直到你意识到1000 / 35 =每秒28帧。
这没有游戏逻辑,没有比特,没有输入,没有AI,什么都没有。
我只是坐在那里更新屏幕,28帧每秒是我的最大帧率。
啊。
解决方案称为“Dirty rect animation(Dirty矩形动画).”。
比起每一帧都更新整个屏幕,我们只更新自最后一帧以来发生变化的部分。
我通过跟踪列表中的这些矩形,然后在帧的末尾调用update(the_dirty_rectangles)来实现这一点。
详细为一个移动的精灵:

  • 在精灵的当前位置上块背景,擦除它。
  • 将精灵的当前位置矩形添加到dirty_rects列表中。
  • 移动精灵。
  • 在它的新位置绘制精灵。
  • 将精灵的新位置添加到我的dirty_rects列表中。
  • 调用display.update (dirty_rects)

速度上的差别是惊人的。
考虑到SolarWolf拥有许多不断移动的精灵,并且仍有足够的时间在背景中呈现视差星场,并进行更新。
这种技术在两种情况下都不起作用。
第一种情况是,整个窗口或屏幕在每一帧都得到更新——可以将平滑滚动引擎想象成实时策略游戏或横向滚动引擎。
那么在这种情况下该怎么做呢?
简单地说,不要用pygame来编写这类游戏。
较长的答案是每次滚动几个像素的步骤;
不要试图让滚动完美平滑。
你的玩家会喜欢滚动速度快,不会注意到背景跳跃太多的游戏。
最后需要注意的是,并非所有游戏都需要高帧率。
一款战略战争游戏能够轻松地通过每秒几次更新—-在这种情况下,添加肮脏的矩形动画的复杂性可能是不必要的。

复杂的Hardware surfaces
如果你一直在看pygame.display.set_mode()中可以使用的各种标志,你可能会这样想:嘿,HWSURFACE!我想要,谁不喜欢硬件加速呢。噢噢噢…DOUBLEBUF;好吧,听起来很快,我想我也想要。这不是你的错;多年的3d游戏经验让我们相信硬件加速是好的,软件渲染是慢的。
不幸的是,硬件渲染有一长串的缺点:

  • 它只适用于某些平台。如果你需要,Windows机器通常会有硬件surfaces。大多数其他平台无法做到。例如,如果安装了X4,如果DGA2工作正常,如果卫星正确对齐,Linux可能能够提供一个硬件surfaces。如果硬件surfaces不可用,SDL将默默地为您提供一个软件surfaces。
  • 它只适用于全屏。
  • 它使逐像素访问变得复杂。如果你有一个硬件surfaces,你需要锁定surfaces之前写入或读取个别像素值。如果你不这样做,坏事就会发生。然后你需要快速解锁surfaces再次,在操作系统变得混乱和开始恐慌。在pygame中,这个过程的大部分都是自动化的,但是需要考虑其他的东西。
  • 你失去了鼠标指针。如果你指定了HWSURFACE(实际上得到了它),你的指针通常会消失(或者更糟,停留在一半在那里,一半不闪烁的状态)。你需要创建一个精灵来充当手动鼠标指针,你需要担心指针的加速和灵敏度。
  • 它可能会慢一些。许多驱动程序并没有像我们所做的那样对绘图类型进行加速,而且因为所有的东西都必须通过视频总线进行比特(除非你也可以将源代码surfaces塞进视频内存中),所以它最终可能会比软件访问要慢。

硬件渲染有它的位置。它在Windows下非常可靠地工作,所以如果您对跨平台性能不感兴趣,那么它可能会为您提供显著的速度提升。然而,这是有代价的——增加了麻烦和复杂性。最好还是坚持使用老式的可靠SWSURFACE,直到你确定你知道自己在做什么。

不要被次要的问题分心
硬件渲染有它的位置。它在Windows下非常可靠地工作,所以如果您对跨平台性能不感兴趣,那么它可能会为您提供显著的速度提升。然而,这是有代价的——增加了麻烦和复杂性。最好还是坚持使用老式的可靠SWSURFACE,直到你确定你知道自己在做什么。
例如,考虑如何组织图形文件的问题。
每个帧应该有自己的图像文件,还是每个精灵?
也许所有的图形应该被压缩到一个存档?
大量的时间被浪费在了许多项目上,在邮件列表上问这些问题,辩论答案,分析,等等。
这是一个次要问题;
任何讨论它的时间都应该花在编写真正的游戏上。
这里的见解是,拥有一个实际执行的“相当好的”解决方案,远比一个你从未花时间编写的完美解决方案要好得多。

Rects是你的朋友。
Pete Shinners的包装可能有很酷的alpha效果和快速的比特速度,但我必须承认我最喜欢的部分pygame是低级的Rect类。
矩形就是一个矩形——仅由其左上角的位置、宽度和高度定义。
许多pygame函数接受rects作为参数,它们也接受’rectstyles’,一个与rect具有相同值的序列:

rect = pygame.Rect(10, 20, 30, 30)
rect = pygame.Rect((10, 20, 30, 30))
rect = pygame.Rect((10, 20), (30, 30))
rect = (10, 20, 30, 30)
rect = ((10, 20, 30, 30))

但是,如果使用前三个版本中的任何一个,就可以访问Rect的实用函数。
其中包括移动、收缩和膨胀矩形的函数,找到两个矩形的并集,以及各种碰撞检测函数。
例如,假设我想要获得包含点(x, y)的所有精灵的列表——也许玩家点击了那里,或者这是子弹的当前位置。
如果每个精灵都有一个.rect成员,这就很简单了——我就是这么做的:

sprites_clicked = [sprite for sprite in all_my_sprites_list if sprite.rect.collidepoint(x, y)]

矩形与surfaces或图形函数没有其他关系,除了你可以使用它们作为参数。您也可以在与图形无关但仍然需要定义为矩形的地方使用它们。在每个项目中,我都发现了一些我从未想过会用到rects的地方。

不要在意像素完美的碰撞检测。
你让精灵四处移动,你需要知道它们是否会相互碰撞。我们很容易这样写:

  • 检查矩形是否发生碰撞。如果不是,那就忽略他们。
  • 对于重叠区域中的每个像素,查看来自两个精灵的对应像素是否不透明。如果是这样,就会发生碰撞。

还有其他方法来做到这一点,与安丁精灵面具等,但任何方式你做它在pygame,它可能会太慢。对于大多数游戏来说,最好是只做“子矩形碰撞”——为每个精灵创建一个比实际图像稍小的矩形,并将其用于碰撞。这样会更快,而且在大多数情况下玩家不会注意到不精确性。

管理事件子系统。
Pygame的事件系统有点棘手。实际上有两种不同的方法可以找出输入设备(键盘、鼠标或操纵杆)在做什么。
第一种方法是直接检查设备的状态。可以通过调用pygame.mouse.get_pos()或pygame.key.get_pressed()来实现这一点。这将告诉您在调用函数时该设备的状态。
第二种方法使用SDL事件队列。这个队列是一个事件列表——在检测到事件时将其添加到列表中,在读取它们时将其从队列中删除。
每个系统都有优点和缺点。
状态检查(系统1)为您提供了精度——您确切地知道给定的输入是在什么时候做出的——如果mouse.get_pressed([0])为1,这意味着此时鼠标左键是向下的。
事件队列仅仅报告鼠标在过去的某个时间宕机;
如果您相当频繁地检查队列,这是可以的,但是如果您被其他代码延迟了检查它,输入延迟可能会增加。
状态检查系统的另一个优点是它可以很容易地检测到“和弦”;
也就是说,同时在几个州。
如果你想知道t和f键是否同时下来,只要检查一下:

if (key.get_pressed[K_t] and key.get_pressed[K_f]):
    print "Yup!"

然而,在队列系统中,每个按键到达队列时都是一个完全独立的事件,所以在检查f键时,您需要记住t键已经关闭,还没有出现。
有点复杂。
然而,国家体制有一个很大的弱点。
它只报告设备在它被调用时的状态;
如果用户点击鼠标按钮,然后在调用mouse.get_pressed()之前释放它,鼠标按钮将返回0——get_pressed()完全错过鼠标按钮按下。
但是,MOUSEBUTTONDOWN和MOUSEBUTTONUP这两个事件仍然在事件队列中,等待被检索和处理。
教训是:选择符合您需求的系统。
如果你的循环中没有太多内容——假设你只是坐在while 1循环中,等待输入,使用get_pressed()或其他状态函数;
延迟将会更低。
另一方面,如果每个按键都很重要,但延迟并不重要——比如用户在编辑框中输入内容,那么就使用事件队列。
有些按键可能会稍晚一些,但至少你会得到所有按键。
关于event.poll() vs. wait()——poll()的注意事项可能看起来更好,因为它在等待输入时不会阻止程序执行任何操作——wait()会挂起程序,直到接收到事件。
但是,poll()在运行时将消耗100%的可用CPU时间,并且它将用NOEVENTS填充事件队列。
使用set_blocked()来选择您感兴趣的事件类型——您的队列将更易于管理。

Colorkey与Alph。
这两种技术有很多混淆,其中大部分来自所使用的术语。
“Colorkey blitering”包括告诉pygame某幅图像中某一颜色的所有像素都是透明的,而不是它们碰巧是什么颜色。
当图像的其余部分被分散时,这些透明像素不会被分散,因此不会模糊背景。
这就是我们制作不是矩形精灵的方法。
只需调用surface.set_colorkey(color),其中color是一个RGB元组——比如(0,0,0)。
这将使源图像中的每个像素都是透明的,而不是黑色的。
“阿尔法”不同,它有两种风格。
“图像alpha”适用于整个图像,这可能是你想要的。
正确地称为“半透明”,alpha使源图像中的每个像素只有部分不透明。
例如,如果你设置一个surfaces的alpha值为192,然后将其移到背景上,那么每个像素的3/4的颜色将来自源图像,1/4来自背景。
Alpha从255到0,其中0是完全透明的,255是完全不透明的。
请注意,colorkey和alpha blbitt可以组合在一起——这将产生一个图像,在某些点是完全透明的,而在其他点是半透明的。
“逐像素alpha”是alpha的另一种风格,它更复杂。
基本上,源图像中的每个像素都有自己的alpha值,从0到255。
每个像素,因此,可以有一个不同的不透明度时,比特到背景。
这种类型的alpha不能与colorkey blitt混合,并且它覆盖了每个图像的alpha。
每像素alpha很少在游戏中使用,为了使用它,你必须将源图像保存在带有特殊alpha通道的图形编辑器中。
它很复杂,先别用。

用Python的方式做事。
最后一点(这不是最不重要的一点;
它只是在结尾)。
Pygame是SDL的一个非常轻量级的包装器,而SDL又是本地OS图形调用的一个非常轻量级的包装器。
如果您的代码仍然很慢,并且您已经完成了我上面提到的事情,那么问题就在于您在python中处理数据的方式。
有些习惯用法在python中会很慢,不管你做什么。
幸运的是,python是一种非常清晰的语言——如果一段代码看起来笨拙或笨拙,那么它的速度也有可能得到提高。
请阅读Python性能技巧,了解一些关于如何提高代码速度的好建议。
也就是说,不成熟的优化是万恶之源;
如果它不够快,不要为了使它更快而折磨代码。
有些事情是注定不会发生的:)就是这样。
现在你知道了我所知道的关于pygame的一切。
现在,去写那个游戏吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值