Repository: ManimCommunity/manim

Issue:
Opengl animations issue when finishing transform
## Description of bug / unexpected behavior
<!-- Add a clear and concise description of the problem you encountered. -->

When using the transform animations when `_MethodAnimation.finish` is called in transform.py it fails as `self.methods` is not iterable in opengl. I am wondering if it could be related to #2665 ?

## Expected behavior
<!-- Add a clear and concise description of what you expected to happen. -->

`finish()` should handle both opengl and cairo renderers


## How to reproduce the issue
<!-- Provide a piece of code illustrating the undesired behavior. -->

<details><summary>Code for reproducing the problem</summary>

```py
from manim import *

class MovingAround(Scene):
    def construct(self):
        square = Square(color=BLUE, fill_opacity=1)

        self.play(square.animate.shift(LEFT))
        self.play(square.animate.set_fill(ORANGE))
        self.play(square.animate.scale(0.3))
        self.play(square.animate.rotate(0.4))
```

</details>


## Additional media files
<!-- Paste in the files manim produced on rendering the code above. -->

<details><summary>Images/GIFs</summary>

<!-- PASTE MEDIA HERE -->

</details>


## Logs
<details><summary>Terminal output</summary>
<!-- Add "-v DEBUG" when calling manim to generate more detailed logs -->

```
Manim Community v0.15.1

[04/16/22 20:43:38] DEBUG    Hashing ...                                                                                                                                                                               hashing.py:343
                    DEBUG    Hashing done in 0.008151 s.                                                                                                                                                               hashing.py:355
                    DEBUG    Hash generated :  3182407478_4060609487_3403807856                                                                                                                                        hashing.py:358
                    DEBUG    List of the first few animation hashes of the scene: ['3182407478_4060609487_3403807856']                                                                                                  caching.py:59
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮                                                                                                                                 
│                                                                                                  │
│ /home/ryan/PycharmProjects/manim/manim/cli/render/commands.py:100 in render                      │
│                                                                                                  │
│    97 │   │   │   while keep_running:                                                            │
│    98 │   │   │   │   for SceneClass in scene_classes_from_file(file):                           │
│    99 │   │   │   │   │   scene = SceneClass(renderer)                                           │
│ ❱ 100 │   │   │   │   │   rerun = scene.render()                                                 │
│   101 │   │   │   │   │   if rerun or config["write_all"]:                                       │
│   102 │   │   │   │   │   │   renderer.num_plays = 0                                             │
│   103 │   │   │   │   │   │   continue                                                           │
│ /home/ryan/PycharmProjects/manim/manim/scene/scene.py:222 in render                              │
│                                                                                                  │
│    219 │   │   """                                                                               │
│    220 │   │   self.setup()                                                                      │
│    221 │   │   try:                                                                              │
│ ❱  222 │   │   │   self.construct()                                                              │
│    223 │   │   except EndSceneEarlyException:                                                    │
│    224 │   │   │   pass                                                                          │
│    225 │   │   except RerunSceneException as e:                                                  │
│                                                                                                  │
│ /home/ryan/PycharmProjects/manim/playground.py:7 in construct                                    │
│                                                                                                  │
│    4 │   def construct(self):                                                                    │
│    5 │   │   square = Square(color=BLUE, fill_opacity=1)                                         │
│    6 │   │                                                                                       │
│ ❱  7 │   │   self.play(square.animate.shift(LEFT))                                               │
│    8 │   │   self.play(square.animate.set_fill(ORANGE))                                          │
│    9 │   │   self.play(square.animate.scale(0.3))                                                │
│   10 │   │   self.play(square.animate.rotate(0.4))                                               │
│                                                                                                  │
│ /home/ryan/PycharmProjects/manim/manim/scene/scene.py:1012 in play                               │
│                                                                                                  │
│   1009 │   │                                                                                     │
│   1010 │   │   """                                                                               │
│   1011 │   │   start_time = self.renderer.time                                                   │
│ ❱ 1012 │   │   self.renderer.play(self, *args, **kwargs)                                         │
│   1013 │   │   run_time = self.renderer.time - start_time                                        │
│   1014 │   │   if subcaption:                                                                    │
│   1015 │   │   │   if subcaption_duration is None:                                               │
│                                                                                                  │
│ /home/ryan/PycharmProjects/manim/manim/utils/caching.py:63 in wrapper                            │
│                                                                                                  │
│   60 │   │   │   "List of the first few animation hashes of the scene: %(h)s",                   │
│   61 │   │   │   {"h": str(self.animations_hashes[:5])},                                         │
│   62 │   │   )                                                                                   │
│ ❱ 63 │   │   func(self, scene, *args, **kwargs)                                                  │
│   64 │                                                                                           │
│   65 │   return wrapper                                                                          │
│   66                                                                                             │
│                                                                                                  │
│ /home/ryan/PycharmProjects/manim/manim/renderer/opengl_renderer.py:442 in play                   │
│                                                                                                  │
│   439 │   │   │   self.animation_elapsed_time = scene.duration                                   │
│   440 │   │                                                                                      │
│   441 │   │   else:                                                                              │
│ ❱ 442 │   │   │   scene.play_internal()                                                          │
│   443 │   │                                                                                      │
│   444 │   │   self.file_writer.end_animation(not self.skip_animations)                           │
│   445 │   │   self.time += scene.duration                                                        │
│                                                                                                  │
│ /home/ryan/PycharmProjects/manim/manim/scene/scene.py:1185 in play_internal                      │
│                                                                                                  │
│   1182 │   │   │   │   break                                                                     │
│   1183 │   │                                                                                     │
│   1184 │   │   for animation in self.animations:                                                 │
│ ❱ 1185 │   │   │   animation.finish()                                                            │
│   1186 │   │   │   animation.clean_up_from_scene(self)                                           │
│   1187 │   │   if not self.renderer.skip_animations:                                             │
│   1188 │   │   │   self.update_mobjects(0)                                                       │
│                                                                                                  │
│ /home/ryan/PycharmProjects/manim/manim/animation/transform.py:364 in finish                      │
│                                                                                                  │
│   361 │   │   super().__init__(mobject)                                                          │
│   362 │                                                                                          │
│   363 │   def finish(self) -> None:                                                              │
│ ❱ 364 │   │   for method, method_args, method_kwargs in self.methods:                            │
│   365 │   │   │   method.__func__(self.mobject, *method_args, **method_kwargs)                   │
│   366 │   │   super().finish()                                                                   │
│   367                                                                                            │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
TypeError: cannot unpack non-iterable method object

Process finished with exit code 1


```

