IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Pygame游戏开发 之四

    英雄哪里出来发表于 2011-04-27 18:19:00
    love 0

    Pygame游戏开发之四

    初学乍练

    Pygame中除了Group这个Sprite的容器类,还有一些继承自Group的容器类,它们分别是RenderUpdates、OrderedUpdates、LayeredUpdates、LayeredDirty。

    pygame.sprite.RenderUpdates继承自pygame.sprite.Group,它相对于Group的不同点就是重写了Group的draw函数,原型是:RenderUpdates.draw(surface) : return Rect_list它将所有它包含的Sprites绘制到surface上,和Group.draw一样。但是这个函数返回了一系列的屏幕上发生改变的矩形列表,这个矩形列表应该被传递到pygame.display.update中去。

    pygame.sprite.OrderedUpdates继承自pygame.sprite.RenderUpdates,它绘制Sprite的时候是以Sprite被添加时的顺序来绘制的。

    pygame.sprite. LayeredUpdates绘制的方式和pygame.sprite.OrderedUpdates类似,只是它引入了图层的概念。你可以通过'default_layer'设置默认图层,或者一个整数来表示图层。如果添加的Sprite自己有一个layer的图层属性那么就是用这个,否则在添加Sprite的时候,在pygame.sprite. LayeredUpdates(*sprites, **kwarges)中的**kwarges参数中对layer属性进行设置,如果两者都没有设置,那么系统将采用默认图层。

    pygame.sprite. LayeredDirty继承自pygame.sprite.LayeredUpdates,它是DirtySprites的专用容器类,继承了Group的所有操作。

    因为我们需要用到DirtySprite,所以之前我们用到的所有Group类都替换成LayeredDirty,而且为了便于扩展,我们将LayeredDirty再封装一层,以便添加新的成员函数。

    class gameManager(pygame.sprite.LayeredDirty) :

    def __init__(self, selfdata) :

    pygame.sprite.LayeredDirty.__init__(self)

    def keyevent(self, keypressed) :

    for son in self.sprites() :

    son.keyevent(keypressed)

    定义一个gameManager类,它继承了pygame.sprite.LayeredDirty,并且添加一个keyevent的成员函数,用于对键盘事件进行处理,它的操作很简单,调用它容器里的元素的keyevent函数,它容器里的元素有可能是另一个gameManager、或者是一个单纯的RenderObject。如果是gameManager的话就会进行递归调用,直到是RenderObject,所以我们还要给RenderObject定义一个keyevent函数,像这样:

    class RenderObject(pygame.sprite.DirtySprite) :

    … …

    def keyevent(self, keypressed) :

    pass

    我们把keyevent定义在基类RenderObject中,并且用pass表示它什么都不做,然后等着子类去实现它。

    class Player(RenderObject) :

    … …

    def keyevent(self, keypressed) :

    left_or_right = keypressed[K_RIGHT] - keypressed[K_LEFT]

    up_or_down = keypressed[K_DOWN] - keypressed[K_UP]

    self.move(left_or_right, up_or_down)

    Player是RenderObject的一个子类,我们可以用以上的语句实现Player的行走,keypressed其实是pygame.key.get_pressed(),用于获取当前键盘的某个键是否按下的映射表。

    接下来我们用之前的模块来写一个游戏的背景,利用图4-1的资源拼接出图4-2的图像,并且让它在屏幕Y轴方向进行滚屏操作。

    图 4-1

    图 4-2

    首先要明确的一点就是图片的四个方向必须是可拼接的,也就是说用9张相同的图片拼成一个九宫格,用肉眼是分辨不出它们是九张图片的,看到的是一个完整的图像。

    我们定义一个RenderBack类:

    class RenderBack(RenderObject) :

    def __init__(self, selfdata) :

    RenderObject.__init__(self, selfdata)

    do_init()

    def update(self) :

    RenderObject.update(self)

    do_update()

    首先确定做这么一个背景需要用到的数据,背景的宽高是肯定要的,而且要求它在Y方向做滚屏操作,所以这个背景的实际的高肯定要比屏幕上显示高还要长(我们假设他是图4-1图片高的整数倍),那么我们用一个四元组(x, y, z, w)来表示需要知道的数据:

    x : 背景图资源在images[]的索引号

    y : 生成图的宽

    z : 生成图的高

    w: 生成图在竖直方向的原图图块的数量

    这个四元组是作为selfdata被传到__init__函数中进行初始化的,并且可以在配置文件中修改它的值。

    因为最后的图像要进行Y方向的滚屏操作,所以我们还需要定义一个Y方向的偏移量,以便每次绘制的时候进行偏移操作。这样初始化就可以这么写:

    self.image = pygame.Surface( (int(selfdata[1]), int(selfdata[2])) )

    self.rect = self.image.get_rect()

    self.source_rect = self.image.get_rect()

    self.backlen = int(selfdata[3])

    self.backimage = RenderObject( [ selfdata[0] ] )

    self.top_offset = self.backimage.image.get_height() * self.backlen - self.rect.height

    这里需要注意的是,LayeredDirty在对DirtySprite进行绘制的时候总是对sprite.image的内容进行绘制的,所以在RenderBack里我们需要额外定义一个backimage来对原图进行缓存,然后通过循环将它blit到image上。这样RenderBack的拥有者才能找到image将它绘制出来。

    对于update函数,每一帧我们需要更新self.top_offset这个Y方向的偏移量,并且根据它更新image图像。具体实现如下:

    def update(self) :

    RenderObject.update(self)

    xstep = self.backimage.image.get_width()

    ystep = self.backimage.image.get_height()

    self.top_offset -= 3

    y_offset = self.top_offset % ystep

    for x in range(0, self.rect.width + xstep, xstep) :

    self.image.blit( self.backimage.image, (x, 0), ((0, y_offset), (xstep, ystep - y_offset) ) )

    for y in range(ystep - y_offset, self.rect.height + ystep, ystep) :

    for x in range(0, self.rect.width + xstep, xstep) :

    self.image.blit( self.backimage.image, (x, y), ((0,0), (xstep, ystep) ) )

    (xstep, ystep)是原图的尺寸,每一帧我们对self.top_offset进行自减操作(效果就是让最后的图像有一种向上延伸的感觉,也就是滚屏的效果)。

    然后我们分x方向和y方向进行讨论,因为x方向没有滚屏操作,所以只要用现有的图片填充整个宽度即可,比如原图是W=100个像素,需要填充的背景是D=250个像素,那么水平方向至少需要用到[ (D + W – 1) / W ] = 3张原图([x]表示比x小的最大整数,即取下整)。对于竖直方向,因为有滚屏操作,所以有两部分组成:第一排(从原图的y方向某个位置粘贴过来的)以及非第一排(总是粘贴原图)。将self.top_offset 对 ystep 取模,得到的是需要绘制图像的最上方在原图的偏移量,通过它可以绘制第一排,然后再调用两层for循环绘制后面几排。这样随着时间的推移,一个Y方向的滚屏效果就出来了。(未完待续)



    英雄哪里出来 2011-04-28 02:19 发表评论


沪ICP备19023445号-2号
友情链接