0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PythonAdvent Calendar 2024

Day 13

PyGameを簡易版Manimとして使う

Posted at

何故そんな事をするのか?

Manimの使い方を既に心得ている人はこの記事は読まなくていいかもしれません。これは私のようにPyGameの使い方は心得ているけどManimを学ぶ気力が無いのでPyGameの知識をそのまま活かしてアニメーションを作りたいという人向けの提案となります。

また何も動画作成に特化した事をするわけではないので、これから行うことはリアルタイムで動くPyGameプログラムでもそのまま使える知識となります。なので動画とPyGameプログラムの作り方を同時に学べるという利もあります。

描かれたフレームをffmpegに流し込むだけ?

動画生成自体は極論それだけやればできますがそれだけならわざわざ記事に取り上げる程の物ではないでしょう。取り上げた理由は私が作っているasyncライブラリを用いる事でかなり良い感じにコードを書けると思ったからです。

async/await構文の利点については多くの文献で語られているのでここでは触れませんが、とりあえずアニメーションに役立つ機能が豊富なので紹介します。

黄色い正方形が画面の端を周るアニメーション

from typing import Unpack
from functools import partial

import pygame
from pygame.colordict import THECOLORS
import asyncpygame as apg


async def main(**kwargs: Unpack[apg.CommonParams]):
    pygame.init()

    screen = pygame.display.set_mode((240, 240))
    screen_rect = screen.get_rect()

    dest = pygame.Rect(0, 0, 40, 40)

    r = kwargs["executor"].register
    r(partial(screen.fill, THECOLORS["black"]), priority=0)
    r(partial(pygame.draw.rect, screen, THECOLORS["yellow"], dest), priority=0x100)
    r(pygame.display.flip, priority=0xFFFFFF00)

    anim_attrs = kwargs["clock"].anim_attrs
    while True:
        await anim_attrs(dest, right=screen_rect.right, duration=500)
        await anim_attrs(dest, bottom=screen_rect.bottom, duration=500)
        await anim_attrs(dest, left=screen_rect.left, duration=500)
        await anim_attrs(dest, top=screen_rect.top, duration=500)


if __name__ == "__main__":
    apg.run(main)

output.gif

解説

kwargs["executor"].register で登録した関数達は毎フレームpriorityの値が小さい順に呼ばれます。なので上記の例ではフレーム毎に

  1. screen.fill
  2. pygame.draw.rect
  3. pygame.display.flip

の順に呼ばれます。registerはコンテキストマネージャを返し、その __exit__()が呼ばれた時に登録を解除するのでwithブロックを用いて登録の有効範囲を視覚的に分かりやすく書き表せます。

# 小規模ではないアプリケーションではこれが主力となる書き方です
r = kwargs["executor"].register
with (
    r(func1, priority=...),
    r(func2, priority=...),
):
    ...

kwargs["clock"].anim_attrs は任意のオブジェクトの属性を時間をかけて徐々に変化させます。上の例ではwhileループ内で Rectのインスタンスであるdest

  1. 右端(right)を500msかけてscreen_rect.rightに変化させる
  2. 下端(bottom)を500msかけてscreen_rect.bottomに変化させる
  3. 左端(left)を500msかけてscreen_rect.leftに変化させる
  4. 上橋(top)を500msかけてscreen_rect.topに変化させる

としています。

動画生成

上の例では動画生成はしていません。そうしたい時はapg.run()に代えてapg.run_and_record()を用いるだけです。

if __name__ == "__main__":
    apg.run_and_record(main, outfile_options=r"-codec:v gif -lossless 1 -loop 0".split(), outfile="./output.gif")

おわりに

asyncpygameの機能はManimほど豊富ではありませんが、PyGame上で使えるものなら何でも使えるため潜在能力は高いと思っています(例えばPymunkのような物理エンジン)。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?