版本1和28间的区别 (跳过第27版)
于2006-07-28 23:12:07修订的的版本1
大小: 10953
编辑: czk
备注:
于2006-09-03 22:42:22修订的的版本28
大小: 17484
编辑: czk
备注:
删除的内容标记成这样。 加入的内容标记成这样。
行号 1: 行号 1:
在这里详述 Python游戏设计基础/第6讲:Pygame介绍.

About

    Pygame is a cross-platfrom library designed to make it easy to write multimedia software, such as games, in Python. Pygame requires the Python language and SDL multimedia library. It can also make use of several other popular libraries.


Installation

    You should definitely begin by installing a binary package for your system. The binary packages usually come with or give the information needed for dependencies. Choose an appropriate installer for your system and version of python from the pygame downloads page. http://www.pygame.org/download.shtml

    Installing from source is fairly automated. The most work will involve compiling and installing all the pygame dependencies. Once that is done run the "setup.py" script which will attempt to auto-configure, build, and install pygame.

    Much more information about installing and compiling is available in the install.html file.


Help

    If you are just getting started with pygame, you should be able to get started fairly quickly. Pygame comes with many tutorials and introductions. There is also full reference documentation for the entire library. Browse the documentation from the documenantation index. docs/index.html.

    On the pygame website, there is also an online copy of this documentation. You should know that the online documentation stays up to date with the development version of pygame in cvs. This may be a bit newer than the version of pygame you are using.

    Best of all the examples directory has many playable small programs which can get started playing with the code right away.


Dependencies

    Pygame is obviously strongly dependent on SDL and Python. It also links to and embeds several other smaller libraries. The font module relies on SDL_tff, which is dependent on freetype. The mixer (and mixer.music) modules depend on SDL_mixer. The image module depends on SDL_image, which also can use libjpeg and libpng. The transform module has an embedded version of SDL_rotozoom for its own rotozoom function. The surfarray module requires the python Numeric package for its multidimensional numeric arrays.

License

    This library is distributed under GNU LGPL version 2.1, which can be found in the file "doc/LGPL". I reserve the right to place future versions of this library under a different license. http://www.gnu.org/copyleft/lesser.html

    This basically means you can use pygame in any project you want, but if you make any changes or additions to pygame itself, those must be released with a compatible license. (preferably submitted back to the pygame project). Closed source and commercial games are fine.

    The programs in the "examples" subdirectory are in the public domain.

Pygame Installation

Python can be built from source, but the easiest way is to get a binary package for your type of system and version of Python. This document will give you information on either type of installation.

Windows Binary Installer

    This is probably the most popular method of installation. If you are running on windows, it is highly recommended you use this form of installing. The installers come with with nearly everything you need, and have an easy point and click installers.

    The first thing you will need is an installation of Python. Python binary installers make it easy to get this done. Pygame binaries usually come for the latest 2 releases of Python, so you'll want to be fairly up to date.

    Once that is in place, you want to download the appropriate windows binary. From the pygame downloads page you can find the .EXE file you need. This will automatically install all of pygame and all the SDL dependencies. The windows binaries have filenames like this; "pygame-1.7.1.win32-py2.4.exe". This would be the installer for pygame version 1.7.1, for Python version 2.4. You shouldn't have trouble finding the correct binary from the "Windows" section of the download page. http://www.pygame.org/download.shtml.

    You will also probably want to install the windows documentation and installation package. This will add easy links to the different documentation and games that come with pygame. The installer for this is found next to the other windows binary downloads. The filename looks like this; "pygame-docs-1.7.1.exe". And this would install the documentation and examples for pygame-1.7.1

    One other thing the windows binaries are missing is the Numeric Python package. You can easily install this separately and it will allow you to use the pygame "surfarray" module. This module is optional, so there is no need to do this. There are binary installers from the Numeric download page. http://sourceforge.net/project/showfiles.php?group_id=1369.


Unix Binary Packages

    For many unix systems, the easiest way to install pygame is from source. Still, there are binary packages available for different distributions.

    There are several binary RPM packages for linux machines. These are actually a little bit of work to install, since you will also need several RPM packages for the dependencies. There is a good chance your linux distribution came with the needed dependencies (like Python and SDL). There are binary RPMs available from the website for each dependency.

    For debian systems, pygame is actively maintained in the debian archives. Visit the debian pygame page for more information. http://packages.qa.debian.org/p/pygame.html

    FreeBSD also has an active pygame package. While techinicaly it isn't binary, it is automatically built and installed by the ports manager. See the FreeBSD package page for more information. http://www.freebsd.org/cgi/ports.cgi?query=py-game&stype=name

    Gentoo has a builtin package for pygame. This is compiled for your system as it installs, similar to BSD, http://www.gentoo.org/dyn/pkgs/dev-python/pygame.xml


Mac OS X Binaries

    For Mac OS X 10.3 and above, binary packages are available from pythonmac.org packages: http://pythonmac.org/packages/

    This package includes almost of the dependencies required for pygame (SDL, SDL_image, etc.), but you need PyObjC 1.2 or later, and may also want to get Numeric and PyOpenGL.

    To build self-contained pygame applications, you should use py2app. There is an example in: /Developer/Python/pygame/Examples/macosx/aliens_app_example



