pygame编程入门之七:Making Games With Pygame1
1. Introduction
首先假设您已经阅读了在线黑猩猩教程的代码,它介绍了Python和pygame的基础知识。
本教程的目标读者是那些懂得如何制作一个非常简单的小“游戏”的人,他们要做一个相对简单的游戏,比如“乒乓”。它向你介绍了游戏设计的一些概念,通过简单的数学计算出了球物理反弹,以及一些让游戏易于维护和扩展的方法。
本教程中的所有代码都致力于实现TomPong功能。在本教程结束时,你不仅应该对pygame有更强理解,而且还应该了解TomPong是如何工作的,以及以后如何制作自己的游戏。
现在,简单回顾一下pygame的基础知识。为游戏组织代码的一种常见方法是将其划分为以下六个部分:
- 1、在游戏中需要的加载模块。标准内容,除此以外,记得导入pygame本地名称以及pygame模块本身。
- 2、资源类处理;定义一些类来处理基本资源,这些资源将加载图像和声音,以及连接和断开网络、加载保存游戏文件和任何其他资源。
- 3、游戏对象类;为游戏对象定义类。
在pong的例子中,这些将是玩家的bat(你可以多次初始化一次,为游戏中的每个玩家初始化一个),一个用于球(可以有多个实例)。如果要一个漂亮的游戏菜单,那么做一个菜单类也是个好主意。 - 4、其他游戏功能;定义其他必要的功能,如记分板、菜单处理等。任何放入主游戏逻辑的代码,将使理解逻辑更加困难,如果是功能块,应该放到它的功能中。
因此,绘制计分板并不是游戏逻辑,应该被移动到一个函数中。 - 5、初始化游戏,包括pygame对象本身、背景、游戏对象(初始化类的实例)和其他您可能想要添加的代码。
- 6、主循环,放入任何输入处理(例如,监视用户点击键盘/鼠标按钮),更新游戏对象的代码,最后是更新屏幕。
所做的每一款游戏都有这样一些或全部的版块,可能还有更多自定义版块。
本教程将写关于“乒乓”如何布局,这样的想法可以被移植到任何做的游戏中。
假设将所有代码保存在单个文件中,但如果您正在进行一个相当大的游戏,那么将某些部分提供到模块文件中通常是一个好主意。
将游戏对象类放入名为object的文件中。py可以帮助您将游戏逻辑与游戏对象分离开来。
如果有大量的资源处理代码,那么将其放入resources.py也很方便。然后,您可以从对象、资源 import *来导入所有的类和函数。
1.1. 编码风格
在进行任何编程项目时,首先要记住的是决定一种编码风格,并保持一致。
严格解释的空格和缩进,Python可以解决很多问题。你可以选择缩进的大小,把每个模块导入一个新行,如何注释代码,等等。
你会看到我如何处理这些代码示例;你不需要使用的风格,但是无论采用什么风格,都要在程序代码中使用它。
尝试为所有类加说明,并对任何看起来晦涩难懂的代码进行注释,不过不要开释明显的代码。我见过很多人这样做:
player1.score += scoreup # Add scoreup to player1 score
最糟糕的代码就是布局很差,样式随意变化,说明文档也很糟糕。
糟糕的代码不仅会让其他人厌烦,而且还让你难以维护。
2。修订:Pygame基本面
2.1基本Pygame游戏
为了修正,以假定你已经熟悉Pygame程序的基本结构。我运行一段简单的Pygame代码,它会显示一个窗口有一些文本,差不多这样。(当然不同的系统,窗口可能会不同):
#!/usr/bin/python
import pygame
from pygame.locals import *
def main():
# Initialise screen
pygame.init()
screen = pygame.display.set_mode((150, 50))
pygame.display.set_caption('Basic Pygame program')
# Fill background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((250, 250, 250))
# Display some text
font = pygame.font.Font(None, 36)
text = font.render("Hello There", 1, (10, 10, 10))
textpos = text.get_rect()
textpos.centerx = background.get_rect().centerx
background.blit(text, textpos)
# Blit everything to the screen
screen.blit(background, (0, 0))
pygame.display.flip()
# Event loop
while 1:
for event in pygame.event.get():
if event.type == QUIT:
return
screen.blit(background, (0, 0))
pygame.display.flip()
if __name__ == '__main__': main()
2.2。基本Pygame对象
如您所见,代码主要由三个对象组成:屏幕、背景和文本。
每个对象通过首先调用内置Pygame对象的实例来创建,然后修改它以满足我们的需要。
屏幕是一个稍微特殊的例子,通过Pygame调用来修改显示,而不是调用属于屏幕对象的方法。
但是对于所有其他Pygame对象,我们首先创建对象作为Pygame对象的副本,赋予它一些属性,并从它们构建游戏对象。
对于背景,首先创建了一个Pygame_Surface对象,并使其大小为屏幕的大小。然后我们执行convert()操作,将表面转换为单个像素格式。
当几个图像和表面有不同的像素格式时,这显然是必要的,渲染速度会非常慢。通过转换所有的表面,可以大大减少渲染时间。
最后,用白色填充背景表面(255,255,255)。这些值是RGB(红绿蓝),可以从任何好的色板程序获得。
对于文本,可能需要不止一个对象。首先创建一个字体对象,它定义要使用哪种字体,以及字体的大小。
然后创建一个文本对象,通过使用属于字体对象的render方法,需要提供三个参数:要呈现的文本内容,是否反锯齿(1=yes,0=no),以及文本的颜色(RGB格式)。
接下来,创建第三个文本对象,它获取文本的矩形。理解这一点的最简单的方法是:想象画个矩形,包围所有的文本;然后用这个矩形来获取/设置屏幕上文本的位置。
我们得到了矩形,把它的centerx属性设置为背景centerx属性(所以文本的中心将与背景的中心相同,也就是文本将以x轴上的屏幕为中心)。我们也可以设置y坐标,没有设置垢话,默认文本以屏幕的顶部对齐。由于屏幕很小,没有必要设置值。
2.3。Blitting
现在已经创建了游戏对象,我们需要渲染它们。如果不这样,我们运行程序,只会看到一个空白的窗口,这些对象将不可见。
渲染对象的术语是“Blitting”,即将属于该对象的像素复制到目标对象上。为了渲染背景对象,把它Blitting在屏幕上。在这个例子中,为了变得简单,我们把文本Blitting在背景上(这样背景就会有一个文本的副本),然后将背景Blitting在屏幕上。
在任何游戏中,Blitting是最慢的操作之一,所以你需要小心,不要在每一帧中都Blitting太多内容到屏幕。
如果你一个背景图像,一个球在屏幕上滚动,你可以把背景和球Blitting在每一帧上,这样就可以掩盖球的前一个位置并呈现出新的球,但这是相当慢的。
一个更好的解决方案是把背景设置在球之前的区域,通过球的此矩形找到这个区域,然后把球Blitting到球的新位置,这样就只会在两个小区域上Blitting。
2.4。事件循环
一旦设置好了游戏,需要把它放到一个无限循环中,这样它就会一直运行,直到用户发出他/她退出的信号。
你开始一个打开的while循环,然后对于循环的每次迭代,也就是游戏的每一帧,更新游戏。
第一件事是检查任何Pygame事件,用户点击键盘,点击鼠标按钮,移动操纵杆,调整窗口大小,或者试图关闭它。
我们只要注意,用户试图通过关闭窗口来退出游戏,游戏返回,结束while循环。
只需要重新设置背景,然后flip(update)显示,把所有的东西画出来。
好吧,例子中没有任何变化或发生,严格地说,在每次迭代都需要重新调整背景,当对象在屏幕上移动时,你需要重做所有Blitting。
2.5。Ta-da!
这就是一个完整的游戏!所有的游戏都将采用类似形式。对于实际的游戏来说,还会有更多的代码。
但是基本功能与这个差不多,在结构上没有比这更复杂。本教程的真正内容,就是细化整个流程,现在继续下去。
3。开始做事了
代码第一部分相对简单,并且一旦编写完成,通常可以在以后的每一个游戏中重用。
他们把所有无聊的、一般的任务,比如加载模块,加载图片,打开网络连接,播放音乐等等完成。它们还包括一些简单但有效的错误处理,以及您导入诸如sys和pygame等模块,在功能之上提供的更多的模块。
3.1。第一行和加载模块
首先,需要开始游戏并加载模块。设置一些东西直接在顶部的主要源文件,如文件的名称,它包含什么许可,和其他有用信息。给那些会看到代码的人,这是一个好主意。
然后加载模块,并进行一些错误检查,这样Python就不会打印出令人讨厌的回馈,而这是非程序员所无法理解的。
代码相当简单,所以不需要解释任何一句:
#!/usr/bin/env python
#
# Tom's Pong
# A simple pong game with realistic physics and AI
# http://www.tomchance.uklinux.net/projects/pong.shtml
#
# Released under the GNU General Public License
VERSION = "0.4"
try:
import sys
import random
import math
import os
import getopt
import pygame
from socket import *
from pygame.locals import *
except ImportError, err:
print "couldn't load module. %s" % (err)
sys.exit(2)
3.2. 资源处理函数
在黑猩猩的例子中,第一段代码是加载图像和声音。由于这些模块完全独立于任何游戏逻辑或游戏对象,它们被编写为单独的函数,并且提前编写,以便后边代码可以使用。
我通常把所有这类性质的代码放在类型无关函数中;一般来说,这些都是资源处理函数。
当然,可以为这些创建类,这样就可以把它们组合在一起,或者有一个可以控制所有资源的对象。与任何好的编程环境一样,这取决于你自己的最佳实践和风格。
编写自己的资源处理函数总是一个好主意。尽管Pygame有打开图像和声音的方法,其他模块也有打开其他资源的方法。但通常不提供令人满意的错误处理,所以需要自己修改一致。
编写资源处理函数可以提供复杂的、可重用的代码,并使您对资源有更多的控制。以图像加载函数为例:
def load_png(name):
""" Load image and return image object"""
fullname = os.path.join('data', name)
try:
image = pygame.image.load(fullname)
if image.get_alpha() is None:
image = image.convert()
else:
image = image.convert_alpha()
except pygame.error, message:
print 'Cannot load image:', fullname
raise SystemExit, message
return image, image.get_rect()
这个函数假设所有图像都在一个名为data的目录中,它接受文件名并创建完整路径名,例如data/ball.png,使用操作系统模块来确保跨平台的兼容性。
然后尝试加载图像,并转换任何alpha区域,这样可以获得透明性.如果出现问题,它会返回一段更易于人类阅读的错误。如果正常,它返回image对象及其rect。
您可以为加载其他资源做类似的功能,比如加载声音。还可以创建资源处理类,以便在更复杂的资源中提供更大的灵活性。
例如,你可以创建一个音乐类,用一个init函数来加载声音(可能从loadsound()函数中调用),一个暂停音乐的函数,一个重新启动函数。
另一个方便的资源处理类用于网络连接。打开套接字,通过适当的安全性和错误检查传递数据,关闭套接字、寻址和其他网络任务。可以使编写具有网络功能的游戏相对轻松。
记住,这些函数/类的主要任务是,当您开始编写游戏对象类和主循环时,在循环里几乎没有什么可做的了。
类继承可以使这些基本类特别友好。不过不要走极端。只被一个类使用的函数应该作为该类的私有函数,而不能作为全局方法。