安装Pygame
Ubuntu下安装Pygame:
1
|
sudo apt-get install python-pygame
|
装好后,查看Pygame版本:
1
2
|
>>> import pygame
>>> print pygame.ver
|
使用Pygame
Pygame有很多模块。每一个设备都有一个对应的模块。Pygame模块一览:
模块名 |
功能 |
pygame.cdrom |
访问和控制光驱 |
pygame.cursors |
加载光标图片 |
pygame.display |
访问显示设备 |
pygame.draw |
绘制形状、线和点 |
pygame.event |
管理事件 |
pygame.font |
使用字体 |
pygame.image |
加载和存储图片 |
pygame.joystick |
使用游戏手柄或者类似设备 |
pygame.key |
读取键盘按键 |
pygame.mixer |
加载和播放声音 |
pygame.mouse |
管理鼠标 |
pygame.movie |
播放视频 |
pygame.music |
处理音乐和音频流 |
pygame.overlay |
访问高级视频叠加 |
pygame |
包含高层Pygame函数 |
pygame.rect |
管理矩形区域 |
pygame.sndarray |
操作声音数据 |
pygame.sprite |
操作移动图像 |
pygame.surface |
管理图像和屏幕 |
pygame.surfarray |
管理点阵图像数据 |
pygame.time |
管理时间和帧信息 |
pygame.transform |
缩放和移动图像 |
有些模块可能在某些平台上不存在,比如游戏运行的硬件驱动没有安装,这种情况下,Pygame将设置这个模块为None,可以使用None来测试。下面这段代码检测pygame.font
是否可用:
1
2
3
|
if pygame.font is None:
print 'The font module is not available!'
exit()
|
重温Hello World
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
background_image_filename = 'sushiplate.jpg'
mouse_image_filename = 'fugu.png'
import pygame
from pygame.locals import *
from sys import exit
pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
pygame.display.set_caption('Hello, World!')
background = pygame.image.load(background_image_filename).convert()
mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.blit(background, (0, 0))
x, y = pygame.mouse.get_pos()
x -= mouse_cursor.get_width() / 2
y -= mouse_cursor.get_height() / 2
screen.blit(mouse_cursor, (x, y))
pygame.display.update()
|
第一行导入pygame包,使我们可以访问它的所有子模块。第二行导入一些常用函数和常量,这个不是必须的,但是更方便。
pygame.init()非常简单,实际上却做了非常多工作。它初始化每一个Pygame的子模块。可以单独初始化某一个模块,比如pygame.sound.init()。
pygame.display.set_mode返回一个Surface对象,代表桌面窗口。第一个参数为元祖,代表分辨率(必须)。第二个是一个标志位,具体意思见下表,如果不用什么特性,就指定0。第三个为色深,如果未提供或设置为0,Pygame将使用当前桌面的值。
标志位 |
用途 |
FULLSCREEN |
创建一个全屏窗口 |
DOUBLEBUF |
创建一个“双缓冲”窗口,建议在HWSURFACE或者OPENGL时使用 |
HWSURFACE |
创建一个硬件加速的窗口,必须和FULLSCREEN同时使用 |
OPENGL |
创建一个OpenGL渲染的窗口 |
RESIZABLE |
创建一个可变大小的窗口 |
NOFRAME |
创建一个没有边框的窗口 |
色深 |
颜色个数 |
8 bits |
256种颜色 |
15 bits |
32768种颜色,空闲一位 |
16 bits |
65536种颜色 |
24 bits |
16.7百万种颜色 |
32 bits |
16.7百万种颜色,空闲8位 |
load函数读取一个文件并返回一个包含图片数据的Surface对象,直到画出来之前是不可见的。
convert函数是将图像数据转化为Surface对象,每次加载完图像以后就应该做这件事件(事实上因为它太常用了,如果你不写Pygame也会帮你做);
convert_alpha相比convert,保留了Alpha通道信息(可以简单理解为透明的部分),这样我们的光标才可以是不规则的形状。
游戏的主循环是一个无限循环,直到用户跳出。在这个主循环里做的事情就是不停地画背景和更新光标位置,虽然背景是不动的,我们还是需要每次都画它,否则鼠标覆盖过的位置就不能恢复正常了。
画完以后一定要update更新一下,否则画面一片漆黑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
init(...)
pygame.init(): return (numpass, numfail)
initialize all imported pygame modules
set_mode(...)
pygame.display.set_mode(resolution=(0,0), flags=0, depth=0): return Surface
initialize a window or screen for display
set_caption(...)
pygame.display.set_caption(title, icontitle=None): return None
set the current window caption
load(...)
pygame.image.load(filename): return Surface
pygame.image.load(fileobj, namehint=): return Surface
load new image from a file
convert(...)
Surface.convert(Surface): return Surface
Surface.convert(depth, flags=0): return Surface
Surface.convert(masks, flags=0): return Surface
Surface.convert(): return Surface
change the pixel format of an image
convert_alpha(...)
Surface.convert_alpha(Surface): return Surface
Surface.convert_alpha(): return Surface
change the pixel format of an image including per pixel alphas
get(...)
pygame.event.get(): return Eventlist
pygame.event.get(type): return Eventlist
pygame.event.get(typelist): return Eventlist
get events from the queue
blit(...)
Surface.blit(source, dest, area=None, special_flags = 0): return Rect
draw one image onto another
get_pos(...)
pygame.mouse.get_pos(): return (x, y)
get the mouse cursor position
get_width(...)
Surface.get_width(): return width
get the width of the Surface
get_height(...)
Surface.get_height(): return height
get the height of the Surface
update(...)
pygame.display.update(rectangle=None): return None
pygame.display.update(rectangle_list): return None
update portions of the screen for software displays
|
理解事件
事件可以在任何时候产生,不管程序当前在做什么。因为你不能对发生的事件立刻做出反应,Pygame将事件存入一个队列,逐个处理。
事件检索
上个程序中,使用pygame.event.get()来处理所有的事件,这就像打开大门让所有的人进入。如果我们使用pygame.event.wait(),Pygame就会等到一个事件发生才继续下去,就好像等在门口,直到有人来。这个函数不太常用,因为它会挂起程序直到有事情发生;而另外一个方法**pygame.event.poll()**就好一些,一旦调用,它会根据现在的情形返回一个真实的事件,否则返回一个类型为NOEVENT的假事件。
每隔一段固定的时间调用事件处理函数很有必要,这样Pygame才能在内部处理事件。如果不使用任何事件处理函数,也可以调用**pygame.event.pump()**替代事件循环。
事件对象包含一些描述事件发生的成员变量。所有事件对象都有一个事件类型。
事件 |
产生途径 |
参数 |
QUIT |
用户点击关闭按钮 |
none |
ACTIVEEVENT |
Pygame被激活或隐藏 |
gain,state |
KEYDOWN |
键盘按下 |
unicode,key,mod |
KEYUP |
键盘松开 |
key,mod |
MOUSEMOTION |
鼠标移动 |
pos,rel,buttons |
MOUSEBUTTONDOWN |
鼠标按下 |
pos,button |
MOUSEBUTTONUP |
鼠标松开 |
pos,button |
JOYAXISMOTION |
游戏手柄(Joystick or pad)移动 |
joy,axis,value |
JOYBALLMOTION |
游戏球(Joy ball)移动 |
joy,ball,rel |
JOYHATMOTION |
游戏手柄(Joystick)移动 |
joy,hat,value |
JOYBUTTONDOWN |
游戏手柄按下 |
joy,button |
JOYBUTTONUP |
游戏手柄放开 |
joy,button |
VIDEORESIZE |
Pygame窗口缩放 |
size,w,h |
VIDEOEXPOSE |
部分或所有Pygame窗口暴露 |
none |
USEREVENT |
触发一个用户事件 |
code |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
import pygame
from pygame.locals import *
from sys import exit
pygame.init()
SCREEN_SIZE = (800, 600)
screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
font = pygame.font.SysFont('arial', 16)
font_height = font.get_linesize()
event_text = []
while True:
event = pygame.event.wait()
event_text.append(str(event))
event_text = event_text[-SCREEN_SIZE[1]/font_height:]
if event.type == QUIT:
exit()
screen.fill((255, 255, 255))
y = SCREEN_SIZE[1] - font_height
for text in reversed(event_text):
screen.blit(font.render(text, True, (0, 0, 0)), (0, y))
y -= font_height
pygame.display.update()
|
pygame.font.SysFont返回一个pygame.font.Font对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
SysFont(name, size, bold=False, italic=False)
pygame.font.SysFont(name, size, bold=False, italic=False) -> Font
create a pygame Font from system font resources
This will search the system fonts for the given font
name. You can also enable bold or italic styles, and
the appropriate system font will be selected if available.
This will always return a valid Font object, and will
fallback on the builtin pygame font if the given font
is not found.
Name can also be a comma separated list of names, in
which case set of names will be searched in order. Pygame
uses a small set of common font aliases, if the specific
font you ask for is not available, a reasonable alternative
may be used.
get_linesize(...)
Font.get_linesize(): return int
get the line space of the font text
wait(...)
pygame.event.wait(): return Event
wait for a single event from the queue
render(...)
Font.render(text, antialias, color, background=None): return Surface
draw text on a new Surface
fill(...)
Surface.fill(color, rect=None, special_flags=0): return Rect
fill Surface with a solid color
pump(...)
pygame.event.pump(): return None
internally process pygame event handlers
|
处理鼠标移动事件
当鼠标移动时,MOUSEMOTION事件发生。包含下面三个值:
- buttons-一个对应鼠标按钮的元组。buttons[0]是鼠标左按钮,buttons[1]是鼠标中间按钮,buttons[2]是鼠标右按钮。如果按钮被按下,则值为1,反之为0。多个按钮可以同时按下。
- pos-一个元组,包含事件发生时鼠标所在位置。
- rel-一个元祖,包含现在距离上次产生鼠标事件时的距离。
处理鼠标按钮事件
除了鼠标移动事件,鼠标还能产生MOUSEBUTTONDOWN和MOUSEBUTTONUP事件。包含下面2个值:
- button-被按下的按钮的数字。1为鼠标左按钮,2为鼠标中间按钮,3为鼠标右按钮。
- pos-一个元组,包含事件发生时鼠标所在位置。
处理键盘事件
键盘和游戏手柄的事件类似。当一个键被按下KEYDOWN事件发生。当一个键松开KEYUP事件发生。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
bg_file = 'sushiplate.jpg'
import pygame
from pygame.locals import *
from sys import exit
pygame.init()
screen = pygame.display.set_mode((640, 480), FULLSCREEN, 32)
background = pygame.image.load(bg_file).convert()
x, y = 0, 0
move_x, move_y = 0, 0
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
if event.type == KEYDOWN:
if event.key == K_LEFT:
move_x = -1
elif event.key == K_RIGHT:
move_x = 1
elif event.key == K_UP:
move_y = -1
elif event.key == K_DOWN:
move_y = 1
elif event.type == KEYUP:
if event.key == K_LEFT or event.key == K_RIGHT:
move_x = 0
elif event.key == K_UP or event.key == K_DOWN:
move_y = 0
x += move_x
y += move_y
screen.fill((0, 0, 0))
screen.blit(background, (x, y))
pygame.display.update()
|
KEYDOWN和KEYUP事件包含下面三个值:
- key-这是一个代表按下或松开的键值的数字。每一个键盘上的物理按钮都有一个以K_开头的常量。字母键为K_a到K_z,其它的如K_SPACE和K_RETURN。
- mod-这个值代表和key组合使用的其它键,比如Shift,Alt和Ctrl。每一个组合键以KMOD_开头,比如KMOD_SHIFT,KMOD_ALT和KMOD_CTRL。如果mod & KMOD_CTRL为真,则表示按下了Ctrl键。
- unicode-这个是被按下的键的Unicode值。每一个符号都有一个Unicode值与它对应。
过滤事件
一个游戏不是所有的事件都需要处理,而且通常存在其它方式获取某个事件可能提供的信息。比如,使用pygame.mouse.get_pos()就不需要响应MOUSEMOTION事件了。
使用pygame.event.set_blocked函数可以屏蔽事件,阻止事件进入事件队列。比如:
1
2
3
4
5
6
7
8
9
|
pygame.event.set_blocked(MOUSEMOTION)
pygame.event.set_blocked([KEYDOWN, KEYUP])
pygame.event.set_blocked(None)
set_blocked(...)
pygame.event.set_blocked(type): return None
pygame.event.set_blocked(typelist): return None
pygame.event.set_blocked(None): return None
control which events are allowed on the queue
|
与之相对的,pygame.event.set_allowed设定允许的事件。
1
2
3
4
5
|
set_allowed(...)
pygame.event.set_allowed(type): return None
pygame.event.set_allowed(typelist): return None
pygame.event.set_allowed(None): return None
control which events are allowed on the queue
|
pygame.event.get_blocked可以查询一个事件是否被屏蔽。
1
2
3
|
get_blocked(...)
pygame.event.get_blocked(type): return bool
test if a type of event is blocked from the queue
|
产生事件
通常Pygame为你产生相应的事件,但是你也可以产生自己的事件。为了产生一个事件,必须首先使用pygame.event.Event创建一个事件对象,然后使用pygame.event.post发送到事件队列尾端。
1
2
|
my_event = pygame.event.Event(KEYDOWN, key=K_SPACE, mod=0, unicode=u' ')
pgame.event.post(my_event)
|
事件构造函数接收事件类型和事件值参数。
1
2
3
4
5
6
7
8
|
Event(...)
pygame.event.Event(type, dict): return Event
pygame.event.Event(type, **attributes): return Event
create a new event object
post(...)
pygame.event.post(Event): return None
place a new event on the queue
|
除了模拟Pygame产生的事件,也可以创建新的事件。你只需使用一个大于USEREVENT的值作为事件的值。
1
2
3
4
5
6
7
|
CATONKEYBOARD = USEREVENT + 1
my_event = pygame.event.Event(CATONKEYBOARD, message="Bad cat!")
pgame.event.post(my_event)
for event in pygame.event.get():
if event.type == CATONKEYBOARD:
print event.message
|
打开一个显示
全屏显示
pygame.display.set_mode第二个参数设置为FULLSCREEN,就能得到一个全屏窗口了。
1
|
screen = pygame.display.set_mode((640, 480), FULLSCREEN, 32)
|
注意
如果在全屏模式下出问题,有时候非常难回到桌面。因此进入全屏模式前,需要先在窗口模式下测试。同时提供一个退出程序方法,因为全屏模式下关闭按钮看不到。
当进入全屏时,你的显卡可能会切换到不同的显示模式,这将改变显示的宽度,高度和一次显示颜色的数量。显卡只支持几种大小和颜色数量的组合。如果显示的大小不支持,Pygame将选择下一个大小,并居中显示。pygame.display.list_modes可以查看显卡支持的分辨率。
1
2
3
4
5
6
7
|
>>> import pygame
>>> pygame.init()
>>> pygame.display.list_modes()
list_modes(...)
pygame.display.list_modes(depth=0, flags=pygame.FULLSCREEN): return list
get list of available fullscreen modes
|
如果显卡不支持你想要的颜色数量,Pygame将自动转换以适应当前显示设备。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
background_image_filename = 'sushiplate.jpg'
import pygame
from pygame.locals import *
from sys import exit
pygame.init()
screen = pygame.display.set_mode((640, 480), 0, 32)
background = pygame.image.load(background_image_filename).convert()
Fullscreen = False
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
if event.type == KEYDOWN:
if event.key == K_f:
Fullscreen = not Fullscreen
if Fullscreen:
screen = pygame.display.set_mode((640, 480), FULLSCREEN, 32)
else:
screen = pygame.display.set_mode((640, 480), 0, 32)
screen.blit(background, (0, 0))
pygame.display.update()
|
可变尺寸的Pygame窗口
有时候用户想要能够改变窗口大小,调用pygame.display.set_mode时使用RESIZABLE标志位,可以达到这个目的。Pygame通过发送包含新宽高的VIDEORESIZE事件告诉用户窗口大小改变了。当收到这个事件时,我们应该再次调用pygame.display.set_mode。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
background_image_filename = 'sushiplate.jpg'
import pygame
from pygame.locals import *
from sys import exit
SCREEN_SIZE = (640, 480)
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE, RESIZABLE, 32)
background = pygame.image.load(background_image_filename).convert()
while True:
event = pygame.event.wait()
if event.type == QUIT:
exit()
if event.type == VIDEORESIZE:
SCREEN_SIZE = event.size
screen = pygame.display.set_mode(SCREEN_SIZE, RESIZABLE, 32)
pygame.display.set_caption('Window resized to ' + str(event.size))
screen_width, screen_height = SCREEN_SIZE
for y in range(0, screen_height, background.get_height()):
for x in range(0, screen_width, background.get_width()):
screen.blit(background, (x, y))
pygame.display.update()
|
VIDEORESIZE事件包含下面的值:
- size-一个元组,包含更改后窗口的尺寸。size[0]代表宽度,size[1]代表高度。
- w-宽度,和size[0]一样,但是更方便
- h-高度,和size[1]一样,但是更方便
无边框窗口
当调用set_mode时使用NOFRAME标志可以设置一个无边框窗口。
其它显示标志
通常最好是使用0显示窗口而使用FULLSCREEN全屏以保证程序在所有平台都能运行。其它高级标志也许会有兼容问题。
如果设置了HWSURFACE标志,将创建一个硬件显示,存储在显存里面,只能和FULLSCREEN一起使用。
1
|
screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE | FULLSCREEN, 32)
|
硬件显示比常规显示更快,因为它能够利用显卡的特性加速显示,缺点是兼容性不够好。硬件显示也能从DOUBLEBUF标志受益。这个有效地创建2个硬件显示,但是一次只能看见一个。
1
|
screen = pygame.display.set_mode(SCREEN_SIZE, DOUBLEBUF | HWSURFACE | FULLSCREEN, 32)
|
通常当你调用**pygame.display.update()**时,整个屏幕从内存拷贝到显示设备,这会花费一些时间。而双缓冲允许你立刻切换到新的屏幕,使你的程序运行更快。
注意
如果使用双缓冲显示,应该调用pygame.display.flip()而不是pygame.display.update()。这个做立即显示切换而不是拷贝屏幕数据。
1
2
3
|
flip(...)
pygame.display.flip(): return None
update the full display Surface to the screen
|
最后一个显示标志是OPENGL,它能够使用3D加速显示。
使用字体模块
字体模块使用TrueType字体(TTFs)。必须先创建一个Font对象才能使用字体。最简单的方式就是使用pygame.font.SysFont,它会使用系统自带的一个字体。
1
|
my_font = pygame.font.SysFont("arial", 16)
|
第一个参数是你想要创建字体的名字,第二个参数指定了字体的大小。Pygame会在系统字体里面查找,如果没找到则返回一个默认字体。pygame.font.get_fonts()可以获得当前系统所有可用字体。也可以使用pygame.font.Font直接从.ttf文件创建字体。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
my_font = pygame.font.Font("my_font.ttf", 16)
class Font(__builtin__.object)
| pygame.font.Font(filename, size): return Font
| pygame.font.Font(object, size): return Font
| create a new Font object from a file
get_fonts()
pygame.font.get_fonts() -> list
get a list of system font names
Returns the list of all found system fonts. Note that
the names of the fonts will be all lowercase with spaces
removed. This is how pygame internally stores the font
names for matching.
|
一旦创建了Font对象,就可以使用Font对象的render函数来渲染文字。它创建一个新的包含文字的Surface,可以输出到显示设备。
1
2
3
4
5
|
text_surface = my_font.render("Pygame is cool!", True, (0,0,0), (255, 255, 255))
render(...)
Font.render(text, antialias, color, background=None): return Surface
draw text on a new Surface
|
render的第一个参数是你想渲染的文字,它必须是一行。如果有多行,必须使用多个render调用。第二个参数是一个布尔值,用来开启抗锯齿。如果设置为True,则文字看起来会比较平滑。后面两个参数是文字的颜色和背景颜色。背景色是可选的,默认为透明的。
1
2
3
4
5
6
7
|
import pygame
pygame.init()
my_name = 'Smith'
my_font = pygame.font.SysFont('arial', 64)
name_surface = my_font.render(my_name, True, (0, 0, 0), (255, 255, 255))
pygame.image.save(name_surface, 'name.png')
|
font模块详尽参考见http://www.pygame.org/docs/ref/font.html
注意
安装的字体因机各异,不能保证某个字体一定存在。解决方法是.ttf文件同游戏一起发布,但必须得到字体作者的许可。
当Pygame出错
当pygame.image.load不能读取图片时,Pygame会抛出pygame.error异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>> import pygame
>>> screen = pygame.display.set_mode((640, 0))
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
pygame.error: Cannot set 0 sized display mode
class error(exceptions.RuntimeError)
| Method resolution order:
| error
| exceptions.RuntimeError
| exceptions.StandardError
| exceptions.Exception
| exceptions.BaseException
| __builtin__.object
|
一般而言,当你碰到pygame.error异常时,你也做不了什么事,因为这说明一个比较大的故障发生了。通常你能做的就是指导用户怎么做。在一个大的项目里面,检查错误很重要。
1
2
3
4
5
6
|
try:
screen = pygame.display.set_mode(SCREEN_SIZE)
except pygame.error, e:
print "Can't create the display :-("
print e
exit()
|
Pygame动起来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
background_image_filename = 'sushiplate.jpg'
SCREEN_SIZE = (640, 480)
message = ' This is a demonstration of the scrolly message script. '
import pygame
from pygame.locals import *
from sys import exit
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
font = pygame.font.SysFont('arial', 80)
text_surface = font.render(message, True, (0, 0, 255))
x = 0
y = (SCREEN_SIZE[1] - text_surface.get_height()) / 2
background = pygame.image.load(background_image_filename).convert()
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
screen.blit(background, (0, 0))
x -= 2
if x < -text_surface.get_width():
x = 0
screen.blit(text_surface, (x, y))
screen.blit(text_surface, (x + text_surface.get_width(), y))
pygame.display.update()
|