You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Manim动画时长异常及添加run_time后无限循环问题求助

Manim动画时长异常及添加run_time后无限循环问题求助

嘿,我来帮你拆解下这段Manim代码里的几个核心问题,以及对应的解决思路:

先贴出你的原始代码方便对照

class PendBall(Scene):
    def construct(self):
        square = Square(side_length=1.5, fill_opacity=1, fill_color=BLUE_E)
        line = Line(ORIGIN, [0, 4, 0], buff=0)
        bullet = Circle(radius=0.2, fill_color=RED, fill_opacity=1)
        bullet.shift(6*LEFT)

        self.flag = False

        def updatebullet(bullet: Mobject, dt):
            if bullet.get_x() >= square.get_left()[0] or self.flag:
                bullet_vel = -1
                self.flag = True
                self.play(Rotate(line, angle=PI/4,
                            about_point=[0, 4, 0], run_time=10),
                        Rotate(square, angle=PI/4, about_point=[0, 4, 0], run_time=10))
            else:
                bullet_vel = 1
            bullet.shift(np.array([bullet_vel, 0, 0])*dt)

        bullet.add_updater(updatebullet)
        self.play(FadeIn(square, line))
        self.play(FadeIn(bullet))
        self.wait(10)

问题1:添加run_time后无限循环

这是因为你在updater函数里调用了self.play()——updater是每一帧都会执行的回调,而self.play()本身会阻塞并处理动画帧。当bullet触发旋转条件后,updater会反复调用self.play()启动新的旋转动画,直接导致无限循环。

问题2:移除run_time后动画仅持续2秒

当你去掉run_time后,Rotate会默认用极短的时间完成,而updater里的逻辑又会快速切换bullet的方向,加上wait(10)没有被正确执行(因为updater的混乱逻辑打断了时间线),最终动画看起来只持续了2秒。

问题3:给FadeIn(bullet)加run_time报错ValueError: write to closed file

你先给bullet添加了updater,再播放带run_time的FadeIn动画——这会导致Mobject在被updater实时修改的同时,又在执行淡入动画,渲染时的文件写入流冲突,就抛出了这个错误。


修改后的代码及说明

class PendBall(Scene):
    def construct(self):
        square = Square(side_length=1.5, fill_opacity=1, fill_color=BLUE_E)
        line = Line(ORIGIN, [0, 4, 0], buff=0)
        bullet = Circle(radius=0.2, fill_color=RED, fill_opacity=1)
        bullet.shift(6*LEFT)

        self.flag = False

        def updatebullet(bullet: Mobject, dt):
            if not self.flag:
                # 检测bullet是否到达触发位置
                if bullet.get_x() >= square.get_left()[0]:
                    self.flag = True
                    # 触发后立即移除updater,避免重复执行
                    bullet.remove_updater(updatebullet)
                    # 外部播放旋转动画,不和updater冲突
                    self.play(
                        Rotate(line, angle=PI/4, about_point=[0,4,0], run_time=10),
                        Rotate(square, angle=PI/4, about_point=[0,4,0], run_time=10)
                    )
                else:
                    bullet.shift(np.array([1, 0, 0])*dt)
            else:
                # 旋转触发后,bullet反向移动
                bullet.shift(np.array([-1, 0, 0])*dt)

        # 先播放静态元素的淡入
        self.play(FadeIn(square, line))
        # 先完成bullet的淡入动画,再添加updater
        self.play(FadeIn(bullet, run_time=1))
        bullet.add_updater(updatebullet)
        
        # 延长等待时间,确保旋转动画(10秒)+ 后续移动都能完成
        self.wait(12)

关键改动点

  1. 把旋转动画移出updater:当bullet到达触发位置后,先移除updater,再在外部播放旋转动画,彻底避免重复调用self.play()导致的无限循环。
  2. 调整动画顺序:先完成bullet的淡入动画,再添加updater,解决了动画执行时Mobject状态冲突的问题,也就不会再出现文件写入错误。
  3. 逻辑拆分:明确区分bullet的正向移动、触发旋转、反向移动三个阶段,让时间线更清晰,确保动画时长符合预期。

备注:内容来源于stack exchange,提问作者SirMrpirateroberts

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 12:29:54