Pygame是脚本语言Python的一个扩展包就是SDL的封装包,是用Python 和C语言开发;开发人是Pete Shinners。专门用于开发各类游戏。SDL意思是Simple Directmedia Library即“简单媒体类库”,这是一个跨平台的控制多媒体的C语言类库,类似于 DirectX。这里说的媒体包括CDROM即光驱、键盘鼠标、音频视频、操纵杆等输入输出设备,Pygame写了关于这些对象的类库可在官方网站上找到。因为是“面向对象”就是所谓Object-Oriented编程语言,所以我们可以继承这些类的性质来编写我们自己的游戏。
Pygame的官方网站: Pygame.org
可惜中文的Pygame文档还没有。因为Pygame是面向对象脚本编程语言Python的一个扩展,所以在使用Pygame之前要求基本的Python的知识和技术。
不管在什么平台上开发,所有游戏的代码通常会有的结构包含六个部分:
- (一)加载Pygame的或者其他工具软件的模块,在Python是使用import语法完成;
- (二)资源处理类包括处理图象、声音、连接或断开网络等;
- (三)游戏本身的对象类;
- (四)其他函数。包括不能归类的必要函数,它们可以归入游戏的逻辑代码块里但是这样处理会增加理解代 码的难度,所以把这类函数单独处理;
- (五)初始化。包括Pygame本身对象的初始化;游戏对象的初始化;背景、鼠标的初始化等;
- (六)主循环就是def main()。处理用户输入、更新游戏对象和显示区等。
实 际上,游戏的结构是个风格问题,不同的人、从不同的侧重点出发会使用不同的方法组织自己的游戏代码。中 国人学太极拳的时候有这样的一个讲究,刚刚开始学的时候要有严格的“架式”就是动作规范,但是等到悟到用意不用力、用意念引导动作的阶段就可以随心所欲, 不必太拘泥于一招一式细节了。
例如有从完全另外的角度的方法,把代码按照一下四个方面组织:(请查看这里的资源作为参考)
- 模块和对象的初始化;
- 处理用户输入和事件循环;
- 动画和模拟;
- 提交;
不管使用什么方式组织代码,上 面例子中最主要的结构有两个:一个是主程序即mail() loop;一个是游戏对象的类。主程序是游戏脚本开始时执行的,包括处理Pygame模块本身的初始化和游戏对象的初始化,还包括所谓事件循环就是处理游 戏玩家的输入如鼠标、键盘动作和其他事件的队列;而游戏类对象封装了游戏角色的数据和逻辑。对于初学者来说,先理解这两个结构可收提纲协领的效果。有些简 单的游戏只有一个游戏对象(没办法,我们总是要从最简单的例子开始),可能没有完全按照上面的格式写,但总是包含了这样的模式在里面。
这里有一个Pygame的代码库,各种游戏代码可供研究:Pygame代码库
第一个简单的例程hello.py:
#Find place where Python interpretor located 让游戏脚本找到Python解释器安装的地方
#!/usr/bin/python
#Import using modules 输入要用到的模块
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()
#start main() 启动脚本时执行主循环
if __name__ == '__main__': main()
把 这段代码用自己的编辑器写入你的一个名字叫py/的目录里,这个目录里只包含作为测试和学习用的代码样例(当然你也可以有其他的安排,你肯定有自己的打 算 。注意你下载这个程序时不要使用中文注释,特别不要使用中文的#号,Python的解释器不能识别。请只下载代码。我这里加中文是为了解释代码的方 便。注意给这个脚本一个名字hello.py。
用这个简单的程序我们完成了一个框架,多数应用程序特别是一个游戏就是写在这上面的,如下图:
不 算空格,加注释行,就是以#号开始的,以上总共31行代码。还有其他的语言能用同样的代码量来完成同样 的工作吗?也许你用可视化工具可以很容易地作出这了来,但那只是“依样画葫芦”!而你用Python写出这个简单的框架,你学会了一种功能强大的语言! 也许你会说Java的Applet能用更少的代码完成这些工作,实际上不是这样的,因为Java/Applet是在用户端的浏览器帮助下完成的,而这个是stand alone。Python还是一种标准的黑客工具,世界上顶极黑客正在使用她!
是很简单吗?但是你已经开始了!这个简单的程序里已经包含了游戏的主要结构。
首先你要在计算机监视器上创建一个游戏执行的框架区域,一般叫做screen。
***********************************************
课堂实验一
使用命令python进入命令行模式:
>>> import pygame
>>> from pygame.locals import *
>>> screen = pygame.display.set_mode((800,400))
这样你创建了一块屏幕面积,宽800个像素,高400个像素,它的左上角的坐标为(0,0)这样你就完成了屏幕初始化的工作。
********************************************
学 习一种语言最好的办法就是实验,通过观察软件的行为,输入输出的变化来理解代码的功能。跟随我的路线图保证你能在最短时间内顺利掌握语言和技术精髓,不必 从基本教程开始。牛顿说过:“在科学发展上实例比理论更重要!”,我相信在学习技术上也是这样。我们先仔细地研究这个程序样例,深入剖析,彻底理解,举一 反三,触类旁通。
- (1)pygame.init( ) ——实现模块初始化,就是把你要输入的模块进行初始化,准备工作;
- (2)screen. = pygame.display.set_mode((150,50)) ——初始化游戏屏幕,宽150高50个像素。
- (3)pygame.display.set_caption( 'Basic Pygame Program') ——在屏幕的页眉上写字;
- (4)background = pygame.Surface(screen.get_size() ) ——开始填满背景,.get_size()表示跟屏幕一样大。引用Surface创建的实例相当于在前面创建的screen上敷上一张画布,所以后面的 blit( )方法实际上是在这张画布上画,而不是直接在screen上画;
- (5)background.fill((255,255,255)) —— 把RGB格式的颜色写入背景;
- (6)font = pygame.font.Font( );text = font.render("Hello There",1,(10,10,10))以及上面的screen是模块的对象,区别于游戏本身的对象。font创建字体对象,text创建文本对象就是 框架中间的Hello There,(10,10,10)是文本字体的颜色,而中间的数字1是对字体性质的限定,这里我们先忽略;
- (7)textpos = text.get_rect( ) —— 是为font.render( )提交变量的text准备一个矩形框,让提交的文本放在这里;
- (8)background.blit ( text,textpos ) —— 可能blit ( )是最值得注意的方法,这不是一个常用词,本copy和past是同义词,就是拷贝和粘貼的意思,把什么什么拷贝/粘貼在什么什么上面,这里就是把 text粘貼在textpos上的意思;
- (9)screen.blit(background,(0,0)) —— 接着又一次出现blit( )方法,这次是把上一步粘貼好文本的background再拷贝在screen上,(0,0)表示从坐标原点开始就是从左上角开始;
- (10)pygame.display.flip( ) —— flip英文意思是轻拍、快速翻转,这个方法激活各个元素,以后我们还会碰到方法update( ),这二者的作用有些相似,从字面上可以看出这个意思;
- (11)While 1 : —— 下面就开始事件循环,把可能的用户输入和其他输入的动作队列循环测试,给出应有的反应;
- (12)最后又出现了blit()、flip()方法,在每次检测之后更新画面;
- (13)if —— 语句告诉游戏脚本执行定义的主循环程序即def main()。
import sys, pygame
pygame.init()
size = width, height = 320, 240
speed = [2, 2]
black = 0, 0, 0
screen = pygame.display.set_mode(size)
ball = pygame.image.load("ball.bmp")
ballrect = ball.get_rect()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
ballrect = ballrect.move(speed)
if ballrect.left < 0 or ballrect.right > width:
speed[0] = -speed[0]
if ballrect.top < 0 or ballrect.bottom > height:
speed[1] = -speed[1]
screen.fill(black)
screen.blit(ball, ballrect)
pygame.display.flip()
在 这个例程中有了一个游戏对象就是一个运动的球,有了处理资源的语句就是ball = pygame.image.load("ball.bmp")。资源包括图像文件、声音文件、互联网接入等。Pygame能接受几乎所有图像音频视频文件 格式如bmp、jpeg、mp3等。
第三个例程是更完整的chimp.py:(可在 这里下载/拷贝/测试)
#/usr/bin/env python首 先在代码开始的地方有三重的双括号""" """,括号内的叫docstring就是关于代码的文档说明,规范地说,每个程序代码都应该包含这部分内容,开发者自己或开发团队可以从这里获得关于代 码的功能、许可证等方面的信息。这是Python所特有的所谓“自省”功能,英语叫“Introspection”。这里有必要加以简单的介绍。在命令行 里使用dir( module/function),在实验中用 Python的模块、函数、方法os、sys、 pygame等等甚至是一个数字如2来代替括号里的斜体字,可以查看对象的性质。因为在Python里,一切都是对象,所以这些都可以使用dir()函数 查看对象的性质,这包括对象的结构、包含的函数和方法、类的继承关系、子类、可以接受的变量、返回的结果等详细信息。另外有同等功能的是在命令行的符号 >>>里使用help( )函数,用法与dir( )相同,但是得到的信息有所不同。这些都内嵌在Python的解释器里所以叫做内省的。非常形象。
"""
This simple example is used for the line-by-line tutorial
that comes with pygame. It is based on a 'popular' web banner.
Note there are comments here, but for the full explanation,
follow along in the tutorial.
"""
#Import Modules
import os, pygame
from pygame.locals import *
if not pygame.font: print 'Warning, fonts disabled'
if not pygame.mixer: print 'Warning, sound disabled'
#functions to create our resources一下是处理资源的函数,如下载图像文件或者声音文件
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:', fullname
raise SystemExit, message
image = 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()
def load_sound(name):
class NoneSound:
def play(self): pass
if not pygame.mixer or not pygame.mixer.get_init():
return NoneSound()
fullname = os.path.join('data', name)
try:
sound = pygame.mixer.Sound(fullname)
except pygame.error, message:
print 'Cannot load sound:', fullname
raise SystemExit, message
return sound
#classes for our game objects以下是本游戏仅有的两个对象的类Fist和Chimp,这是游戏中涉及模拟和动画的部分
class Fist(pygame.sprite.Sprite):
"""moves a clenched fist on the screen, following the mouse"""
def __init__(self):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image, self.rect = load_image('fist.bmp', -1)
self.punching = 0
def update(self):
"move the fist based on the mouse position"
pos = pygame.mouse.get_pos()
self.rect.midtop = pos
if self.punching:
self.rect.move_ip(5, 10)
def punch(self, target):
"returns true if the fist collides with the target"
if not self.punching:
self.punching = 1
hitbox = self.rect.inflate(-5, -5)
return hitbox.colliderect(target.rect)
def unpunch(self):
"called to pull the fist back"
self.punching = 0
class Chimp(pygame.sprite.Sprite):
"""moves a monkey critter across the screen. it can spin the
monkey when it is punched."""
def __init__(self):
pygame.sprite.Sprite.__init__(self) #call Sprite intializer
self.image, self.rect = load_image('chimp.bmp', -1)
screen = pygame.display.get_surface()
self.area = screen.get_rect()
self.rect.topleft = 10, 10
self.move = 9
self.di = 0
def update(self):
"walk or spin, depending on the monkeys state"
if self.di:
self._spin()
else:
self._walk()
def _walk(self):
"move the monkey across the screen, and turn at the ends"
newpos = self.rect.move((self.move, 0))
if self.rect.left < self.area.left or /
self.rect.right > self.area.right:
self.move = -self.move
newpos = self.rect.move((self.move, 0))
self.image = pygame.transform.flip(self.image, 1, 0)
self.rect = newpos
def _spin(self):
"spin the monkey image"
center = self.rect.center
self.di = self.di + 12
if self.di >= 360:
self.di = 0
self.image = self.original
else:
rotate = pygame.transform.rotate
self.image = rotate(self.original, self.di)
self.rect = self.image.get_rect(center=center)
def punched(self):
"this will cause the monkey to start spinning"
if not self.di:
self.di = 1
self.original = self.image
#主循环开始。当游戏脚本开始这个函数即被调用,并初始化模块和游戏对象,执行事件循环直到返回
def main():
"""this function is called when the program starts.
it initializes everything it needs, then runs in
a loop until the function returns."""
#Initialize Everything初始化
pygame.init()
screen = pygame.display.set_mode((468, 60))
pygame.display.set_caption('Monkey Fever')
pygame.mouse.set_visible(0)
#Create The Backgound创建背景
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((250, 250, 250))
#Put Text On The Background, Centered把文本贴在或者拷贝在背景上。第一次出现blit( )函数
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)
#Display The Background显示背景并更新。第二次出现blit( )函数
screen.blit(background, (0, 0))
pygame.display.flip()
#Prepare Game Objects准备好游戏对象包括时钟控制、模拟的对象、资源对象等
clock = pygame.time.Clock()
whiff_sound = load_sound('whiff.wav')
punch_sound = load_sound('punch.wav')
chimp = Chimp()
fist = Fist()
allsprites = pygame.sprite.RenderPlain((fist, chimp))
#Main Loop 事件循环,鼠标、键盘等的事件队列
while 1:
clock.tick(60)
#Handle Input Events 处理用户输入事件
for event in pygame.event.get():
if event.type == QUIT:
return
elif event.type == KEYDOWN
and event.key == K_ESCAPE:
return
elif event.type == MOUSEBUTTONDOWN:
if fist.punch(chimp):
punch_sound.play() #punch
chimp.punched()
else:
whiff_sound.play() #miss
elif event.type is MOUSEBUTTONUP:
fist.unpunch()
allsprites.update()
#Draw Everything 第三次出现blit( )函数
screen.blit(background, (0, 0))
allsprites.draw(screen)
pygame.display.flip()
#Game Over
#this calls the 'main' function when this script is executed 当本游戏脚本执行时调用main( )函数
if __name__ == '__main__': main()
***********************************************
课堂实验二
>>> import pygame
>>> print dir(pygame)
>>> print dir(pygame.Rect)
>>> print help(pygame.Rect)
例如:使用print dir(pygame.Rect)得到以下信息:
['__class__', '__cmp__', '__coerce__', '__copy__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__getattribute__', '__getitem__', '__getslice__', '__hash__', '__init__', '__len__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__safe_for_unpickling__', '__setattr__', '__setitem__', '__setslice__', '__str__', 'bottom', 'bottomleft', 'bottomright', 'center', 'centerx', 'centery', 'clamp', 'clamp_ip', 'clip', 'collidedict', 'collidedictall', 'collidelist', 'collidelistall', 'collidepoint', 'colliderect', 'contains', 'fit', 'h', 'height', 'inflate', 'inflate_ip', 'left', 'midbottom', 'midleft', 'midright', 'midtop', 'move', 'move_ip', 'normalize', 'right', 'size', 'top', 'topleft', 'topright', 'union', 'union_ip', 'unionall', 'unionall_ip', 'w', 'width', 'x', 'y']
信息给出了Pygame的模块Rect包含的函数、需要的变量和子类。这样你就知道怎样 使用这个模块,例如由Rect创建的矩形面积有bottonleft、bottonright,就是左按钮、右按钮,那么跟按钮绑定的动作就是可引用的。 面向对象包含的封装就是把对象的性质和动作封装在一个类里面。
如果使用help(pygame.Rect)得到信息如下:
Help on class Rect in module pygame:
class Rect(__builtin__.object)
| The rectangle object is a useful object
| representing a rectangle area. Rectangles are
| created from the pygame.Rect() function. This routine
| is also in the locals module, so importing the locals
| into your namespace allows you to just use Rect().
|
| Rect contains helpful methods, as well as a list of
| modifiable members:
| top, bottom, left, right, topleft, topright,
| bottomleft, bottomright, size, width, height,
| center, centerx, centery, midleft, midright, midtop,
| midbottom.
| When changing these members, the rectangle
| will be moved to the given assignment. (except when
| changing the size, width, or height member, which will
| resize the rectangle from the topleft corner)
|
| The rectstyle arguments used frequently with the
| Rect object (and elsewhere in pygame) is one of
| the following things.
... ...
这些信息告诉我们库类Rect能为我们作些什么工作,它创建一个矩形的面积把Sprite创建的对象就是图像放在里面等等。
********************************************
我 们在注释里强调了三次出现blit( )函数,所以这个函数对于理解游戏代码是个关键!第一个blit()的语法是background.blit(text,textpos),即把文本 text拷贝在text的位置textpos上,并把textpos拷贝在background上;第二个blit()的语法是screen.blit (background,(0,0)),是把有了文本的background拷贝在screen上;第三个screen.blit (background,(0,0))把上面刚创建、并拷贝在background上的游戏模拟对象fist和chimp再拷贝到screen上;还有要 注意的是,每个blit()后面都跟随一个flip(),上面在简单例子里我们已经介绍了英语flip的意义,这是要更新画面,动画就是每一个循环给游戏 对象一个新的位置,然后用flip()使得动画不断翻转,得到运动的感觉。而语句clock.tick(60)是由创建游戏对象的部分里的clock = pagame.time.Clock()语句对象化得到的,这个对象告诉游戏脚本按照每秒60桢的速度更新屏幕。
PyGame的库类介绍
Pygame的类库:
Cdrom || Cursors || Display || Draw || Event || Font || Image || Joystick || Key || Mixer || Mouse || Movie || Music || Overlay || Pygame || Rect ||Sndarray || Sprite || Surface || Surfarray || Time || Transform
这 些库类的功能可以望文生义,如Crom是处理光盘的类,Cursors是处理光标的类,Key是处理键盘的类,Joystick是处理游戏操纵杆的类, Mouse是处理鼠标的,而Music显然是处理音乐的。等大家有了一些基础之后就应该仔细地研究这些类库,熟练地掌握他们做起游戏来得心应手,左右逢 源。下面我们将就最常用的加以说明,在上面的样例涉及到的就是最重要的类库。下面我们将以sprite、Display、Event、Image等为例说明类库的使用。