马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
本帖最后由 Maz马 于 2025-8-31 16:42 编辑
这是一个用动态可视组件写的帧动画播放器
[RenPy] 纯文本查看 复制代码 #用来刷新动画时间轴入点的动态可视组件
init python:
#假设,image循环时间为2,当st为2.3时切换出image,image会处于2.3的循环位置,而不是在2.3重新唤起循环,这是renpy的图像时间轴机制
#
#因此需要让image时间轴和提取动画的时间轴相对异步,可以查询动画时间切片和滑窗算法来理解
#
# 用来操作动画切片入点的全局变量
start_anim = 0.0
# 重置帧动画入点的函数
def reset_animation():
global start_anim
start_anim = 0.0
# 帧动画函数
def act_animation(st,at,frames,repeat=None):
global start_anim
# 初始化
if start_anim == 0.0:
start_anim = st # 记录传入时间轴的当前时间(类似于剪辑中的入点)
index = 0 # 帧序列
meter_time = 0.0 # 当前动画帧播放的时间
total_duration = sum(frame_time for (frame, frame_time) in frames) # 计算动画总时长
# 动画已播放时间 = 当前时间 - 动画开始时间(类似于剪辑中的出点)
frame_end_time = st - start_anim
# 如果不重复播放且动画已播放完毕
if not repeat and frame_end_time >= total_duration:
image, _ = frames[-1] # 返回最后一帧
return image, None # 返回None表示不再更新
# 如果重复播放,计算循环后的剩余时间
if repeat:
frame_end_time = frame_end_time % total_duration
# 关键难点,函数本身是动态可视组件的循环调用,这里又进行循环,因此是循环嵌套
for i,(frame,frame_time) in enumerate(frames): # 因为用的是同一个时间轴,所以frame_end_time在这个循环中是另一种含义
if frame_end_time >= frame_time: # “当前帧已播放时间”大于当前帧持有的时间时
frame_end_time = (frame_end_time - frame_time) # 减去当前帧持有的时间,相对化成下一帧的“当前帧已播放时间”,继续遍历
else: # “当前帧已播放时间”小于当前帧持有的时间时
index = i # 记录当前帧的索引
meter_time = frame_time - frame_end_time # 用当前帧持有的时间减去遍历中的“当前帧已播放时间”
break # 得到 当前帧索引 和 下一次调用这个函数的间隔
# 当再次调用这个函数时,循环块外部的frame_end_time的含义会重新初始化成“动画已播放时间”
image,_= frames[index] # 获取当前帧的图像(忽略帧时间,用_接收)
return image,meter_time # 返回 当前图像 和 下次更新时间/当前帧剩余时间
#使用方法
# 抬头使用DynamicDisplayable()动态可视组件
#
# 入参repeat=True,是否循环。frames是一个帧序列表,每个元素是一个元组 (帧的路径,这一帧播放的时间)
image mon_walk_left:
DynamicDisplayable(
act_animation,repeat=True,
frames=[
("images/anim/mon_1/walk/l0.png",0.05),
("images/anim/mon_1/walk/l1.png",0.05),
("images/anim/mon_1/walk/l2.png",0.05)
])
正题:
为什么我要写这个东西而不是使用image原生的写法,看起来有点画蛇添足?
[RenPy] 纯文本查看 复制代码 image mon_walk_left:
"images/anim/mon_1/walk/l0.png"
pause 0.1
"images/anim/mon_1/walk/l1.png"
pause 0.1
"images/anim/mon_1/walk/l2.png"
pause 0.1
repeat
这里涉及到一个时间戳的问题,是我在写横板格斗的cdd轮子时出现的
所有的可视组件都会有隐含的参数st,at,也就是可视组件被显示的时间以及持续的时间
这里简单形容它为“时间入点”和“时间出点”(pr中的概念)
image本身的接口并不允许你调整时间戳,所以当你在展示这个循环动画并切换它时(也就是没有隐藏,仅仅是不显示)
image的时间轴仍然在执行,用图表展示就是:
[RenPy] 纯文本查看 复制代码 #时间轴
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
#假设你的动画在第3帧开始显示,那么这是你动画轴的位置,1234则是你循环的动画帧
[1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,]
#假设你没有隐藏它重新显示,那么时间入点仍旧是你第一次显示这个组件的st,你只是在间断的切换它,那么实际上取得值是这样的
[1,2,3,4] [3,4,1,2] [1,2,3,4]
可以看到,并不是动画重新开始,而是你截取了循环动画的一部分。[3,4,1,2]
而花里胡哨的CDD常常以常驻的形式展示在游戏中,当然,在其他情况下也有可能出现持续显示的可视组件
因此这会导致循环动画的失效或是不匹配,而这个帧动画的动态可视组件,就是提供了重新调整st的方法(并不是实际st而是相对的调整,这是时间切片的概念)
[RenPy] 纯文本查看 复制代码 reset_animation()
当你需要调整循环动画的st时,并不需要重新唤起可视组件,而是调用这个函数,那么循环动画就会重新计算
用图表展示就是:
[RenPy] 纯文本查看 复制代码 #时间轴
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
#
[1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,]
#
[1,2,3,4] [3,4,1,2] [1,2,3,4]
#当你开始截取片段时,调用函数,会相对调整st
[1,2,3,4,1,][1,2,3,4,1,][1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,]
#此时你截取的片段为
[1,2,3,4] [1,2,3,4] [1,2,3,4]
#查看我写的更多屎
时间切片的帧动画动态可视组件
https://www.renpy.cn/forum.php?mod=viewthread&tid=1740
在回放中影响当前游戏存档数据的方法
https://www.renpy.cn/forum.php?mod=viewthread&tid=1739
她的心里话!隐藏文本!
https://www.renpy.cn/forum.php?mod=viewthread&tid=1713
能力雷达图
https://www.renpy.cn/forum.php?mod=viewthread&tid=1719
暴学CDD的十四天/附虚拟摇杆
https://www.renpy.cn/forum.php?mod=viewthread&tid=1675
简单rpg地图移动
https://www.renpy.cn/forum.php?mod=viewthread&tid=1652
摇骰子小游戏
https://www.renpy.cn/forum.php?mod=viewthread&tid=1653
猜球盅小游戏
https://www.renpy.cn/forum.php?mod=viewthread&tid=1574
FontGroup() 游戏里全局使用两种字体的方法
https://www.renpy.cn/forum.php?mod=viewthread&tid=1491
粉身碎骨浑不怕,要留答辩在人间
|