'Asynchronously run different animations in manim

I'm trying run essentially two animations (ref. following code):

class RelTrain(Scene):
    def construct(self):
        train = Rectangle(height=1, width=4)
        train2 = Rectangle(height=2, width=2)
        train.move_to(np.array([-10,0,0]))
        train2.move_to(np.array([0,0,0]))
        self.add(train, train2)
        self.play(
            train.move_to, np.array([10,0,0]),
            train2.move_to, np.array([15,0,0]),
            run_time=18,
            rate_func=linear,
        )
        self.wait()

Essentially two rectangles are moving, but I do not want them to begin movement simultaneously. I want train to start moving, and after 2 seconds (train would still be moving at this point since run_time=18), I want train2 to pop up on the screen and begin its motion. I'm not sure how this is done and would appreciate any help.



Solution 1:[1]

After playing for a while, I've figured how to do this with ManimCE (v0.3.0). This is not very well documented yet, but essentially you can use mobject updaters. I'm not sure if this is the best way to do this (it seems to me that is too much verbose and low level), but it works:

Code


import numpy as np
from manim import *

class DeplayedTrains(Scene):
    def construct(self):
        # create both trains
        trains = (
            Rectangle(height=1, width=4),
            Rectangle(height=2, width=2),
        )
        
        # indicate start and end points
        start_points_X, end_points_X = ((-5, 0), (5, 5))
        # compute movement distances for both trains
        distances = (
            (end_points_X[0] - start_points_X[0]),
            (end_points_X[1] - start_points_X[1]),
        )
        
        # place trains at start points and add to the scene
        for train, start_point in zip(trains, start_points_X):
            train.move_to(np.array([start_point, 0, 0]))
            self.add(train)
        
        # deifine durations of movements for both trains, get FPS from config
        durations, fps = ((5, 3), config["frame_rate"])
        
        # create updaters
        updaters = (
            # add to the current position in X the difference for each frame, 
            # given the distance and duration defined
            lambda mobj, dt: mobj.set_x(mobj.get_x() + (distances[0] / fps / durations[0])),
            lambda mobj, dt: mobj.set_x(mobj.get_x() + (distances[1] / fps / durations[1])),
        )
        
        # add updaters to trains objects, movement begins
        trains[0].add_updater(updaters[0])
        # wait 2 seconds
        self.wait(2)
        
        # start the movement of the second train and wait 3 seconds
        trains[1].add_updater(updaters[1])
        self.wait(3)
        
        # remove the updaters
        trains[0].clear_updaters()  # you can also call trains[0].remove_updater(updaters[0])
        trains[1].clear_updaters()

Output

Solution 2:[2]

For those looking to making animations start at different times while still partially temporally overlapping, take a look at LaggedStart and similar things:

https://docs.manim.community/en/stable/reference/manim.animation.composition.LaggedStart.html#manim.animation.composition.LaggedStart

https://docs.manim.community/en/stable/reference/manim.animation.composition.html

Unfortunately they are not documented, but it took me a while to realize they even existed.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Emerson Peters