Installing From Source

    Compiling and installing pygame is handled by Python's distutils. Pygame also comes with some scripts to automatically configure the flags needed to build pygame. Use the "setup.py" script to start the installation.

    The first time you run the setup script, it will call the "config.py" script. This will build a "Setup" file which stores all the information needed to compile. The "config.py" will do a good job of detecting what dependencies are available and where they are located. If it isn't perfect, it is easy to build your own, or edit the created "Setup" text file. This "Setup" file is a simple Makefile-like text file. It defines variables needed to use each dependency, and then enables all the pygame modules with found dependencies. If you have trouble compiling, you should be able to easily fix any problems inside the "Setup" file.

    Running the "setup.py" script will call distutils to build and install the pygame package. Distutils actually supports a wide variety of compile and install options. running "python setup.py help" will start to show you the different options available. You can change many things like install locations, compiler to use, and more. Calling the "setup.py" script with no arguments and it will just ask you if you want the default flags needed to compile and install.


Windows Compiling Info

    Compiling all the dependencies on windows is a real challenge. Fortunately there is zip file with all the libraries and headers you need to compile. You should definitely unzip this into your pygame source folder, and all the files go into a "prebuilt" subdirectory. The autoconfigure "config.py" script will find this prebuilt directory and make use of it for you. http://www.pygame.org/ftp/win32-dependencies.zip. If you don't use the prebuilt binaries directory, the autoconfig script will scan the neighboring directory trees to find the dependencies.

    When installing on windows, the "setup.py" script will also copy all the used .DLL files into the pygame directory.

    If compiling with VisualC, these instructions are all you'll need to do. If you are using Borland's free compiler or cygwin, you will need an extra step. You need to convert the VisualC style .LIB files into the format used by your compiler. Both Borland and Cygwin include a command to do this. To convert the Python library the command looks like this with Borland: COFF2OMF python22.lib python22_bcpp.lib. For Cygwin, this is the command you need: DLLTOOL --dllname python15.dll --def python15.def --output-lib libpython15.a. You will also need to do this conversion on all the LIB files that come with the prebuilt dependencies (in the prebuilt/lib directory). Note that with cygwin you should replace the --def flag and argument with --export-all-symbols since they do not come with .def files.

    Mingw is well supported by SDL, but a little rougher for Python extensions. Here is a link with more information to compiling with non-ms compilers. http://www.python.org/doc/current/inst/non-ms-compilers.html


Unix Compiling Info

    Compiling from linux shouldn't give you any problems. One thing you must keep in mind is that most linux RPM packages separate the actual library from the "dev" files needed to compile. To build you will need to make sure the packages like "SDL-dev" are installed.

    You can check to see if SDL is ready to be built from by running the command sdl-config and seeing if it is found. If the sdl-config script is not on the path (or you have more than one?) Set the environment variable SDL_CONFIG to its location.

    Sometimes you will have the SDL libraries installed in once location, and the other SDL libraries in another. This tricks the pygame config scripts, but you can help it out by setting the environment LOCALBASE to a path prefix where the other libraries are. The common case for this is SDL installed in /usr and other SDL libs installed in /usr/local. The command for this situation is "LOCALBASE=/usr/local python setup.py install".


Mac OS X Compiling Info

    Make sure to have Numeric, PyObjC 1.3 or later, PIL, and the SDL frameworks installed. Compilation should work just same as the unix compiling instructions. Python 2.3 and Mac OS X 10.2 or later are required. To build an installer for Mac OS X that includes pygame, the examples, and the SDL frameworks use the following command: python setup.py bdist_mpkg
[[TableOfContents]]

= 概述 =

== 介绍 ==

pygame是主要构筑在SDL库基础上的一组Python模块的集合,它使我们能够用Python语言来创建功能完整的游戏和多媒体程序。pygame是高度括平台可移植的,在任何SDL支持的平台上都可以运行(几乎可以在任何平台和操作系统上运行)。

pygame是自由、免费的,以GNU LGPL 2.1协议发布,完整的协议可以在 http://www.gnu.org/copyleft/lesser.html 找到。这协议基本上就让你可以在你的任何项目中使用pygame。你可以用它来创建开源、自由、免费、共享或者商业游戏。但是如果你要增加和改变pygame本身的功能,你必须以一种LGPL兼容的协议来发布这种修改。

而examples子目录中的程序属于公共域软件。


== 安装 ==

pygame安装的简单方法是下载对应于你的系统的二进制pygame安装包。二进制安装包通常包括了依赖信息。从 http://www.pygame.org/download.shtml 可以找到你的系统和python版本对应的pygame安装包。

从源代码安装pygame也是相当自动的过程。主要的工作就是要安装pygame所依赖的软件包。一旦它所依赖的东西就绪,那么只要简单的运行setup.py就可以完成pygame的自动配置、编译和安装。

pygame非常强的依赖于SDL和Pygame。同时它也连接或者嵌入了几个其他的小程序库。font模块依赖于SDL_tff(它又依赖于freetype),mixer和mixer.music模块依赖于SDL_mixer,imag模块依赖于SDL_image(它又依赖于libjpeg和libpng),transform模块为了rotozoom函数而内嵌了SDL_rotozoom,surfarray模块需要python的Numeric包来提供多维数组。

在 http://www.pygame.org/install.html 有更多关于安装的信息。

== 文档 ==

