找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 54|回复: 1

[教程] 时间切片的帧动画动态可视组件

[复制链接]
发表于 3 天前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 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

粉身碎骨浑不怕,要留答辩在人间





评分

参与人数 1活力 +300 干货 +3 收起 理由
烈林凤 + 300 + 3 感谢分享!

查看全部评分

发表于 3 天前 来自手机 | 显示全部楼层
老马好强!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号|苏公网安备 32092302000068号 )

GMT+8, 2025-9-1 12:58 , Processed in 0.051714 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表