<!-- Insert screenshots here (only when absolutely necessary, we prefer copy/pasted output!) -->

</details>


## System specifications

<details><summary>System Details</summary>

- OS (with version, e.g., Windows 10 v2004 or macOS 10.15 (Catalina)):
- RAM:
- Python version (`python/py/python3 --version`):
- Installed modules (provide output from `pip list`):
```
PASTE HERE
```
</details>

<details><summary>LaTeX details</summary>

+ LaTeX distribution (e.g. TeX Live 2020):
+ Installed LaTeX packages:
<!-- output of `tlmgr list --only-installed` for TeX Live or a screenshot of the Packages page for MikTeX -->
</details>

<details><summary>FFMPEG</summary>

Output of `ffmpeg -version`:

```
PASTE HERE
```
</details>

## Additional comments
<!-- Add further context that you think might be relevant for this issue here. -->



Discussion:
This was certainly my PR, apologies! But why is `self.methods` constructed differently here? I thought I checked for that and didn't see any opengl-specific code?

Oh wait, while typing this I remembered: the implementation of the animation builder is duplicated for `OpenGLMobject`, isn't it? The same modification I've made to the cairo version should be applied to the OpenGL one.

Resolving patch:
diff --git a/manim/mobject/opengl/opengl_mobject.py b/manim/mobject/opengl/opengl_mobject.py
--- a/manim/mobject/opengl/opengl_mobject.py
+++ b/manim/mobject/opengl/opengl_mobject.py
@@ -2729,7 +2729,6 @@ def __call__(self, **kwargs):
 
     def __getattr__(self, method_name):
         method = getattr(self.mobject.target, method_name)
-        self.methods.append(method)
         has_overridden_animation = hasattr(method, "_override_animate")
 
         if (self.is_chaining and has_overridden_animation) or self.overridden_animation:
@@ -2747,6 +2746,7 @@ def update_target(*method_args, **method_kwargs):
                     **method_kwargs,
                 )
             else:
+                self.methods.append([method, method_args, method_kwargs])
                 method(*method_args, **method_kwargs)
             return self
 


Test patch:
diff --git a/tests/test_scene_rendering/opengl/test_play_logic_opengl.py b/tests/test_scene_rendering/opengl/test_play_logic_opengl.py
--- a/tests/test_scene_rendering/opengl/test_play_logic_opengl.py
+++ b/tests/test_scene_rendering/opengl/test_play_logic_opengl.py
@@ -101,3 +101,19 @@ def test_t_values_save_last_frame(using_temp_opengl_config):
     scene.update_to_time = Mock()
     scene.render()
     scene.update_to_time.assert_called_once_with(1)
+
+
+def test_animate_with_changed_custom_attribute(using_temp_opengl_config):
+    """Test that animating the change of a custom attribute
+    using the animate syntax works correctly.
+    """
+
+    class CustomAnimateScene(Scene):
+        def construct(self):
+            vt = ValueTracker(0)
+            vt.custom_attribute = "hello"
+            self.play(vt.animate.set_value(42).set(custom_attribute="world"))
+            assert vt.get_value() == 42
+            assert vt.custom_attribute == "world"
+
+    CustomAnimateScene().render()