zoukankan      html  css  js  c++  java
  • 【python游戏编程之旅】第六篇---pygame中的Sprite(精灵)模块和加载动画

    本系列博客介绍以python+pygame库进行小游戏的开发。有写的不对之处还望各位海涵。

    直到现在我们已经学了很多pygame基础知识了,从这篇博客开始我们将会学习pygame中高级部分,更多和精灵模块,冲突检测相关的知识。

    一、Sprite模块、sprite对象

    在pygame.sprite模块里面包含了一个名为Sprite类,他是pygame本身自带的一个精灵。但是这个类的功能比较少,因此我们新建一个类对其继承,在sprite类的基础上丰富,以方便我们的使用。

    首先来了解一下如何使用sprite类来加载动画吧。

    1、精灵序列图

    将要加载的动画帧放在一个精灵序列图里面,然后在程序里面调用它。pygame会自动更新动画帧,这样一个动态的图像就会展现在我们面前了。

    下面是一个典型的精灵序列图:行和列的索引都是从0开始的。

    2、加载精灵图序列:

    在加载一个精灵图序列的时候,我们需要告知程序一帧的大小,(传入帧的宽度和高度,文件名)。

    除此之外,还需要告诉精灵类,精灵序列图里面有多少列。load函数可以加载一个精灵序列图。

    def load(self, filename, width, height, columns):
            self.master_image = pygame.image.load(filename).convert_alpha()
            self.frame_width = width
            self.frame_height = height
            self.rect = 0,0,width,height
            self.columns = columns

    3.更新帧

    一个循环动画通常是这样工作的:从第一帧不断的加载直到最后一帧,然后在折返回第一帧,并不断重复这个操作。

    self.frame += 1
                if self.frame > self.last_frame:
                    self.frame = self.first_frame
                self.last_time = current_time

    但是如果只是这样做的话,程序会一股脑地将动画播放完了,我们想让它根据时间间隔一张一张的播放,因此加入定时的代码。

    pygame中的time模块有一个get_ticks()方法可以满足定时的需要。

    ticks = pygame.time.get_ticks()

    然后将ticks变量传递给sprite的update函数,这样就可以轻松让动画按照帧速率来播放了。哦,帧速率还没有设置,咱们现在设置一下帧速率。

    启动一个定时器,然后调用tick(num)函数就可以让游戏以num帧来运行了。

    framerate = pygame.time.Clock()
    framerate.tick(60)

    4、绘制帧

    sprite.draw()方法是用来绘制帧的,但是这个函数是由精灵来自动调用的,我们没有办法重写它,因此需要在update函数里面做一些工作。

    首先需要计算单个帧左上角的x,y位置值(x表示列编号,y表示行编号):

    frame_x = (self.frame % self.columns) * self.frame_width
    #用帧数目除以行数,然后在乘上帧的高度 frame_y
    = (self.frame // self.columns) * self.frame_height

    然后将计算好的x,y值传递给位置rect属性。

    frame_x = (self.frame % self.columns) * self.frame_width
    frame_y = (self.frame // self.columns) * self.frame_height
    rect = ( frame_x, frame_y, self.frame_width, self.frame_height )
    self.image = self.master_image.subsurface(rect)

    5、精灵组

    当程序中有大量的实体的时候,操作这些实体将会是一件相当麻烦的事,那么有没有什么容器可以将这些精灵放在一起统一管理呢?答案就是精灵组。

    pygame使用精灵组来管理精灵的绘制和更新,精灵组是一个简单的容器。

    使用pygame.sprite.Group()函数可以创建一个精灵组:

    group = pygame.sprite.Group()
    group.add(sprite_one)

    精灵组也有update和draw函数:

    group.update()
    group.draw()

    二、自定义的精灵类

    好了,通过前面的学习,我们已经了解了一些精灵的知识了,现在我们将前面说到的方法封装成一个自定义的类,以方便我们的调用,这个类继承自pygame.sprite.Sprite:

     1 class MySprite(pygame.sprite.Sprite):
     2     def __init__(self, target):
     3         pygame.sprite.Sprite.__init__(self) #基类的init方法
     4         self.target_surface = target
     5         self.image = None
     6         self.master_image = None
     7         self.rect = None
     8         self.topleft = 0,0
     9         self.frame = 0
    10         self.old_frame = -1
    11         self.frame_width = 1
    12         self.frame_height = 1
    13         self.first_frame = 0
    14         self.last_frame = 0
    15         self.columns = 1
    16         self.last_time = 0
    17 
    18     def load(self, filename, width, height, columns):
    19         self.master_image = pygame.image.load(filename).convert_alpha()
    20         self.frame_width = width
    21         self.frame_height = height
    22         self.rect = 0,0,width,height
    23         self.columns = columns
    25         rect = self.master_image.get_rect()
    26         self.last_frame = (rect.width // width) * (rect.height // height) - 1
    27 
    28     def update(self, current_time, rate=60):
    29         #更新动画帧
    30         if current_time > self.last_time + rate:
    31             self.frame += 1
    32             if self.frame > self.last_frame:
    33                 self.frame = self.first_frame
    34             self.last_time = current_time
    35 
    37         if self.frame != self.old_frame:
    38             frame_x = (self.frame % self.columns) * self.frame_width
    39             frame_y = (self.frame // self.columns) * self.frame_height
    40             rect = ( frame_x, frame_y, self.frame_width, self.frame_height )
    41             self.image = self.master_image.subsurface(rect)
    42             self.old_frame = self.frame

    好了现在我们写一个小程序来测试一下这个类的性能怎么样。

    这里我用ps制作了一个简单的精灵序列图,咱们就用这个萌萌的嗷大喵好了:

    代码:

    import pygame
    from pygame.locals import *
    
    class MySprite(pygame.sprite.Sprite):
        def __init__(self, target):
            pygame.sprite.Sprite.__init__(self)
            self.target_surface = target
            self.image = None
            self.master_image = None
            self.rect = None
            self.topleft = 0,0
            self.frame = 0
            self.old_frame = -1
            self.frame_width = 1
            self.frame_height = 1
            self.first_frame = 0
            self.last_frame = 0
            self.columns = 1
            self.last_time = 0
    
        def load(self, filename, width, height, columns):
            self.master_image = pygame.image.load(filename).convert_alpha()
            self.frame_width = width
            self.frame_height = height
            self.rect = 0,0,width,height
            self.columns = columns
            rect = self.master_image.get_rect()
            self.last_frame = (rect.width // width) * (rect.height // height) - 1
    
        def update(self, current_time, rate=60):
            if current_time > self.last_time + rate:
                self.frame += 1
                if self.frame > self.last_frame:
                    self.frame = self.first_frame
                self.last_time = current_time
    
            if self.frame != self.old_frame:
                frame_x = (self.frame % self.columns) * self.frame_width
                frame_y = (self.frame // self.columns) * self.frame_height
                rect = ( frame_x, frame_y, self.frame_width, self.frame_height )
                self.image = self.master_image.subsurface(rect)
                self.old_frame = self.frame
    
    pygame.init()
    screen = pygame.display.set_mode((800,600),0,32)
    pygame.display.set_caption("精灵类测试")
    font = pygame.font.Font(None, 18)
    framerate = pygame.time.Clock()
    
    
    cat = MySprite(screen)
    cat.load("sprite.png", 100, 100, 4)
    group = pygame.sprite.Group()
    group.add(cat)
    
    while True:
        framerate.tick(30)
        ticks = pygame.time.get_ticks()
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                exit()
        key = pygame.key.get_pressed()
        if key[pygame.K_ESCAPE]:
            exit()
            
        screen.fill((0,0,100))
    
        group.update(ticks)
        group.draw(screen)
        pygame.display.update()

    效果图:萌萌的嗷大喵跃然于屏幕上。看起来功能还不错的说。

    大家也可以制作一些自己喜欢的精灵序列图,然后加载并查看他们的效果。

    关于精灵与精灵之间的冲突检测,精灵与组之间的碰撞检测,我们将会放在下个博客一起学习。

  • 相关阅读:
    【译】深入理解G1的GC日志(一)
    【译】深入理解G1的GC日志(一)
    【译】深入理解G1的GC日志(一)
    Java 8 Time Api 使用指南-珍藏限量版
    颠覆微服务认知:深入思考微服务的七个主流观点
    函数式编程让你忘记设计模式
    使用Optional摆脱NPE的折磨
    使用Optional摆脱NPE的折磨
    使用Optional摆脱NPE的折磨
    面试官:优化代码中大量的if/else,你有什么方案?
  • 原文地址:https://www.cnblogs.com/msxh/p/5013555.html
Copyright © 2011-2022 走看看