manim 101:如何用 manim 画一个 2D 防火墙

•807 words•5 min read•

近日做视频需要绘制一个防火墙,但苦于对动画制作不熟,于是用 manim (3b1b 大神做视频时用的动画库)绘制了一个简易、2D 版本。 过程中也是一边问 AI,一边测试,学了不少新知识,这里简单记录一下。

以上 gif 对应的详细代码参见 firewall.py

简单实现

实现思路很简单,通过继承 VGroup 类,在 self.submobjects 中添加若干砖块(bricks),就实现了一个简单的防火墙。

VGroup 是 Mobject 的子类,后者是 manim 中所有可渲染图形的基类,可以说是 manim 中核心的一个概念。通过 Mobject 可以把多个对象组织在一起,再把它作为一个整体进行操作(旋转、移动、变形等),大大简化复杂动画的创建。

以上 gif 中防火墙的变色、旋转、移动就是通过这个逻辑实现的。

from manim import *
import random
from manima.mobjects import MediaMobject, HandDrawnArrow


class Firewall(VGroup):
    def __init__(
        self,
        num_bricks_x=4,
        num_bricks_y=6,
        brick_width=1,
        brick_height=0.5,
        brick_spacing_x=0.1,
        brick_spacing_y=0.1,
        corner_radius=0.05,
        fill_color=BLUE,
        outline_color=WHITE,
        rotation_x=0,  # X轴旋转
        rotation_y=0,  # Y轴旋转
        rotation_z=0,  # Z轴旋转
        **kwargs
    ):
        super().__init__(**kwargs)
        self.fill_color = fill_color
        self.outline_color = outline_color

        # Create a list of all possible brick positions
        positions = [(i, j) for j in range(num_bricks_y)
                     for i in range(num_bricks_x)]

        # Shuffle the positions to randomize brick creation order
        random.shuffle(positions)

        for i, j in positions:
            text = f"{i*j}"
            brick_group = self.create_brick(
                brick_width, brick_height, corner_radius, text)
            brick_group.shift(
                RIGHT * i * (brick_width + brick_spacing_x) +
                UP * j * (brick_height + brick_spacing_y)
            )
            self.add(brick_group)

        self.center()
        self.rotate(rotation_x, axis=RIGHT)  # 绕X轴旋转
        self.rotate(rotation_y, axis=UP)     # 绕Y轴旋转
        self.rotate(rotation_z, axis=OUT)    # 绕Z轴旋转

    def create_brick(self, brick_width, brick_height, corner_radius, text):
        brick_group = VGroup()

        brick = RoundedRectangle(
            width=brick_width,
            height=brick_height,
            corner_radius=corner_radius,
            fill_color=self.fill_color,
            fill_opacity=1,
            stroke_color=self.outline_color,
            stroke_width=2
        )

        number = Text(text, font_size=24, color=WHITE)
        number.set_z_index(1)
        number.move_to(brick.get_center())

        brick_group.add(brick, number)
        return brick_group

    def set_color(self, color):
        for brick_group in self.submobjects:
            brick_group[0].set_fill(color)
        return self

    def set_text(self, text):
        for brick_group in self.submobjects:
            brick_group[1] = Text(text, font_size=24, color=WHITE)
            brick_group[1].move_to(brick_group[0].get_center())
        return self

设置属性

比如文本与颜色,这点还是挺好实现的,通过重写 set_text 和 set_color 方法即可。

但动作的设计有点难,比如我想让砖块逐个旋转到某个角度,就不是很好实现。

# not working
def rotate_brick(self, angle):
    for brick_group in self.submobjects:
        brick_group[0].rotate(angle)

manim 命令行常用参数

  • 播放选项:
    • -p 或 --play: 在成功渲染后自动播放生成的视频。
    • -a 或 --write_all: 渲染提供文件中的所有场景,而不仅仅是第一个场景。
  • 质量与分辨率选项:
    • -q 或 --quality [l|m|h|p|k]: 设置视频质量。
      • l: 低质量(480p)
      • m: 中等质量(720p)
      • h: 高质量(1080p)
      • p: 产品质量(大概是高清质量)
      • k: 4K 质量
    • -r 或 --resolution WIDTH,HEIGHT: 手动设置视频的输出分辨率。 E.g., -pqp -r 1920,1080 即输出一段高质量(60fps) 1080p 质量的视频。
  • 文件与输出设置:
    • -o 或 --output_file FILE_NAME: 自定义输出文件名。
    • --format [mp4|gif|png]: 指定输出格式,可以是视频、GIF 或静态图像。
  • 帧与速率控制:
    • --fps FPS: 设置输出视频的帧率。
    • -n 或 --from_animation_number N: 从特定的动画序号开始渲染。
  • 日志与输出控制:
    • --log_level [DEBUG|INFO|WARNING|ERROR|CRITICAL]: 设置日志输出的详细级别。
    • --progress_bar [DISPLAYED|LEAVE|HIDDEN]: 控制进度条的显示情况。
  • 其他有用选项:
    • --media_dir DIR: 设置媒体目录路径,用于存放生成的视频和图像。
    • --config_file FILE: 指定不同的配置文件运行 manim。