「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」
本系列专栏将通过不断编写游戏的方式,带你夯实 Python 知识。
本专栏追求迅速掌握 pygame 的同时,夯实 python 知识,所以一起来吧。
Sprite 模块、Sprite 对象
精灵类与精灵对象。首先要看的是精灵类中提供了哪些属性与方法。
print(dir(pygame.sprite))
复制代码
['AbstractGroup', 'DirtySprite', 'Group', 'GroupSingle', 'LayeredDirty', 'LayeredUpdates', 'OrderedUpdates', 'Rect', 'RenderClear', 'RenderPlain', 'RenderUpdates', 'Sprite', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'collide_circle', 'collide_circle_ratio', 'collide_mask', 'collide_rect', 'collide_rect_ratio', 'from_surface', 'get_ticks', 'groupcollide', 'pygame', 'spritecollide', 'spritecollideany', 'truth']
复制代码
提供的内容并不多,下文要重点学习的就是 Sprite 类。
查阅该类的属性与方法,清单如下:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'add', 'add_internal', 'alive', 'groups', 'kill', 'layer', 'remove', 'remove_internal', 'update']
复制代码
列举一下重点方法(默认提供的方法确实不多,在使用中更多的需要自行扩展)
update()
:适用于控制 sprite 对象行为的方法。基类中该方法没有任何实现,需要重写该方法;add()
:将该 sprite 对象添加到 group 中;remove()
:sprite 对象会从 group 中删掉;kill()
:将该 sprite 对象从所有 groups 中删掉;groups()
:返回 sprite 对象所在的所有组;alive()
:判断 sprite 方法是否还在组中。
完成精灵图动态切换
先看最终效果,在进行代码学习。
使用的素材图片如下,你可以直接保存到本地,或者自行去互联网检索精灵图资源。
精灵图是将多张图片合成到一张图片上,编写代码时不用在逐个加载图片,优点是加载图片速度快。
精灵图资源,可以在如下网址下载:
www.aigei.com/game2d/
www.6pea.com/
www.6m5m.com/
www.spriters-resource.com/
opengameart.org/
openclipart.org/
精灵图在使用的时候,可以进行部分区域的显示,代码如下:
import sys
import pygame
pygame.init()
pygame.display.set_caption("精灵图局部展示")
screen = pygame.display.set_mode([500, 500])
clock = pygame.time.Clock()
fps = 60
my_sprite = pygame.image.load('./imgs/guofurong.png').convert_alpha()
while True:
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill((12, 200, 180))
screen.blit(my_sprite, (100, 100))
screen.blit(my_sprite, (100, 100), (0, 0, 128, 192))
screen.blit(my_sprite, (300, 100), (0, 0, 32, 48))
screen.blit(my_sprite, (300, 200), (0, 48, 32, 48))
pygame.display.update()
复制代码
screen.blit(my_sprite, (100, 100), (0, 0, 128, 192))
blit 存在三个参数,分别是图像,绘制的坐标点,绘制的截面。这个截面相当于拿着一个透明的玻璃在图片上滑动,透明部分可以展示出来。
pygame.display.set_mode()
该函数的原型如下,默认情况该建议只传递第一个参数,即宽和高即可,第三个参数操作系统会自动选择最优的参数,一般不进行设置。第二个参数表示的是显示类型,不设置的情况下会展示默认窗口,可设置的在原型后面描述。
set_mode(size=(0, 0), flags=0, depth=0, display=0, vsync=0) -> Surface
复制代码
flags 参数的值
该参数有如下选型,多个值的时候使用 |
分割。
pygame.FULLSCREEN
:全屏展示;pygame.DOUBLEBUF
:双缓冲模式,推荐和HWSURFACE
或者OPENGL
一起使用;pygame.OPENGL
:创建一个 OPENGL 渲染窗口;pygame.HWSURFACE
:硬件加速,在全屏时有效;pygame.RESIZABLE
:窗口可调整尺寸;pygame.NOFRAME
:窗口没有边框和控制按钮(最大化,最小化,关闭)。
利用精灵图让精灵动起来
核心代码如下,重点部分用注释进行解释。
import pygame
# 声明一个类继承自 Sprite
class MySprite(pygame.sprite.Sprite):
def __init__(self):
# 调用基类的构造方法
pygame.sprite.Sprite.__init__(self)
self.image = None
self.master_image = None
self.rect = None
self.frame = 0
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.master_image = self.master_image.subsurface((0, 0, 128, 48))
# 获取目标区域的宽度和高度
self.frame_width = width
self.frame_height = height
# 获取矩形区域
self.rect = 0, 0, width, height
# 合计 4 张小图
self.columns = columns
rect = self.master_image.get_rect()
print(rect)
# 获取最后一帧,这里得到的是 3
# with 传递进来的是 32,合计宽度是 128 / 32
# height 传递进来的是 48,合计高度是 48
self.last_frame = rect.width // width - 1
def update(self, current_time, rate=60):
# 如果当前时间大于 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
# 获取 x 坐标,x 坐标变化规律为 0,3,64,96
frame_x = (self.frame % self.columns) * self.frame_width
# 获取 y 坐标,y 坐标在该案例中变化规律为 0
frame_y = (self.frame // self.columns) * self.frame_height
rect = (frame_x, frame_y, self.frame_width, self.frame_height)
# print(rect)
# 继续裁剪图片
self.image = self.master_image.subsurface(rect)
pygame.init()
screen = pygame.display.set_mode((300, 300))
pygame.display.set_caption("My Sprite")
rate = pygame.time.Clock()
my_sprite = MySprite()
my_sprite.load("./imgs/guofurong.png", 32, 48, 4)
groups = pygame.sprite.Group()
groups.add(my_sprite)
while True:
rate.tick(60)
ticks = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
screen.fill((44, 169, 240))
groups.update(ticks)
groups.draw(screen)
pygame.display.update()
复制代码
上述案例比较长,核心代码就是在计算每个小图所在的坐标位置。还有一点,在本案例中,我并未使用 blit()
进行绘制,而是采用的 subsurface
,这两个方法本质上的区别在于blit()
直接把图像绘制在 surface
对象表面,subsurface()
先将图像提取子表面,然后通过Sprite.draw
, 或者 Group.draw
将子表面绘制出来。
所以在本文精灵组进行绘制的使用,调用了 groups.draw(screen)
方法。
本系列专栏属于番外篇,希望你能学到新知识。
有任何疑问,都可以联系橡皮擦进行解决,一起做游戏吧
本专栏每天的练习量大概在 1 小时左右,整篇博客节奏会比较快,毕竟咱们是有基础的人。
近期评论