You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
7.6 KiB

4 months ago
import pygame
from pygame.surface import Surface
class Animation:
"""
动画类
"""
def __init__(self, x, y, img, dw, dh, time, loop, frame_range, frame_callback=None, done_callback=None, fps=60,
need_blend=False, **kwargs):
"""
:param x:绘制动画的中心点不是左上角
:param y:绘制动画的中心点不是左上角
:param img:序列帧图片
:param dw:单位宽度
:param dh:单位高度
:param time:动画播放时长,单位毫秒
:param loop:否是循环播放
:param frame_range:动画帧范围,第一帧为0比如[0,3]为4帧 [1,3]为3帧
:param frame_callback:帧回调
:param done_callback:完成回调
"""
self.x = x
self.y = y
self.img = img
self.time = time
self.need_blend = need_blend
# 一共多少帧
self.frame_range = frame_range
self.frame = frame_range[1] - frame_range[0] + 1
self.dw = dw
self.dh = dh
self.speed = int(time / self.frame)
self.loop = loop
self.least_once = False # 动画是否至少播放了一次
self.frame_callback = frame_callback
self.done_callback = done_callback
# 精灵图的行和列
self.row = int(self.img.get_height() / self.dh)
self.col = int(self.img.get_width() / self.dw)
self.current_frame = self.frame_range[0] # 当前帧
self.frame_count = int(self.speed / (1000 / fps)) # 需要等待几次主循环才切换1帧
self.current_count = self.frame_range[0] * self.frame_count # 当前计数
if self.frame_count < 1:
self.frame_count = 1
def update(self):
last_frame = self.current_frame
self.current_count += 1
self.current_frame = int(self.current_count / self.frame_count)
if self.current_frame > self.frame_range[1]:
# 播放完成
self.current_frame = self.frame_range[0]
self.current_count = self.frame_range[0] * self.frame_count
self.least_once = True
# 帧回调
if self.frame_callback:
self.frame_callback(last_frame)
# 完成回调
if self.done_callback:
self.done_callback(last_frame)
else:
# 播放中
# 每个动画帧回调,而不是每个主循环回调
if last_frame != self.current_frame and self.frame_callback:
self.frame_callback(last_frame)
def draw(self, surface):
"""
绘图,把x,y作为中心点绘制
:param surface: 目标surface
"""
dest_x = self.x - self.dw / 2
dest_y = self.y - self.dh / 2
cell_x = self.current_frame % self.col
cell_y = int(self.current_frame / self.col)
self.draw_cell(surface, self.img, dest_x, dest_y, cell_x, cell_y, self.dw, self.dh)
def blend_draw(self, surface, blend_type):
"""
带alpha混合的draw
"""
dest_x = self.x - self.dw / 2
dest_y = self.y - self.dh / 2
cell_x = self.current_frame % self.col
cell_y = int(self.current_frame / self.col)
self.draw_cell(surface, self.img, dest_x, dest_y, cell_x, cell_y, self.dw, self.dh, blend_type)
def draw_src(self, surface, x, y):
"""
绘图把xy作为左上角绘制
"""
cell_x = self.current_frame % self.col
cell_y = int(self.current_frame / self.col)
self.draw_cell(surface, self.img, x, y, cell_x, cell_y, self.dw, self.dh)
def reset(self):
"""
重置动画
"""
self.current_frame = 0
self.current_count = 0
self.least_once = False
@staticmethod
def draw_cell(dest, source, x, y, cell_x, cell_y, cell_w=32, cell_h=32, blend_type=0):
"""
绘制精灵图中指定x,y的图像
:param dest: surface类型要绘制到的目标surface
:param source: surface类型来源surface
:param x: 绘制图像在dest中的坐标
:param y: 绘制图像在dest中的坐标
:param cell_x: 在精灵图中的格子坐标
:param cell_y: 在精灵图中的格子坐标
:param cell_w: 单个精灵的宽度
:param cell_h: 单个精灵的高度
:return:
"""
dest.blit(source, (x, y), (cell_x * cell_w, cell_y * cell_h, cell_w, cell_h), special_flags=blend_type)
class Animator:
"""
动画管理类
"""
def __init__(self, surface):
"""
:param surface: 动画显示的目标surface(一般是窗口surface)
"""
self.surface = surface
self.animations = [] # 存储着当前的动画
def update(self):
"""
动画管理类逻辑更新
"""
for animation in self.animations[::-1]:
animation.update()
# 动画播放完成,删除动画
if not animation.loop and animation.least_once:
self.animations.remove(animation)
def draw(self):
"""
渲染各个动画
"""
for animation in self.animations:
if animation.need_blend:
animation.blend_draw(self.surface, pygame.BLEND_ADD)
else:
animation.draw(self.surface)
def add(self, x, y, img, dw, dh, time, loop, frame_range, frame_callback=None, done_callback=None):
"""
添加一个动画
:param x:绘制动画的中心点不是左上角
:param y:绘制动画的中心点不是左上角
:param img:序列帧图片
:param dw:单位宽度
:param dh:单位高度
:param time:动画播放时长单位毫秒
:param loop:否是循环播放
:param frame_range:动画帧范围
:param frame_callback:帧回调
:param done_callback:完成回调
"""
animation = Animation(x, y, img, dw, dh, time, loop, frame_range, frame_callback, done_callback)
self.animations.insert(0, animation)
self.animations.sort(key=lambda obj: obj.y)
def add_ani(self, *animations):
for ani in animations:
self.animations.insert(0, ani)
self.animations.sort(key=lambda obj: obj.y)
def clear(self):
"""
清空所有动画
"""
self.animations = []
class Fade:
"""
淡出淡入
"""
def __init__(self, dest):
self.sw = False # 开关,是否启动淡入淡出
self.callback = None # 回调函数
self.state = 0 # 当前状态
self.speed = 5
self.alpha = 0
self.dest = dest
self.surface = Surface((640, 480))
self.surface.fill((0, 0, 0))
self.surface.set_alpha(self.alpha)
def logic(self):
if not self.sw:
return
if self.state == 0: # 第一阶段,淡出
self.alpha += self.speed
if self.alpha >= 255:
self.alpha = 255
if self.callback:
self.callback()
self.state = 1
elif self.state == 1: # 第二阶段,淡入
self.alpha -= self.speed
if self.alpha <= 0:
self.sw = False
self.surface.set_alpha(self.alpha)
def draw(self):
if not self.sw:
return
self.dest.blit(self.surface, (0, 0))
def start(self, callback=None):
self.reset()
self.sw = True
self.callback = callback
def reset(self):
self.state = 0 # 当前状态
self.alpha = 0