WheatField
WheatField

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

October 12, 2024807 words, 5 min read
Authors

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

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

简单实现

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

VGroupMobject 的子类,后者是 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_textset_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。