前言
在pygame游戏开发专栏中的第一篇文章(一)增量时间的利用中讲解了利用增量时间可以使玩家移动速度不受帧率的影响。
这里播放帧动画同样需要利用增量时间,将游戏的人物行为如行走、待机等动作截取若干帧,利用增量时间不同时刻显示不同的帧。
这篇文章只是说明播放角色行走、待机等动作,如果玩家使用工具的话,这篇文章的方法不能够解决。使用工具的时候,动画不能够一直播放,实现的思路是可以添加定时器,按键按下时候激活定时器,到一定时间失能定时器就可以实现,具体代码在下一篇文章中讲解。
素材准备
正常游戏角色移动需要向上、下、左、右移动,需要相应的动画,如下图所示展示了向右移动时角色的若干帧。
GIF动图如图所示
pygame中播放动画
现在需要在pygame中去播放动画,需要将不同帧截取保存,下面是素材存放示例
这里的12张图片对应上面的角色动作,现在需要用代码实现在不同时刻绘制不同的图片。
实现思路及代码讲解
初始化
def import_image(path):
"""
导入图像并转换为surface对象
:param path: 文件夹路径
:return: 返回surface列表
"""
surface_list = []
# 获取path路径下的所有文件名并转换为surface对象
for _, __, img_files in walk(path):
for image in img_files:
full_path = path + '/' + image
surface = pygame.image.load(full_path).convert_alpha()
# 调整图像大小,因为这里图形比较小,放大演示更加明显
surface_width, surface_height = surface.get_size()
surface = pygame.transform.scale(surface, (2*surface_width, 2*surface_height))
surface_list.append(surface) # 添加到列表中
return surface_list
# 玩家动作字典
self.player_actions = {
'idle': import_image('../images/characters/idle'), # 玩家闲置
'walk': import_image('../images/characters/walk') # 行走
}
self.player_action = 'idle' # 玩家初始的状态
self.player_action_index = 0 # 玩家动作列表索引
self.image = self.player_actions[self.player_action][self.player_action_index]
self.rect = self.image.get_rect()
self.rect.center = (SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2) # 起始将玩家置于屏幕中心
这里使用一个字典存放角色的对应动作,如果获取到键盘输入将self.player_action即角色的初始状态改变至玩家动作字典相应的键即可。
播放动画
def animate(self, dt):
"""根据玩家行为动作切换动作帧"""
self.player_action_index += 4 * dt # 根据时间增量不断累加
# 如果超过玩家动作列表范围,重新置于第一帧
if self.player_action_index >= len(self.player_actions[self.player_action]):
self.player_action_index = 0
self.image = self.player_actions[self.player_action][int(self.player_action_index)]
# 将图像绘制在屏幕上
self.display_surface.blit(self.image, self.rect)
播放动画利用增量时间,因为每次dt很小,需要移动的时间才能切换到下一张图片,然后将图像绘制在屏幕上
完整代码
player.py
import pygame
from settings import *
from os import walk
def import_image(path):
"""
导入图像并转换为surface对象
:param path: 文件夹路径
:return: 返回surface列表
"""
surface_list = []
# 获取path路径下的所有文件名并转换为surface对象
for _, __, img_files in walk(path):
for image in img_files:
full_path = path + '/' + image
surface = pygame.image.load(full_path).convert_alpha()
# 调整图像大小,因为这里图形比较小,放大演示更加明显
surface_width, surface_height = surface.get_size()
surface = pygame.transform.scale(surface, (2*surface_width, 2*surface_height))
surface_list.append(surface) # 添加到列表中
return surface_list
class Player:
def __init__(self):
# 获取显示界面
self.display_surface = pygame.display.get_surface()
# 玩家设置
self.width, self.height = 20, 20
self.speed = 200 # 玩家速度
# 玩家移动方向
self.direction = pygame.math.Vector2()
# 玩家动作字典
self.player_actions = {
'idle': import_image('../images/characters/idle'), # 玩家闲置
'walk': import_image('../images/characters/walk') # 行走
}
self.player_action = 'idle' # 玩家初始的状态
self.player_action_index = 0 # 玩家动作列表索引
self.image = self.player_actions[self.player_action][self.player_action_index]
self.rect = self.image.get_rect()
self.rect.center = (SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2) # 起始将玩家置于屏幕中心
def input(self):
"""获取键盘输入"""
keys = pygame.key.get_pressed()
self.player_action = 'idle' # 如果未获取键盘输入则将玩家置于空闲状态
# ↑ and ↓
if keys[pygame.K_UP] or keys[pygame.K_w]:
self.direction.y = -1
elif keys[pygame.K_DOWN] or keys[pygame.K_s]:
self.direction.y = 1
else:
self.direction.y = 0
# ← and →
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
self.direction.x = 1
self.player_action = 'walk'
elif keys[pygame.K_LEFT] or keys[pygame.K_a]:
self.direction.x = -1
else:
self.direction.x = 0
def move(self, dt):
"""玩家移动"""
# 如果方向向量长度大于0才执行相关动作
if self.direction.magnitude() > 0:
self.direction = self.direction.normalize() # 归一化
self.rect.x += self.direction.x * self.speed * dt
self.rect.y += self.direction.y * self.speed * dt
def animate(self, dt):
"""根据玩家行为动作切换动作帧"""
self.player_action_index += 4 * dt # 根据时间增量不断累加
# 如果超过玩家动作列表范围,重新置于第一帧
if self.player_action_index >= len(self.player_actions[self.player_action]):
self.player_action_index = 0
self.image = self.player_actions[self.player_action][int(self.player_action_index)]
# 将图像绘制在屏幕上
self.display_surface.blit(self.image, self.rect)
def update(self, dt):
"""更新玩家相关行为"""
self.input()
self.move(dt)
self.animate(dt)
level.py
import pygame
from player import Player
class Level:
def __init__(self):
# 获取显示界面
self.display_surface = pygame.display.get_surface()
self.display_surface_rect = self.display_surface.get_rect()
self.player = Player()
def run(self, dt):
self.display_surface.fill('gray') # 填充白色
self.player.update(dt)
settings.py
# 屏幕宽度、屏幕高度
SCREEN_WIDTH, SCREEN_HEIGHT = 1200, 800
# 帧率
FPS = 90
mian.py
import pygame, sys
from level import Level
from settings import *
class Game:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('测试') # 设置标题
self.clock = pygame.time.Clock() # 创建时钟对象
self.level = Level()
def run(self):
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
dt = self.clock.tick(FPS) / 1000 # 计算自上次调用到现在经过的秒数
self.level.run(dt)
pygame.display.flip()
if __name__ == '__main__':
game = Game()
game.run()