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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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