)
z_index是 Manim 里每个物体都有的一个数字用来定重叠时谁在上面默认是 0。mob.set_z_index(2)把它改成 2数字大的盖住数字小的可以是负数和小数。两个物体z_index相同的时候才轮到添加顺序后self.add的在上面。对VGroup调set_z_index组里每个成员都会被设上self.bring_to_front只改添加顺序压不过一个z_index更高的物体。先交代一下版本下面的代码和结论出自 Manim Community 0.21.0这是 2026 年的最新版本我们是 2026 年 10 月在上面逐条渲出画面、读重叠处的像素颜色核对的。Manim 本身是一个 Python 动画库3Blue1Brown 的数学视频用的就是它图形和公式都拿代码来摆背景知识可以看这篇介绍。极坐标⋅XYZ 上还有一套 2026 年最新的 Manim 中文视频教程这篇讲的z_index是其中一节。下面三段完整代码可以直接贴进极坐标⋅XYZ 的工作区点运行里面装的也是 Manim 0.21.0本机不用装 Python 和 LaTeX。三个图形叠在一起先不设再设一个红圆、一个青色三角、一个紫色方块位置互相重叠。先按这个顺序加进场景停一秒再给圆和三角设上z_indexfrom manim import * class WhoIsOnTop(Scene): def construct(self): circle Circle(radius1.2, colorRED).set_fill(RED, 1).move_to([-1, 0, 0]) triangle Triangle(colorTEAL).set_fill(TEAL, 1).scale(1.6) square Square(side_length2.2, colorPURPLE).set_fill(PURPLE, 1).move_to([1, 0, 0]) # 三个都没设 z_index默认 0后 add 的盖住先 add 的 self.add(circle, triangle, square) self.wait() # 不动添加顺序只改 z_index数字大的在上面 circle.set_z_index(2) triangle.set_z_index(1) self.wait()前一秒里方块在最上面三角压着圆。我们在画面上取了三个点看颜色圆和三角重叠的地方是青色三角和方块重叠的地方是紫色三个图形都盖到的那一点也是紫色。后一秒整个反过来。同样三个点颜色变成了红、青、红圆在最上面三角居中方块到了最底下。这中间没有重新add也没有把谁移走只改了两个数字。set_z_index可以在add之前调也可以像上面这样在add之后调下一帧就按新的数字画。数字不一定是正整数我们试过 0.5 和 -1 都认。背后只有一次排序Manim 0.21.0 画每一帧之前会把场景里的东西整理成一张表这段逻辑在manim/utils/family.py里去掉无关的部分大致是extracted_mobjects remove_list_redundancies( list(it.chain(*(m.get_family() for m in mobjects))), ) if use_z_index: return sorted(extracted_mobjects, keylambda m: m.z_index)第一步是摊平。mobjects是场景里的物体按添加顺序排着。每个物体连同它的成员、成员的成员Manim 管这叫 family依次展开接成一张长表。一个VGroup里装着三个方块展开后就是组自己加三个方块一共四项。第二步是按z_index从小到大排序。表里靠前的先画靠后的后画后画的盖住先画的。Python 的sorted是稳定排序z_index相同的项排完之后彼此的先后和排之前一样。所以「z_index相同看添加顺序」其实是这次排序自带的结果Manim 没有为它另写规则。要注意的是排的是摊平之后的表。组在这张表里只是其中一项组里的每个成员各自带着自己的z_index参加排序不会因为同属一个组就挨在一起。我们试过这样一个场景g1 VGroup(红, 橙)g2 VGroup(蓝, 绿)只给红设了z_index 1然后self.add(g1, g2)。打印出来的绘制顺序是橙、蓝、绿、红。红是最先加进来的却画在最后压住了后加的g2。想自己看这张表在construct里加一行print([(type(m).__name__, m.z_index) for m in self.get_mobject_family_members()])后进场的底框用负数垫下去z_index最常见的用处是这种情况文字已经在画面上了后面想给它加一个实心的底框。底框是后进场的按添加顺序它在上面会把文字整个盖掉。from manim import * class BoxBehindText(Scene): def construct(self): left Text(z_index 0).move_to([-3.3, 0, 0]) right Text(z_index -1).move_to([3.3, 0, 0]) self.add(left, right) self.wait(0.5) # 两个实心底框都比文字后进场区别只在右边那个设了 z_index-1 box_l SurroundingRectangle(left, buff0.3).set_fill(BLUE_E, 1) box_r SurroundingRectangle(right, buff0.3).set_fill(BLUE_E, 1).set_z_index(-1) self.play(FadeIn(box_l), FadeIn(box_r)) self.wait()播完之后左边只剩一块黄边、深蓝底的框字看不见了。我们按 1280×720 渲出来数过左半边的白色像素底框进场前是 1764 个进场后是 0 个。右边的字还在白色像素前后是 1651 和 1658底框垫在了字的下面。背景网格、坐标系后面的色块、给公式加的高亮底色都是同一个写法给要垫底的那个东西一个负的z_index什么时候加进场景就不用管了。反过来一个标签要一直浮在所有东西上面给它一个比别人都大的数。z_index在动画进行的过程中也一直算数。上面的FadeIn淡入期间右边的字始终在框上面。我们另外试过红色方块z_index 1停着不动一个后加的蓝色方块滑过来叠到它身上重叠处从头到尾都是红色。组的 z_index设的是每个成员对一个VGroup调set_z_index(1)Manim 会把组里每个成员、成员的成员都设成 1。参数里有个familyTrue默认就是这样。这是调用那一刻做的事组不会记住「我的成员都该是 1」。之后再加进组的成员z_index还是它自己原来的值from manim import * class LateMember(Scene): def construct(self): plate RoundedRectangle(width5, height2, corner_radius0.2, colorBLUE).set_fill(BLUE_E, 1) card VGroup(plate).set_z_index(1) label Text(label) card.add(label) # 后加进组的成员z_index 还是 0 self.add(card) self.wait() card.set_z_index(1) # 再设一次这回 label 也是 1 了 self.wait()前一秒画面上只有一块蓝色的底板label这几个字被它盖住了卡片范围内一个白色像素都没有。label明明是后加进组的可它的z_index是 0底板是 1排序之后它排到了底板前面先画。后一秒字出来了因为又调了一次card.set_z_index(1)两个成员都是 1回到按组内顺序排label在后。所以往一个设过z_index的组里加东西要么加完再对整个组调一次set_z_index要么给新成员单独设。动画里什么时候换层z_index是物体身上的一个属性改了就改了没有中间值。放进动画里有两个地方和直觉不太一样。第一个是.animate.set_z_index(1)。它能用但换层发生在动画播完的时候播的过程中不变。我们让下面的红方块self.play(red.animate.set_z_index(1), run_time1)这一秒里重叠处一直是蓝色播完才跳成红色。想让它在某个时刻换层直接在两个play之间写red.set_z_index(1)更清楚.animate在这里没有额外的好处。第二个是变形。Transform(a, b)把a变成b的样子场景里留下的还是az_index也还是a原来的值b身上设的z_index带不过来。ReplacementTransform(a, b)播完后场景里换成了b从那一刻起用的是b的z_index。如果两个数不一样最后一帧会跳一下。我们试的是红方块z_index 3变成一个没设z_index的黄方块旁边有个蓝方块和它重叠变形过程中它一直在蓝方块上面颜色从红渐变到黄播完的瞬间被蓝方块盖住了。target Square(colorYELLOW).set_fill(YELLOW, 1) target.set_z_index(a.z_index) # 目标和原物体用同一个 z_index播完就不会跳 self.play(ReplacementTransform(a, target))什么时候用哪个你想做的事写法让一个物体压在所有东西上面mob.set_z_index(10)比别人都大就行让底框、背景网格垫到最底下mob.set_z_index(-1)都没设z_index把其中一个提到最上面self.bring_to_front(mob)或再self.add(mob)一次都没设z_index把其中一个压到最下面self.bring_to_back(mob)整个组一起换层group.set_z_index(1)成员都会被设上复制出来的物体mob.copy()会带着原来的z_index查现在的绘制顺序self.get_mobject_family_members()常见写错的地方用 bring_to_front 去压一个 z_index 更高的物体blue.set_z_index(1) self.add(red, blue) self.bring_to_front(red) # 红还是在蓝下面bring_to_front在 0.21.0 里的函数体只有一句self.add(*mobjects)也就是把物体重新加一次让它排到添加顺序的最后。它改的是添加顺序而添加顺序只在z_index相同的时候才起作用。蓝是 1红是 0红加多少次都在蓝下面。两个物体都没设z_index的时候bring_to_front(red)是管用的我们试过红到了上面。bring_to_back同理。add_foreground_mobject也压不过红用它加成前景物体蓝的z_index是 1重叠处还是蓝色。场景里只要有人用了z_index后面调层次就都用z_index别两套混着来。把 z_index 写在 VGroup 的括号里card VGroup(plate, label, z_index5)这样写不报错也没有效果。构造函数里的z_index5只设给了组自己plate和label还是 0。组本身没有可画的东西它排在哪都不影响画面。我们拿这样一个组和一个后加的蓝方块比蓝方块照样盖在上面打印绘制顺序两个方块的z_index都是 0只有VGroup那一项是 5。改成VGroup(plate, label).set_z_index(5)。set_z_index(5, familyFalse)和写在括号里是一个效果同样不起作用。想用 shift(OUT) 把物体抬到前面red.shift(OUT * 5)普通的Scene是从正面平着看的物体的 z 坐标不参与遮挡。我们把红方块往外挪了 5 个单位后加的蓝方块还是盖在它上面。如果手里的物体本来就带着 z 坐标想按它来定层次可以调red.set_z_index_by_z_Point3D()它会把物体中心的 z 坐标抄给z_index。上面那个红方块调完之后z_index是 5.0就到蓝方块上面了。常见追问FAQQz_index的默认值是多少能用负数和小数吗A默认是 0。负数和小数都能用我们试过set_z_index(-1)和set_z_index(0.5)都按大小正常排。想在 0 和 1 之间再插一层不用把别的数字全改一遍给 0.5 就行。Q物体已经add进场景了再调set_z_index还管用吗A管用。Manim 每画一帧都重新排一次改完之后的下一帧就按新的数字来。本文第一段代码就是先add后设的。Qalways_redraw生成的物体z_index写在哪A写在lambda里面或者外面都行。我们两种都试了always_redraw(lambda: Square().set_z_index(1))以及先always_redraw(...)再对返回的物体调set_z_index(1)播了几帧之后z_index都还是 1遮挡关系也对。Qa.become(b)会把b的z_index带过来吗A不会。我们让一个默认的方块become一个z_index 4的方块之后它的z_index还是 0。copy()是带的复制出来的物体z_index也是 4。Q3D 场景里z_index还管用吗A平面图形之间管用。我们在ThreeDScene里从正上方看两个重叠的方块红的z_index 1蓝的后加红在上面。球面、曲面这类带明暗的 3D 物体Manim 的 3D 相机还会按它们离镜头的远近再排一次那是另一套规则这篇不展开。Q能不能让 Manim 完全不看z_indexA能在construct开头写self.camera.use_z_index False之后只按添加顺序画。我们这样设了之后z_index 1的红方块被后加的蓝方块盖住了。一般用不到接手别人的场景、想先排除z_index的影响时可以临时开一下。z_index和添加顺序在教程《布局下· 尺寸 / 包围 / 编组 / 深度 实战》的第四节「深度方向布局 z_index」里有视频讲解旁边的代码可以边看边改。同一课第三节讲VGroup文字版是《Manim 怎么把一组物体排成一行或网格》。来源极坐标⋅XYZ · jizuobiao.xyz · 更新于 2026-10-06 · 基于 Manim Community 0.21.0