前言

在平常的学习、刷视频时,一些科普或算法视频中经常会看到一些非常炫酷的动画效果,例如:

一些复杂数学公式和数学图像:

fig1.png

又或者一些简单的数学推理或物理动画:

还有B站大佬编写的 dijkstra 算法和本文即将带大家实现的冒泡排序的演示动画

甚至还可以实现 3D 效果

更多的案例可以参考Manim 社区

那么类似的动画都是如何实现的那?借助开源库 Manim。

Manim 是一个用于创建精确程序化动画的引擎,特别适用于制作解释性数学视频,借助它,可以实现打造动起来的数学世界和物理世界,小时候枯燥的课本冷冰冰的公式可以变得活能活现,理解不通的算法题让它动起来,形象的告诉背后的原理。

Manim 构建演示视频的功能特别强大,生成的质量也特别高,唯一的缺点 Manim 程序需要借助 python 来进行实现,如果大家从未学过或很少使用 python,会存在一定的门槛。但是也不必慌,当下有 MarsCode、Cursor、Windsurf等诸多 AI 编程助手,只要会合理的运用AI编程助手,任何编程都阻挡不了我们,Manim 也一样。

在本文中:

带领大家使用 MarsCode 编写 Manim 案例,边生成代码,边学习收获实现一个冒泡排序的演示动画一起体验一下如何与 MarsCode 进行交互,以及遇到问题又该如何处理Manim 快速入门

fig5.png

首先带大家使用 MarsCode 编写一下 Manim 官方提供快速入门中的案例,初步的熟悉一下 Manim,同样也建立起对 MarsCode 使用的基本思路。使用编程助手切记要注意的事项:不要太粗暴的描述需求,要尽可能的细致,在最开始的时候,不知道如何写提示词,可以借助豆包啊、gpt来辅助生成。

快速入门中,案例演示了一个圆的绘制过程,在强调一下,使用 MarsCode 时,不要一次性输入太多信息,尽可能的描述清晰。在这个准则的基础上,输入:帮我绘制一个pink颜色,透明度为0.5的圆,展示绘制过程,使用manim程序进行实现。

fig6.png

可以使用 MarsCode 提供的命令行代码执行,也可以使用 Manim Slideview 插件提供的 run 图标,注意如果使用后者,需要在刚才的代码里面补充 scene 的绘制

from manim import *
class DrawCircle(Scene):
    def construct(self):
        # 创建一个圆
        circle = Circle(color=PINK, fill_opacity=0.5)
        # 将圆添加到场景中
        self.add(circle)
        self.wait(1)
# 需要补充
scene = DrawCircle()
scene.render()

fig7.png

上述生成的视频是没有达到要求的,没有体现出绘制过程,继续交互:没有体现出圆的绘制过程。

from manim import *
class DrawCircle(Scene):
    def construct(self):
        # 创建一个圆
        circle = Circle(color=PINK, fill_opacity=0.5)
        # 使用Create动画来展示圆的绘制过程
        self.play(Create(circle))
        self.wait(1)
scene = DrawCircle()
scene.render()

实现效果如下:

本着不学白不学的想法,继续想 MarsCode 输入指令:帮我解释一下所写代码。

fig9.png

增加动画的难度,继续交互:增加一些需求,最开始先绘制一个square,然后变换成pink圆,最后圆淡出。

from manim import *
class DrawCircle(Scene):
    def construct(self):
        # 创建一个正方形
        square = Square(color=BLUE, fill_opacity=0.5)
        self.add(square)
        self.wait(1)
        # 将正方形变换成圆
        circle = Circle(color=PINK, fill_opacity=0.5)
        self.play(Transform(square, circle))
        self.wait(1)
        # 圆淡出
        self.play(FadeOut(circle))
        self.wait(1)
scene = DrawCircle()
scene.render()

最后的圆是没有淡出,这会是什么问题那,没事,交给 MarsCode 来解决,这里顺便删除掉中间转换的等待:不需要中间的wait,并且圆没有淡出效果

from manim import *
class DrawCircle(Scene):
    def construct(self):
        # 创建一个正方形
        square = Square(color=BLUE, fill_opacity=0.5)
        self.add(square)
        # 将正方形变换成圆
        circle = Circle(color=PINK, fill_opacity=0.5)
        self.play(Transform(square, circle))
        # 圆淡出
        self.play(FadeOut(circle))
scene = DrawCircle()
scene.render()

