Pygame游戏开发之一
初涉红尘
本人是个游戏爱好者,同样是个游戏开发爱好者,最近开始学习Pygame,Pygame是 跨平台的 Python模块(包括Win32、Unix、Mac、Android等等),主要包含图像和声音,专为电子游戏设计。所有需要的游戏功能和理念都完全简化为游戏逻辑本身,这样就大大提高了开发效率。
我学Pygame之前对Python可以说是一无所知,所以大致浏览了一下《Python核心编程》,但是学一门语言不能光靠看书,必须亲身实践。可以说是一边做游戏,一边学习的过程,通过做游戏来更好地理解这门语言。Pygame的文档可以在以下这个链接中找到:http://www.pygame.org/docs/ 基本游戏中需要用到的一些操作它都给你封装好了,非常方便。
做游戏之前让我们先把环境配好,到Python的官网把Python相关的软件下载下来(http://www.python.org/download),因为它是跨平台的,所以会有很多不同平台的版本,我下了一个Windows版的python-2.5.msi,之后执行该文件安装Python,一般是安装在C:盘下。安装完毕后,进入 开始菜单->Python 2.5->IDLE,就是Python的IDE(集成开发环境)了。然后是安装Pygame模块,到http://www.pygame.org/download.shtml下载对应平台的pygame,安装即可。
接下来就开始我们的“Pygame游戏制作之旅”,和大多数的游戏制作一样,程序的框架都是大同小异的,一般就是:
初始化 - (图像、声音)
主循环 -
事件处理(鼠标、键盘)
逻辑层
渲染层
释放资源
Pygame的初始化非常方便,首先调用pygame.init(),这个函数为所有导入的pygame模块进行初始化,如果初始化失败,不会引发任何的异常。
Pygame中用于显示的是一个叫Surface的东西,Surface可以理解成一个表面,就像画画的白纸一样,任何东西都可以在上面绘制。每个Surface都有它固定的分辨率(可以理解成白纸的大小,即宽和高)以及像素格式(pixel format)。
调用pygame.Surface可以创建一个图像对象。起初Surface会被清空成全黑,唯一需要的参数是它的大小。其它还有几个默认的参数:
pygame.Surface((width, height), flags=0, depth=0, masks=None): return Surface
pygame.Surface((width, height), flags=0, Surface): return Surface
像素格式可以通过传入位深depth或者一张存在的Surface来决定,flags参数是表面的一组位或标志,可以传入以下参数的任意组合:
HWSURFACE, 在显存中创建图像
SRCALPHA, 像素格式有alpha值,即透明度
Surface可以有许多额外的属性,像alpha层,关键色,源矩形裁剪等。这些功能主要影响Surface是如何粘贴到其它表面的。这里的粘贴不是一般意义上的粘贴,因为它不止是原图拷贝,有可能是进行像素的与运算、或运算等等,我们称这种操作为blit(位块传输),blit会尽可能使用硬件加速,如果实在不行,它们会使用具有很好优化手段的软件传输方法。
在Pygame中支持三种类型的透明处理:关键色(colorkeys),表面alpha(surface alphas),像素alpha(pixel alphas)。表面alpha能够和关键色混合使用,但是如果用了像素alpha,就不能和其它两种进行混合使用了。关键色使得某一种颜色成为透明,任何和这个颜色一样的像素将不会被绘制出来。表面alpha是一个单一的值,它决定了整张图片的透明度。这个值为255表示不透明,0则表示完全透明。
像素alpha储存了所有像素的alpha值,也就是透明度。在表面上的像素访问是允许的,但是对于一个游戏来说,这个速度实在是太慢了-_-|||(不是一般的慢啊…试过你就知道了)。像素的访问和修改可以通过get_at() 和 set_at()函数,但是如果需要批量操作,这种方法是不可取的,实在要做,可以利用pygame.surfarray 模块,它将Surface看做是很大的高维数组,操作起来相当快。任何函数如果要直接访问一个Surface的某个像素数据的话需要将这个Surface锁定,锁定和解锁可以调用lock()和unlock()方法。
对Surface有一定了解之后,我们就可以来看display了,pygame.display是Pygame中用于屏幕显示的模块,一旦创建了一个display,你就可以把它当成是常规的Surface来使用了。Pygame在进行显示的时候是采用双缓冲机制的,其实它内部有两个Surface,每次要绘制时是在后备缓冲上进行绘制,当所有东西都绘制完毕后需要手动调用pygame.display.flip()函数,这个函数的作用就是将后备缓冲和前台缓冲进行一次交换,如此一来,先前绘制的东西也就自然而然得显示到屏幕上来了(也可以调用pygame.display.update(),它是对前一个翻页函数的优化,可以传入一些矩形,只在传入的矩形进行更新操作)。我们利用以下函数对屏幕的Surface进行初始化:
pygame.display.set_mode(resolution=(0,0), flags=0, depth=0) : return Surface
resolution表示需要创建的窗口的大小,即分辨率。flags表示一些位或标志,一般有以下几个:
pygame.FULLSCREEN 创建一个全屏的显示模式
pygame.DOUBLEBUF 建议和 HWSURFACE 或 OPENGL组合
pygame.HWSURFACE 全屏下硬件加速
pygame.OPENGL 创建一个opengl 的渲染显示模式
pygame.RESIZABLE 显示窗口大小可变
pygame.NOFRAME 显示窗口没有边框
depth则表示当前Surface的位深。
screen = pygame.display.set_mode(SCREENRECT.size, pygame.FULLSCREEN)
我们可以采用以上语句创建一个用于全屏显示的Surface对象,并且返回给全局变量screen。接下来我们从文件中读取一张图片并且将它显示到屏幕上来,pygame.image模块是用于图像的传输的,它包含了一些读取和保存图片的函数,读取图片可以采用pygame.image.load
。它的原型是:
pygame.image.load(filename) return Surface
从文件中载入一个图像,传入的是文件名。Pygame将会自动决定图像的类型(gif 或者 bmp)并且从数据中创建一个新的Surface对象。返回的Surface对象将会包含和文件中的原图一样的颜色格式、关键色和alpha通道。你可以返回Surface.convert()来创建一个拷贝,它在屏幕上绘制的时候效率更高。
对于alpha通道,像png图像可以在载入后使用convert_alpha()方法使得图像的像素具有透明度。Pygame不总是支持所有的图像文件格式,但是它肯定支持的是标准的BMP文件,可以通过pygame.image.get_extended来检测它是否支持其他图像文件,例如png。
我们可以通过定义一个load_image函数来将图像的载入封装起来,以后用的时候就会方便许多了。
def load_image(file) :
file = os.path.join('data', file)
try:
surface = pygame.image.load(file)
except pygame.error:
raise SystemExit, 'Can not load image %s' % pygame.get_error()
return surface.convert_alpha()
以上是将一个data文件夹下的名为file的图像文件读取并且返回一个Surface的过程。
调用如下:
myImage = load_image(‘hero.png’)
现在我们拥有两个Surface:一个是screen,表示屏幕显示设备;另外一个则是由图像文件中创建的myImage,如果想要将myImage显示到屏幕上来,只需要进行如下的操作:
screen.blit(myImage, pos, srcRect, 0)
第一个参数是源Surface对象,pos则是传送到screen的左上角的位置,是一个二元组,表示xy坐标, srcRect表示的是一个矩形四元组,是将myImage中srcRect这个矩形的区域传送到screen上来,最后一个参数则是对两张Surface的一个混合的标志,BLEND_ADD表示将两者的对应点的颜色像素值相加,BLEND_SUB表示相减等等。到此为止,myImage还不能被显示到屏幕上来,因为blit的过程只是将myImage这个Surface传送到后备缓冲中,我们还需要调用pygame.display.flip()进行翻页。如此一来,就可以轻松得实现图像文件在屏幕的贴图了。
大致了解了图像的显示之后让我们来看下框架代码的编写,C++开头一般都会包含一些头文件,Python也类似,Pygame是Python的一个模块,但是如果不进行导入是不能用它的东西的,Python中用import进行模块的导入:
import pygame, sys, os.path
sys和os.path都是系统模块,当然,自己写的.py文件也可以作为一个模块来用于导入。
必要模块导入之后需要检测当前系统下是否支持除标准BMP以外的图像文件,可以调用pygame.image.get_extended()函数来检测,如果返回False则只能使用标准BMP文件了。
Python中有一个特殊的变量__name__,如果当前程序是被当成一个模块被导入的,那么它的值是模块名;如果是作为程序执行的,则它的值是’__main__’,所以我们需要对这个变量进行判断,像这样:
if __name__ == '__main__' : Go()
以上的Go函数就好比是C语言中的main()函数,不同的是它可以以任意的名字命名。所有的初始化工作可以放在Go函数的开头,我用Initialize函数将它封装起来了。
def Go():
Initialize()
while True :
for event in pygame.event.get():
if event.type == pygame.KEYDOWN :
if event.key == K_ESCAPE :
pygame.quit()
sys.exit(0)
elif event.type == pygame.QUIT :
pygame.quit()
sys.exit(0)
keystatus = pygame.key.get_pressed()
logical()
render()
pygame.display.update()
上面这段代码是Pygame的游戏框架,对图像之类的初始化工作被放在Initialize函数里,之后是游戏的主循环,循环内部首先进行的是事件处理,这里的事件是用的Pygame中的pygame.event模块,Pygame对事件的组织是通过事件队列的,pygame.event.get()将得到事件队列中所有的事件,并且将他们从事件队列中移除。每个事件有一个事件类型event.type,例如KEYDOWN表示键盘上的键按下,KEYUP表示键抬起等等。
logical()和render()分别是游戏的逻辑层和渲染层,可以在逻辑层中添加一些控件,并且设置它们的位置以及大小,然后渲染层则负责将这些东西显示到屏幕上来。(未完待续)
以上内容均为原创 转载请注明出处
图 1-1