游戏中需要用到很多对象,比如人物、野怪、建筑物、UI控件等等,他们的行为不尽相同,但是总有那么些共同点,如果将所有的东西都封装成各自的类难免会写上很多重复的代码,因为两个对象如果具有相同的行为(比如人物和野怪都可以行走)如果写两个move()函数,以后改起来就要修改两个地方,不容易维护而且扩展很麻烦,但是人物和野怪又有其它不相同的行为,比如技能之类的不同。所以比较好的处理方法是抽象出一个生物类,然后让人物和野怪去继承它,举个例子:
class RenderObject(pygame.sprite.Sprite) :
# 静态变量
images = []
def __init__(self, selfdata) :
self.image = self.images[ int(selfdata[0]) ]
self.size = [self.image.get_width(), self.image.get_height()]
self.offset = [0,0]
self.position = [0,0]
self.speed = [0.0,0.0]
def move(self, xgo, ygo) :
do_move()
def draw(self, screen) :
do_draw()
以上这个类RenderObject是自己定义的用于渲染的类的基类,它继承自pygame.sprite.Sprite,这里有必要先介绍一下pygame.sprite,这个模块在pygame中是选择性使用的,他可以作为游戏中很多对象的基类,并且将它拥有的东西绘制到Surface上,还有一个Group类是Sprite类的容器类,可以包含很多Sprite。
RenderObject有三个类函数,__init__是Python中的特殊函数,类似C语言中的构造函数,当类实例被创建的时候,这个函数会被自动执行,它的目的是执行一些该对象的必要的初始化工作(我在这个函数中加入了一个selfdata的列表,以便于不同类型的控件添加初始化信息)。move函数是执行移动操作,而draw函数则负责渲染。
首先来看下__init__函数,在Python中使用变量不需要提前声明,所以要用到的变量我把它放在了__init__函数中进行初始化,注意到每个函数都有一个self参数,这个就好比C语言中的this指针,但是它是显式声明,隐式调用的。在使用类内部变量的时候需要加self做修饰,我们可以看到该类的一些成员变量:
self.image # 该渲染对象对应的Surface
self.size # 该渲染对象在Surface的矩形区域的宽高
self.offset # 该渲染对象在Surface的左上角坐标
self.position # 该渲染对象将被绘制到屏幕的位置
self.speed # 该渲染对象的速度
之所以要用到offset和size,是因为该Surface不是完全被用来显示的,我们只取某一张Surface的某一部分用于显示,如果不是很明白,那么参看Player类的相关描述就知道为什么了。
对于move(self, xgo, ygo)函数,我们暂时可以这样实现:
if xgo :
self.position[0] += self.speed[0] * xgo
if ygo :
self.position[1] += self.speed[1] * ygo
xgo和ygo都是三值变量(1,0,-1),分别表示在两个坐标轴方向是否发生了位移,并且这个位移是正向的还是逆向的。于是我们可以通过键盘输入来传递对应的参数,position的值就会随之变化了。注意如果这里速度为0,则表示静态物体,position不会做任何变化。
对于draw(self, screen)函数,用于将当前对象绘制到屏幕上,screen参数是上一节中提到的屏幕Surface,我们调用Surface的blit函数就可以在屏幕上绘制当前对象了,像这样:
screen.blit( self.image, self.position, (self.offset, self.size))
(self.offset, self.size)表示self.image这个Surface上对应的矩形区域。
基类完成后,我们需要派生出一些子类,例如:
class Player(RenderObject) :
actionwait = 50
def __init__(self, selfdata) :
RenderObject.__init__(self, selfdata)
self.size[0] /= 4
self.size[1] /= 4
self.speed = [0.1,0.1]
self.direction=3
self.action=0
def move(self, xgo, ygo) :
RenderObject.move(self, xgo, ygo)
if xgo or ygo :
self.action = (self.action + 1) % (self.actionwait * 4)
self.offset[0] = (self.action / self.actionwait) * self.size[0]
self.offset[1] = self.direction * self.size[1]