可以看到,没能成功实现 circle 淡出。一般这种情形下,建议是可以尝试再次和 MarsCode 进行交互,如果依旧未达成需求,一种可能 MarsCode 陷入了自证循环,就得想办法略过这个话题;另一种可能是使用的专业名词有误。

这种情况下可以借助其他的大模型来辅助,当时借助了豆包模型(不小心删除了会话,找不到了),这个问题有点离谱,如果自己独立解决的话,需要一定的 Manim 基础,还好有豆包。

在官方文档中,有这样一段话,大意是:基于 transform 方法进行的变化,其实物体本质没变,只是属性变了。通俗讲,给 MarsCode 的提示词应该是 square 淡出。

fig11.png

from manim import *
class DrawCircle(Scene):
    def construct(self):
        # 创建一个正方形
        square = Square(color=BLUE, fill_opacity=0.5)
        self.add(square)
        # 将正方形变换成圆
        circle = Circle(color=PINK, fill_opacity=0.5)
        self.play(Transform(square, circle))
        # 正方形淡出
        self.play(FadeOut(square))
scene = DrawCircle()
scene.render()

比如后续又增加了 square 的旋转操作,类似的一步一步的写 MarsCode 提示词即可,最终获得的代码如下:

from manim import *
class DrawCircle(Scene):
    def construct(self):
        # 创建一个正方形
        square = Square(color=BLUE, fill_opacity=0.5)
        # 使用Create动画来展示正方形的绘制过程
        self.play(Create(square))
        # 旋转正方形
        self.play(Rotate(square, angle=PI/4))
        # 将正方形变换成圆
        circle = Circle(color=PINK, fill_opacity=0.5)
        self.play(Transform(square, circle))
        # 正方形淡出
        self.play(FadeOut(square))
        # 圆颜色渐变
        self.play(circle.animate.set_color(PINK))
scene = DrawCircle()
scene.render()

炫酷动画视频_炫酷动画ppt作品展示_

通过这部分快速入门案例的训练,大致可以总结出下面要点

冒泡排序演示动画准备过程

下面咱们来一起做一个复杂案例:冒泡排序演示动画,具体效果上面已经展示过了,冒泡排序演示动画的核心流程:

首先尽可能清晰的输入指令:

fig14.png

from manim import *
import random
class BubbleSort(Scene):
    def construct(self):
        # 生成随机数组
        arr = [random.randint(1, 20) for _ in range(10)]
        # 创建矩形并初始化位置
        rectangles = []
        for i, num in enumerate(arr):
            rect = Rectangle(width=0.5, height=num/10)
            rect.align_to(RIGHT, DOWN)
            rect.shift(LEFT * i)
            rectangles.append(rect)
        # 将矩形添加到场景中
        self.add(*rectangles)
        self.wait(1)
        # 冒泡排序
        for i in range(len(arr)):
            for j in range(0, len(arr) - i - 1):
                if arr[j] > arr[j + 1]:
                    arr[j], arr[j + 1] = arr[j + 1], arr[j]
                    rectangles[j].become(rectangles[j + 1])
                    rectangles[j + 1].become(rectangles[j])
                    self.wait(0.1)
        self.wait(2)
scene = BubbleSort()
scene.render()

当时运行出结果后,直接人就有点昏啊,后来总结来看,其实就是需求还是不够清晰,一次性输入了太多的要求,切描述的不够清晰。

fig15.png

没办法,自己生成的代码,再苦也要咽下去,想办法尝试是否有优化空间,于是进行了一系列交互

fig16.png

fig17.png

可以发现,矩形位置是偏移的,一个好的构图,核心内容应该处于中间,反复沟通了多轮,下面可以见证一下破防时刻(这还只是部分,太惨了)

这里的问题其实很明显,因为完全不懂 Manim,使用的提示词非常不专业,导致 Marscode 错误理解,由于记忆性上下文的存在,也就是已经先入为主了,AI 目前没有这种跳出能力,陷入了僵局。

那这时候怎么办呐?理智的想法就是借助其他大模型进行辅助。于是又借助了豆包模型:要确保创建的矩形位于页面中间,你可以在创建矩形后,将它们作为一个整体进行居中对齐。

MarsCode 进行如下修改,实现需求。

fig18.png

# 将矩形添加到场景中
rectangles_group = VGroup(*rectangles)
rectangles_group.arrange(RIGHT, aligned_edge=DOWN)
rectangles_group.move_to(ORIGIN)
self.add(rectangles_group)
self.wait(1)

fig19.png

实现到这里,冒泡排序基本工作就完成,下面进入核心的动画部分。