作为起步,pygame有很多tutorial和介绍文章(在 http://www.pygame.org/wiki/tutorials 可以找到)。然后在examples目录中,有很多可以运行的小程序,可以从这些程序的代码开始动手。

而完整的pygame参考手册在 http://www.pygame.org/docs/index.html 可以找到。

如果需要帮助,可以在IRC的pygame频道找到可以提供帮助的人:irc.freenode.net 6667 #pygame

pygame维护了一个活跃的邮件列表。你可以发信到pygame-users@seul.org来发信到邮件列表。发送特定内容的信到majordomo@seul.org可以实现一些特别功能,比如订阅、取消订阅和获取帮助可以分别用如下内容(写在信的内容而不是标题中)的信件实现:
    * subscribe pygame-users
    * unsubscribe pygame-users
    * help

== 参考资料 ==

 * 官方主页 http://www.pygame.org/
 * pygame下载 http://www.pygame.org/download.shtml
 * pygame入门 http://www.pygame.org/wiki/tutorials
 * pygame文档 http://www.pygame.org/docs/
 * 获取pygame帮助 http://www.pygame.org/wiki/info
 * pygame游戏下载 http://www.pygame.org/projects/6

= 起步 =

== 初始化 ==
pygame的import和初始化是非常简单的。首先我们必须先导入(import)pygame这个包。

{{{#!python
import pygame
from pygame.locals import *
}}}
第一行是必须的,它导入了pygame包中所有可用的模块。第二行是可选的,它把一些常量和函数放在global名字空间中。
 
必需注意的是,有些pygame模块是可选的,比如font模块。当你import pygame时,pygame会检查font模块是否存在。如果font模块存在,那么它会自动被导入为pygame.font。如果它不存在,pygame.font会被设置成None。

在用pygame前,必须先初始化。一般只要一句话就可以完成:
{{{#!python
pygame.init()
}}}

执行这个语句,它会试着去初始化所有的pygame模块。并不是所有的模块都是需要初始化的,这个语句会自动去初始化那些需要初始化的模块。你也可以手动的去初始化各个模块,比如说要单独初始化font模块可以这样做:
{{{#!python
pygame.font.init()
}}}

注意,如果执行pygame.init()初始化有错误,它不会给出异常。如果手动初始化单个模块,错误会导致抛出异常。任何必须初始化的模块都包含一个get_init()函数,来判断这个模块是否已经被初始化。
 
可以对任何模块多次调用init()函数而不会出错。

已经初始化的模块通常会有一个quit()函数来完成清理工作。没有必要显式的调用这个函数,因为pygame会在python退出的时候自动清理所有已经初始化的模块。

== surface和screen ==

pygame最重要的部分就是surface。我们可以把surface看作是一张白纸。你可以对surface作很多操作,比如在surface上画线、用某种颜色填充surface上的部分区域、把图片拷贝到surface上去,把图片从surface上复制下来、设置或者读取surface上某个点的颜色。一个surface可以是任何大小,一个游戏可以有任意多surface。其中有一个surface是特别的,就是用pygame.display.set_mode()创建的display surface。它代表了screen屏幕,对它的任何操作会出现在用户的屏幕上。一个游戏只能有一个这样的surface,这是SDL的限制。

怎样创建surface?刚才提到,用pygame.display.set_mode()可以创建特殊的display surface。此外,还可以用image.load()创建一个包含图片的surface,还可以用font.render()创建一个包含文字的surface。你甚至可以用Surface()创建一个不包含任何东西的surface。

surface的大部分方法都不重要,只要学习其中的blit(), fill(), set_at()和get_at()就够用了。

display surface的初始化操作是这样的:
{{{#!python
screen = pygame.display.set_mode((1024, 768))
screen = pygame.display.set_mode((1024, 768), pygame.FULLSCREEN)
}}}
你可以用set_mode把原来窗口的游戏变成全屏。其它的俄显式模式(可以用|连接)有
 * DOUBLEBUF
        对于平滑的动画所必须
 * OPENGL
        让你可以用PyOpenGL,但是不能用pygame的绘图函数
还有一个可选的depth参数,用来控制颜色显示的深度。一般情况下不用指定这个参数,只要用默认值就可以了。

如果使用DOUBLEBUF,你需要用flip函数来把绘制的内容显示到屏幕上。
{{{
>>> pygame.display.flip()
}}}

== 画图 ==

接下来,我们在屏幕上画一幅图像。我们通过最重要的画图原语BLIT(BLock Image Transfer)来实现,它可以把图像从一个地方(比如源图像)拷贝到另一个地方(比如屏幕上的某个位置)。
{{{
>>> car = pygame.image.load('car.png')
>>> screen.blit(car, (50, 100))
>>> pygame.display.flip()
}}}
这时,图片car.png中的内容会显示在屏幕上,图片的左上角在屏幕上的坐标是(50, 100)。屏幕坐标的X轴从作往右的,Y轴是从上往下的。然后我们可以用如下语句来旋转图片:
{{{
>>> rotated = pygame.transform.rotate(car, 45)
>>> screen.blit(car, (50, 100))
>>> pygame.display.flip()
}}}

要让屏幕上的任何东西动起来,需要一个这样的过程:画一个场景,然后把它擦掉,然后再画一幅稍微不同的场景,然后再擦掉……如此反复。比如:
{{{#!python
for i in range(100):
    screen.fill((0, 0, 0))
    screen.blit(car, (i, 0))
}}}
注意:这里我们把整个屏幕清楚然后重新绘制不是很好的方法。最好是只把屏幕上变化的部分擦掉重画,其它部分不变。Sprite模块可以帮助我们做这个事情。


== 获取输入 ==
pygame有多种方法获得用户的输入事件,最常见的方法有:
{{{#!python
pygame.event.wait()
pygame.event.poll()
pygame.event.get()
}}}
wait函数会阻塞游戏的执行直到有用户事件发生。这对于游戏不是很有用,因为我们希望在等待输入的同时还要产生动画。poll函数会查看是否有事件等待处理,并返回等待处理的一个事件,如果没有事件它就返回NOEVENT。get函数和poll类似,只是它会返回所有在等待处理的事件,没有事件它会返回空列表。

这里还要提一下非常重要的时间控制,如果没有它你的游戏会在计算机上能跑多快就跑多快,这往往不是我们希望的。时间控制很容易:
{{{#!python
clock = pygame.time.Clock()
FRAMES_PER_SECOND = 30
deltat = clock.tick(FRAMES_PER_SECOND)
}}}
tick函数会暂停一定的时间,使得上次调用tick的时间到此次调用tick的时间满1/30秒。这样有效的限制了调用tick的次数只有每秒30次。两次tick调用之间的实际时间差由tick函数返回(以毫秒为单位),在比较慢的计算机上,可能不能达到每秒30次。

非常重要的是每秒30帧的速度也决定了游戏响应用户输入的速度,因为它只在每次画图的时候检查用户的输入。以慢于每秒30次的速度检测输入会使用户觉得明显的延时。以每秒30次的帧速显示是因为30帧以上的速度对于人眼是没有用的。如果你的游戏是动作型的,那么你可能要把帧速提高一倍,这样使用户觉得他们的输入能够被更快地得到响应。


== 结合在一起 ==
把前面所讲的各个部分结合在一起,我们可以构成一个完整pygame程序:
{{{#!python
# INTIALISATION
import pygame, math, sys
from pygame.locals import *
screen = pygame.display.set_mode((640, 480))
car = pygame.image.load('car.png')
clock = pygame.time.Clock()
k_up = k_down = k_left = k_right = 0
speed = direction = 0
position = (100, 100)
TURN_SPEED = 5
ACCELERATION = 2
MAX_FORWARD_SPEED = 10
MAX_REVERSE_SPEED = 5
BLACK = (0,0,0)
while 1:
    # USER INPUT
    clock.tick(30)
    for event in pygame.event.get():
        if not hasattr(event, 'key'): continue
        down = event.type == KEYDOWN # key down or up?
        if event.key == K_RIGHT: k_right = down * TURN_SPEED
        elif event.key == K_LEFT: k_left = down * TURN_SPEED
        elif event.key == K_UP: k_up = down * ACCELERATION
        elif event.key == K_DOWN: k_down = down * ACCELERATION
        elif event.key == K_ESCAPE: sys.exit(0) # quit the game
    screen.fill(BLACK)
    # SIMULATION
    # .. new speed and direction based on acceleration and turn
    speed += (k_up - k_down)
    if speed > MAX_FORWARD_SPEED: speed = MAX_FORWARD_SPEED
    if speed < MAX_REVERSE_SPEED: speed = MAX_REVERSE_SPEED
    direction += (k_left - k_right)
    # .. new position based on current position, speed and direction
    x, y = position
    rad = direction * math.pi / 180
    x += speed*math.sin(rad)
    y += speed*math.cos(rad)
    position = (x, y)
    # RENDERING
    # .. rotate the car image for direction
    rotated = pygame.transform.rotate(car, direction)
    # .. position the car on screen
    rect = rotated.get_rect()
    rect.center = position
    # .. render the car to screen
    screen.blit(rotated, rect)
    pygame.display.flip()

}}}

== sprite ==

上面的代码有点混乱,我们需要更好的组织一下,以更好的适应更大的游戏开发。为此我们需要使用sprite。一个sprite就是一个图像(比如说一辆车)和有关这个图像被显示在屏幕上什么地方的信息(即位置)。这些信息分别储存在sprite的image属性和rect属性里。

sprite总是被一组一组的处理而不是单个处理,甚至一组只有一个sprite也是这样。sprite组有一个叫做draw的方法,可以把这组里面的sprite画在给定的surface上。sprite组还有一个clear方法可以把sprite从surface上清除。上面的代码可以用sprite重写为:
{{{#!python
# INTIALISATION
import pygame, math, sys
from pygame.locals import *
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
class CarSprite(pygame.sprite.Sprite):
    MAX_FORWARD_SPEED = 10
    MAX_REVERSE_SPEED = 5
    ACCELERATION = 2
    TURN_SPEED = 5
    def __init__(self, image, position):
        pygame.sprite.Sprite.__init__(self)
        self.src_image = pygame.image.load(image)
        self.position = position
        self.speed = self.direction = 0
        self.k_left = self.k_right = self.k_down = self.k_up = 0
    def update(self, deltat):
        # SIMULATION
        self.speed += (self.k_up - self.k_down)
        if self.speed > self.MAX_FORWARD_SPEED:
            self.speed = self.MAX_FORWARD_SPEED
        if self.speed < self.MAX_REVERSE_SPEED:
            self.speed = self.MAX_REVERSE_SPEED
        self.direction += (self.k_left - self.k_right)
        x, y = self.position
        rad = self.direction * math.pi / 180
        x += self.speed*math.sin(rad)
        y += self.speed*math.cos(rad)
        self.position = (x, y)
        self.image = pygame.transform.rotate(self.src_image, self.direction)
        self.rect = self.image.get_rect()
        self.rect.center = self.position

# CREATE A CAR AND RUN
rect = screen.get_rect()
car = CarSprite('car.png', rect.center)
car_group = pygame.sprite.RenderPlain(car)
while 1:
    # USER INPUT
    deltat = clock.tick(30)
    for event in pygame.event.get():
        if not hasattr(event, 'key'): continue
        down = event.type == KEYDOWN
        if event.key == K_RIGHT: car.k_right = down * car.TURN_SPEED
        elif event.key == K_LEFT: car.k_left = down * car.TURN_SPEED
        elif event.key == K_UP: car.k_up = down * car.ACCELERATION
        elif event.key == K_DOWN: car.k_down = down * car.ACCELERATION
        elif event.key == K_ESCAPE: sys.exit(0)
    # RENDERING
    screen.fill((0,0,0))
    car_group.update(deltat)
    car_group.draw(screen)
    pygame.display.flip()
}}}

这里我们并没有发现sprite带来的多大好处,sprite的真正好处是在屏幕上有很多很多图像要画的时候。sprite有一些功能可以帮助我们来检测碰撞,使得碰撞检测变得容易。Let's put some pads to drive over into the simulation:
{{{#!python
class PadSprite(pygame.sprite.Sprite):
    normal = pygame.image.load('pad_normal.png')
    hit = pygame.image.load('pad_hit.png')
    def __init__(self, position):
        pygame.sprite.Sprite.__init__(self)
        self.rect = pygame.Rect(self.normal.get_rect())
        self.rect.center = position
    def update(self, hit_list):
        if self in hit_list: self.image = self.hit
        else: self.image = self.normal
pads = [
    PadSprite((100, 100)),
    PadSprite((400, 100)),
    PadSprite((100, 300)),
    PadSprite((400, 300)),
]
pad_group = pygame.sprite.RenderPlain(*pads)
}}}
现在,当进行动画的时候,在画车之前,我们检查车的sprite是否和任意一个pad的sprite相撞了,并把这个信息传给pad.update(),使得每个pad知道自己是否被撞到(hit)了:
{{{#!python
collisions = pygame.sprite.spritecollide(car, pad_group, False)
pad_group.update(collisions)
pad_group.draw(screen)
}}}
现在,我们有一个可以由玩家控制在屏幕上移动的汽车,并且能够检测汽车是否碰到了屏幕上的其他东西。


== 背景 ==
接下来我们给汽车构造一个赛道。
It'd be nice if we could determine whether the car has made a "lap" of the "circuit" we've constructed. We'll
keep information indicating which order the pads must be visited:
{{{#!python
class PadSprite(pygame.sprite.Sprite):
    normal = pygame.image.load('pad_normal.png')
    hit = pygame.image.load('pad_hit.png')
    def __init__(self, number, position):
        pygame.sprite.Sprite.__init__(self)
        self.number = number
        self.rect = pygame.Rect(self.normal.get_rect())
        self.rect.center = position
        self.image = self.normal
pads = [
    PadSprite(1, (100, 100)),
    PadSprite(2, (400, 100)),
    PadSprite(3, (400, 300)),
    PadSprite(4, (100, 300)),
]
current_pad_number = 0
pad_group = pygame.sprite.RenderPlain(*pads)
}}}
我们把检测碰撞代码改成这样(用来保证汽车与pad碰撞的正确的顺序):
{{{#!python
pads = pygame.sprite.spritecollide(car, pad_group, False)
if pads:
    pad = pads[0]
    if pad.number == current_pad_number + 1:
        pad.image = pad.hit
        current_pad_number += 1
elif current_pad_number == 4:
    for pad in pad_group.sprites():
        pad.image = pad.normal
    current_pad_number = 0
pad_group.draw(screen)
}}}

现在我们在画每一帧之前都把整个屏幕清空screen.fill((0,0,0))。这是相当慢的,不过这也是容易改进的。首先我们载入一张背景图片,把它画在屏幕上。
{{{#!python
background = pygame.image.load('track.png')
screen.blit(background, (0,0))
}}}
然后在循环中,在update之前,我们把汽车sprite从屏幕上清除掉,也把pad清除掉。
{{{#!python
pad_group.clear(screen, background)
car_group.clear(screen, background)
}}}
现在我们每画一帧只用更新屏幕的一小部分。还可以更进一步的优化改进,因为pad很少情况下才需要更新,我们不需要每一帧都清除然后重画它们,除非他们的状态确实改变了。这个优化暂时不需要,一个简单的规则就是除非真的有必要,否则不需要优化,没有必要的优化只会增加代码的复杂度。


= End =

TableOfContents

概述

1. 介绍

pygame是主要构筑在SDL库基础上的一组Python模块的集合,它使我们能够用Python语言来创建功能完整的游戏和多媒体程序。pygame是高度括平台可移植的,在任何SDL支持的平台上都可以运行(几乎可以在任何平台和操作系统上运行)。

pygame是自由、免费的,以GNU LGPL 2.1协议发布,完整的协议可以在 http://www.gnu.org/copyleft/lesser.html 找到。这协议基本上就让你可以在你的任何项目中使用pygame。你可以用它来创建开源、自由、免费、共享或者商业游戏。但是如果你要增加和改变pygame本身的功能,你必须以一种LGPL兼容的协议来发布这种修改。

而examples子目录中的程序属于公共域软件。

2. 安装

pygame安装的简单方法是下载对应于你的系统的二进制pygame安装包。二进制安装包通常包括了依赖信息。从 http://www.pygame.org/download.shtml 可以找到你的系统和python版本对应的pygame安装包。

从源代码安装pygame也是相当自动的过程。主要的工作就是要安装pygame所依赖的软件包。一旦它所依赖的东西就绪,那么只要简单的运行setup.py就可以完成pygame的自动配置、编译和安装。

pygame非常强的依赖于SDL和Pygame。同时它也连接或者嵌入了几个其他的小程序库。font模块依赖于SDL_tff(它又依赖于freetype),mixer和mixer.music模块依赖于SDL_mixer,imag模块依赖于SDL_image(它又依赖于libjpeg和libpng),transform模块为了rotozoom函数而内嵌了SDL_rotozoom,surfarray模块需要python的Numeric包来提供多维数组。

http://www.pygame.org/install.html 有更多关于安装的信息。

3. 文档

作为起步,pygame有很多tutorial和介绍文章(在 http://www.pygame.org/wiki/tutorials 可以找到)。然后在examples目录中,有很多可以运行的小程序,可以从这些程序的代码开始动手。

而完整的pygame参考手册在 http://www.pygame.org/docs/index.html 可以找到。

如果需要帮助,可以在IRC的pygame频道找到可以提供帮助的人:irc.freenode.net 6667 #pygame

pygame维护了一个活跃的邮件列表。你可以发信到pygame-users@seul.org来发信到邮件列表发送特定内容的信到majordomo@seul.org可以实现一些特别功能,比如订阅、取消订阅和获取帮助可以分别用如下内容(写在信的内容而不是标题中)的信件实现:

  • subscribe pygame-users
  • unsubscribe pygame-users
  • help

4. 参考资料

起步

1. 初始化

pygame的import和初始化是非常简单的。首先我们必须先导入(import)pygame这个包。

   1 import pygame
   2 from pygame.locals import *

第一行是必须的,它导入了pygame包中所有可用的模块。第二行是可选的,它把一些常量和函数放在global名字空间中。

必需注意的是,有些pygame模块是可选的,比如font模块。当你import pygame时,pygame会检查font模块是否存在。如果font模块存在,那么它会自动被导入为pygame.font。如果它不存在,pygame.font会被设置成None。

在用pygame前,必须先初始化。一般只要一句话就可以完成:

   1 pygame.init()

执行这个语句,它会试着去初始化所有的pygame模块。并不是所有的模块都是需要初始化的,这个语句会自动去初始化那些需要初始化的模块。你也可以手动的去初始化各个模块,比如说要单独初始化font模块可以这样做:

   1 pygame.font.init()

注意,如果执行pygame.init()初始化有错误,它不会给出异常。如果手动初始化单个模块,错误会导致抛出异常。任何必须初始化的模块都包含一个get_init()函数,来判断这个模块是否已经被初始化。

可以对任何模块多次调用init()函数而不会出错。

已经初始化的模块通常会有一个quit()函数来完成清理工作。没有必要显式的调用这个函数,因为pygame会在python退出的时候自动清理所有已经初始化的模块。

2. surface和screen

pygame最重要的部分就是surface。我们可以把surface看作是一张白纸。你可以对surface作很多操作,比如在surface上画线、用某种颜色填充surface上的部分区域、把图片拷贝到surface上去,把图片从surface上复制下来、设置或者读取surface上某个点的颜色。一个surface可以是任何大小,一个游戏可以有任意多surface。其中有一个surface是特别的,就是用pygame.display.set_mode()创建的display surface。它代表了screen屏幕,对它的任何操作会出现在用户的屏幕上。一个游戏只能有一个这样的surface,这是SDL的限制。

怎样创建surface?刚才提到,用pygame.display.set_mode()可以创建特殊的display surface。此外,还可以用image.load()创建一个包含图片的surface,还可以用font.render()创建一个包含文字的surface。你甚至可以用Surface()创建一个不包含任何东西的surface。

surface的大部分方法都不重要,只要学习其中的blit(), fill(), set_at()和get_at()就够用了。

display surface的初始化操作是这样的:

   1 screen = pygame.display.set_mode((1024, 768))
   2 screen = pygame.display.set_mode((1024, 768), pygame.FULLSCREEN)

你可以用set_mode把原来窗口的游戏变成全屏。其它的俄显式模式(可以用|连接)有

  • DOUBLEBUF
    • 对于平滑的动画所必须
  • OPENGL
    • 让你可以用PyOpenGL,但是不能用pygame的绘图函数

还有一个可选的depth参数,用来控制颜色显示的深度。一般情况下不用指定这个参数,只要用默认值就可以了。

如果使用DOUBLEBUF,你需要用flip函数来把绘制的内容显示到屏幕上。

>>> pygame.display.flip()

3. 画图

接下来,我们在屏幕上画一幅图像。我们通过最重要的画图原语BLIT(BLock Image Transfer)来实现,它可以把图像从一个地方(比如源图像)拷贝到另一个地方(比如屏幕上的某个位置)。

>>> car = pygame.image.load('car.png')
>>> screen.blit(car, (50, 100))
>>> pygame.display.flip()

这时,图片car.png中的内容会显示在屏幕上,图片的左上角在屏幕上的坐标是(50, 100)。屏幕坐标的X轴从作往右的,Y轴是从上往下的。然后我们可以用如下语句来旋转图片:

>>> rotated = pygame.transform.rotate(car, 45)
>>> screen.blit(car, (50, 100))
>>> pygame.display.flip()

要让屏幕上的任何东西动起来,需要一个这样的过程:画一个场景,然后把它擦掉,然后再画一幅稍微不同的场景,然后再擦掉……如此反复。比如:

   1 for i in range(100):
   2     screen.fill((0, 0, 0))
   3     screen.blit(car, (i, 0))

注意:这里我们把整个屏幕清楚然后重新绘制不是很好的方法。最好是只把屏幕上变化的部分擦掉重画,其它部分不变。Sprite模块可以帮助我们做这个事情。

4. 获取输入

pygame有多种方法获得用户的输入事件,最常见的方法有:

   1 pygame.event.wait()
   2 pygame.event.poll()
   3 pygame.event.get()

wait函数会阻塞游戏的执行直到有用户事件发生。这对于游戏不是很有用,因为我们希望在等待输入的同时还要产生动画。poll函数会查看是否有事件等待处理,并返回等待处理的一个事件,如果没有事件它就返回NOEVENT。get函数和poll类似,只是它会返回所有在等待处理的事件,没有事件它会返回空列表。

这里还要提一下非常重要的时间控制,如果没有它你的游戏会在计算机上能跑多快就跑多快,这往往不是我们希望的。时间控制很容易:

   1 clock = pygame.time.Clock()
   2 FRAMES_PER_SECOND = 30
   3 deltat = clock.tick(FRAMES_PER_SECOND)

tick函数会暂停一定的时间,使得上次调用tick的时间到此次调用tick的时间满1/30秒。这样有效的限制了调用tick的次数只有每秒30次。两次tick调用之间的实际时间差由tick函数返回(以毫秒为单位),在比较慢的计算机上,可能不能达到每秒30次。

非常重要的是每秒30帧的速度也决定了游戏响应用户输入的速度,因为它只在每次画图的时候检查用户的输入。以慢于每秒30次的速度检测输入会使用户觉得明显的延时。以每秒30次的帧速显示是因为30帧以上的速度对于人眼是没有用的。如果你的游戏是动作型的,那么你可能要把帧速提高一倍,这样使用户觉得他们的输入能够被更快地得到响应。

5. 结合在一起

把前面所讲的各个部分结合在一起,我们可以构成一个完整pygame程序:

   1 # INTIALISATION
   2 import pygame, math, sys
   3 from pygame.locals import *
   4 screen = pygame.display.set_mode((640, 480))
   5 car = pygame.image.load('car.png')
   6 clock = pygame.time.Clock()
   7 k_up = k_down = k_left = k_right = 0
   8 speed = direction = 0
   9 position = (100, 100)
  10 TURN_SPEED = 5
  11 ACCELERATION = 2
  12 MAX_FORWARD_SPEED = 10
  13 MAX_REVERSE_SPEED = 5
  14 BLACK = (0,0,0)
  15 while 1:
  16     # USER INPUT
  17     clock.tick(30)
  18     for event in pygame.event.get():
  19         if not hasattr(event, 'key'): continue
  20         down = event.type == KEYDOWN     # key down or up?
  21         if event.key == K_RIGHT: k_right = down * TURN_SPEED
  22         elif event.key == K_LEFT: k_left = down * TURN_SPEED
  23         elif event.key == K_UP: k_up = down * ACCELERATION
  24         elif event.key == K_DOWN: k_down = down * ACCELERATION
  25         elif event.key == K_ESCAPE: sys.exit(0)     # quit the game
  26     screen.fill(BLACK)
  27     # SIMULATION
  28     # .. new speed and direction based on acceleration and turn
  29     speed += (k_up - k_down)
  30     if speed > MAX_FORWARD_SPEED: speed = MAX_FORWARD_SPEED
  31     if speed < MAX_REVERSE_SPEED: speed = MAX_REVERSE_SPEED
  32     direction += (k_left - k_right)
  33     # .. new position based on current position, speed and direction
  34     x, y = position
  35     rad = direction * math.pi / 180
  36     x += speed*math.sin(rad)
  37     y += speed*math.cos(rad)
  38     position = (x, y)
  39     # RENDERING
  40     # .. rotate the car image for direction
  41     rotated = pygame.transform.rotate(car, direction)
  42     # .. position the car on screen
  43     rect = rotated.get_rect()
  44     rect.center = position
  45     # .. render the car to screen
  46     screen.blit(rotated, rect)
  47     pygame.display.flip()

6. sprite

上面的代码有点混乱,我们需要更好的组织一下,以更好的适应更大的游戏开发。为此我们需要使用sprite。一个sprite就是一个图像(比如说一辆车)和有关这个图像被显示在屏幕上什么地方的信息(即位置)。这些信息分别储存在sprite的image属性和rect属性里。

sprite总是被一组一组的处理而不是单个处理,甚至一组只有一个sprite也是这样。sprite组有一个叫做draw的方法,可以把这组里面的sprite画在给定的surface上。sprite组还有一个clear方法可以把sprite从surface上清除。上面的代码可以用sprite重写为:

   1 # INTIALISATION
   2 import pygame, math, sys
   3 from pygame.locals import *
   4 screen = pygame.display.set_mode((640, 480))
   5 clock = pygame.time.Clock()
   6 class CarSprite(pygame.sprite.Sprite):
   7     MAX_FORWARD_SPEED = 10
   8     MAX_REVERSE_SPEED = 5
   9     ACCELERATION = 2
  10     TURN_SPEED = 5
  11     def __init__(self, image, position):
  12         pygame.sprite.Sprite.__init__(self)
  13         self.src_image = pygame.image.load(image)
  14         self.position = position
  15         self.speed = self.direction = 0
  16         self.k_left = self.k_right = self.k_down = self.k_up = 0
  17     def update(self, deltat):
  18         # SIMULATION
  19         self.speed += (self.k_up - self.k_down)
  20         if self.speed > self.MAX_FORWARD_SPEED:
  21             self.speed = self.MAX_FORWARD_SPEED
  22         if self.speed < self.MAX_REVERSE_SPEED:
  23             self.speed = self.MAX_REVERSE_SPEED
  24         self.direction += (self.k_left - self.k_right)
  25         x, y = self.position
  26         rad = self.direction * math.pi / 180
  27         x += self.speed*math.sin(rad)
  28         y += self.speed*math.cos(rad)
  29         self.position = (x, y)
  30         self.image = pygame.transform.rotate(self.src_image, self.direction)
  31         self.rect = self.image.get_rect()
  32         self.rect.center = self.position
  33 
  34 # CREATE A CAR AND RUN
  35 rect = screen.get_rect()
  36 car = CarSprite('car.png', rect.center)
  37 car_group = pygame.sprite.RenderPlain(car)
  38 while 1:
  39     # USER INPUT
  40     deltat = clock.tick(30)
  41     for event in pygame.event.get():
  42         if not hasattr(event, 'key'): continue
  43         down = event.type == KEYDOWN
  44         if event.key == K_RIGHT: car.k_right = down * car.TURN_SPEED
  45         elif event.key == K_LEFT: car.k_left = down * car.TURN_SPEED
  46         elif event.key == K_UP: car.k_up = down * car.ACCELERATION
  47         elif event.key == K_DOWN: car.k_down = down * car.ACCELERATION
  48         elif event.key == K_ESCAPE: sys.exit(0)
  49     # RENDERING
  50     screen.fill((0,0,0))
  51     car_group.update(deltat)
  52     car_group.draw(screen)
  53     pygame.display.flip()

这里我们并没有发现sprite带来的多大好处,sprite的真正好处是在屏幕上有很多很多图像要画的时候。sprite有一些功能可以帮助我们来检测碰撞,使得碰撞检测变得容易。Let's put some pads to drive over into the simulation:

   1 class PadSprite(pygame.sprite.Sprite):
   2     normal = pygame.image.load('pad_normal.png')
   3     hit = pygame.image.load('pad_hit.png')
   4     def __init__(self, position):
   5         pygame.sprite.Sprite.__init__(self)
   6         self.rect = pygame.Rect(self.normal.get_rect())
   7         self.rect.center = position
   8     def update(self, hit_list):
   9         if self in hit_list: self.image = self.hit
  10         else: self.image = self.normal
  11 pads = [
  12     PadSprite((100, 100)),
  13     PadSprite((400, 100)),
  14     PadSprite((100, 300)),
  15     PadSprite((400, 300)),
  16 ]
  17 pad_group = pygame.sprite.RenderPlain(*pads)

现在,当进行动画的时候,在画车之前,我们检查车的sprite是否和任意一个pad的sprite相撞了,并把这个信息传给pad.update(),使得每个pad知道自己是否被撞到(hit)了:

   1 collisions = pygame.sprite.spritecollide(car, pad_group, False)
   2 pad_group.update(collisions)
   3 pad_group.draw(screen)

现在,我们有一个可以由玩家控制在屏幕上移动的汽车,并且能够检测汽车是否碰到了屏幕上的其他东西。

7. 背景

接下来我们给汽车构造一个赛道。 It'd be nice if we could determine whether the car has made a "lap" of the "circuit" we've constructed. We'll keep information indicating which order the pads must be visited:

   1 class PadSprite(pygame.sprite.Sprite):
   2     normal = pygame.image.load('pad_normal.png')
   3     hit = pygame.image.load('pad_hit.png')
   4     def __init__(self, number, position):
   5         pygame.sprite.Sprite.__init__(self)
   6         self.number = number
   7         self.rect = pygame.Rect(self.normal.get_rect())
   8         self.rect.center = position
   9         self.image = self.normal
  10 pads = [
  11     PadSprite(1, (100, 100)),
  12     PadSprite(2, (400, 100)),
  13     PadSprite(3, (400, 300)),
  14     PadSprite(4, (100, 300)),
  15 ]
  16 current_pad_number = 0
  17 pad_group = pygame.sprite.RenderPlain(*pads)

我们把检测碰撞代码改成这样(用来保证汽车与pad碰撞的正确的顺序):

   1 pads = pygame.sprite.spritecollide(car, pad_group, False)
   2 if pads:
   3     pad = pads[0]
   4     if pad.number == current_pad_number + 1:
   5         pad.image = pad.hit
   6         current_pad_number += 1
   7 elif current_pad_number == 4:
   8     for pad in pad_group.sprites(): 
   9         pad.image = pad.normal
  10     current_pad_number = 0
  11 pad_group.draw(screen)

现在我们在画每一帧之前都把整个屏幕清空screen.fill((0,0,0))。这是相当慢的,不过这也是容易改进的。首先我们载入一张背景图片,把它画在屏幕上。

   1 background = pygame.image.load('track.png')
   2 screen.blit(background, (0,0))

然后在循环中,在update之前,我们把汽车sprite从屏幕上清除掉,也把pad清除掉。

   1 pad_group.clear(screen, background)
   2 car_group.clear(screen, background)

现在我们每画一帧只用更新屏幕的一小部分。还可以更进一步的优化改进,因为pad很少情况下才需要更新,我们不需要每一帧都清除然后重画它们,除非他们的状态确实改变了。这个优化暂时不需要,一个简单的规则就是除非真的有必要,否则不需要优化,没有必要的优化只会增加代码的复杂度。

End

Pygame起步 (2010-01-29 16:48:28由s235-200编辑)

ch3n2k.com | Copyright (c) 2004-2020 czk.