核心流程

元素的交换又踩了一个大坑,顺序进行了下面的交互,结果是一塌糊涂啊。

多次尝试依旧失败,那么解决方案就很单一了,help me,豆包。给豆包描述一下当前场景,豆包给出一个示例代码,喂给 Marscode。

def cyc_move(self,vm1,vm2):
    vm1.generate_target()
    vm1.target.next_to(vm2,ORIGIN,aligned_edge=DOWN)
    vm2.generate_target()
    vm2.target.next_to(vm1,ORIGIN,aligned_edge=DOWN)
    self.play(MoveToTarget(vm1),MoveToTarget(vm2))
    self.wait()

冒泡排序的核心功能就实现了,后续又做了一些样式调整,这里就不赘述了,贴一下最后的完整代码。

from manim import *
import random
class BubbleSort(Scene):
    def construct(self):
        # 生成随机数组
        arr = [random.randint(1, 20) for _ in range(10)]
        # 创建矩形并初始化位置
        rectangles = []
        colors = [BLUE, GREEN, RED, YELLOW, PURPLE]  # 定义一个颜色列表
        for i, num in enumerate(arr):
            # 为每个矩形赋予一个唯一的颜色
            color = colors[i % len(colors)]  # 循环使用颜色列表中的颜色
            rect = Rectangle(width=1, height=num/5, fill_color=color, fill_opacity=0.8)
            rect.align_to(RIGHT, DOWN)
            rect.shift(LEFT * i)
            # 将数组元素的值渲染到矩形的中心
            text = Text(str(num), font_size=24, color=WHITE)
            text.next_to(rect, DOWN, buff=0.1)
            rectangles.append(VGroup(rect, text))
        # 将矩形添加到场景中
        rectangles_group = VGroup(*rectangles)
        rectangles_group.arrange(RIGHT, aligned_edge=DOWN)
        rectangles_group.move_to(ORIGIN)
        self.add(rectangles_group)
        self.wait(1)
        # 冒泡排序
        for i in range(len(arr)):
            for j in range(0, len(arr) - i - 1):
                # 对正在比较的元素添加放大缩小的呼吸效果
                self.play(ApplyMethod(rectangles[j].scale, 1.2), ApplyMethod(rectangles[j + 1].scale, 1.2), run_time=0.3)
                self.play(ApplyMethod(rectangles[j].scale, 1/1.2), ApplyMethod(rectangles[j + 1].scale, 1/1.2), run_time=0.3)
                if arr[j] > arr[j + 1]:
                    self.wait(0.2)
                    arr[j], arr[j + 1] = arr[j + 1], arr[j]
                    # 交换矩形的位置,但不改变它们的颜色
                    rectangles[j], rectangles[j + 1] = rectangles[j + 1], rectangles[j]
                    self.wait(0.1)
                    # 交换矩形的显示位置,并保持在同一水平线上
                    self.cyc_move(rectangles[j], rectangles[j + 1])
        self.wait(2)
    def cyc_move(self, vm1, vm2):
        vm1.generate_target()
        vm1.target.next_to(vm2, ORIGIN, aligned_edge=DOWN)
        vm2.generate_target()
        vm2.target.next_to(vm1, ORIGIN, aligned_edge=DOWN)
        # 在移动过程中不再添加呼吸效果
        self.play(MoveToTarget(vm1), MoveToTarget(vm2), run_time=0.2)
# 这里不需要传入 Scene 实例
scene = BubbleSort()
scene.render()

选择排序与冒泡排序有一定的相似性,于是做一个尝试,是否此时可以顺遂的完成:根据上述的思路,你能为我写一个选择排序的演示动画吗

整体实现其实是对的,下面的动画看起来有点怪,因为当时在冒泡排序的时候,约定了比较元素呼吸的顺序,微调一下即可。

接着又尝试了一下插入排序,插入排序思想与冒泡差距很大,没有办法推理得出,得出的演示动画非常不符合预期。总体 MarsCode 的生成过程还是非常让人满意的,毕竟光给一句笼统的实现插入排序提示词有些过分简易。

总结

本文借助 MarsCode 生成 Manim 程序,完成了快速入门案例和冒泡/选择排序演示动画,文章完整的展示整体使用 Marscode 的一些方法和过程,有一些心得,下面粗略的总结一下:

借助 MarsCode,可以降低程序的实现难度,下一个想要尝试使用 MarsCode + 豆包实现一个小米 su7 的展示,使用 Threejs 知识实现,敬请期待。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。