Pygameで滑らかな楕円を描くには?draw.ellipse以外の代替手法

2025-05-27

基本的な使い方は以下の通りです。

pygame.draw.ellipse(surface, color, rect, width=0)

それぞれの引数について説明します。

  • width (オプション): 楕円の線の太さを指定する整数です。
    • width0 の場合(デフォルト)、楕円は指定された色で塗りつぶされます(中が詰まった楕円)。
    • width0 より大きい場合、その値が楕円の線の太さ(アウトライン)となります。中身は塗りつぶされません。
    • width0 より小さい場合、何も描画されません。
  • rect: 楕円の位置と寸法を定義する Rect オブジェクト、または4つの整数(x座標、y座標、幅、高さ)のタプル(例: (x, y, width, height))です。この rect で指定された四角形の中に楕円が内接するように描画されます。つまり、楕円の中心は rect の中心に、楕円の幅と高さは rect の幅と高さによって決まります。
  • color: 楕円の色を指定します。これはRGB値のタプル(例: (255, 0, 0) で赤)か、pygame.Color オブジェクトで指定できます。アルファ値(透明度)を含めることも可能です。
  • surface: 楕円を描画する対象となる Surface オブジェクトです。通常は pygame.display.set_mode() で作成したメインのディスプレイサーフェスを指定します。

戻り値:

この関数は、変更されたピクセルのバウンディングボックス(境界線)を表す Rect オブジェクトを返します。これは、描画された楕円が占める最小の四角形の領域です。

:

import pygame

pygame.init()

# 画面の設定
screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Pygame draw.ellipseの例")

# 色の定義
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill(WHITE) # 画面を白で塗りつぶす

    # 塗りつぶされた楕円を描画
    # (x, y, width, height) = (50, 50, 200, 100) の四角形に内接する赤色の楕円
    pygame.draw.ellipse(screen, RED, (50, 50, 200, 100))

    # 太さのあるアウトラインの楕円を描画
    # (x, y, width, height) = (300, 150, 150, 250) の四角形に内接する緑色の楕円、線の太さ5
    pygame.draw.ellipse(screen, GREEN, (300, 150, 150, 250), 5)

    # 別のアウトラインの楕円
    # (x, y, width, height) = (500, 50, 100, 200) の四角形に内接する青色の楕円、線の太さ2
    pygame.draw.ellipse(screen, BLUE, (500, 50, 100, 200), 2)

    pygame.display.flip() # 画面を更新

pygame.quit()

このコードでは、白い背景に3つの異なる楕円を描画しています。最初の楕円は塗りつぶされ、残りの2つは線の太さが指定されたアウトラインの楕円です。



pygame.draw.ellipse に関連する一般的なエラーとトラブルシューティング

    • 画面更新の忘れ: 最も一般的な原因は、描画後に pygame.display.update() または pygame.display.flip() を呼び出していないことです。Pygameは、描画コマンドを実行してもすぐに画面に反映されるわけではありません。すべての描画が完了した後に、これらの関数を呼び出して画面を更新する必要があります。

      # 誤った例 (楕円が表示されない)
      # pygame.draw.ellipse(screen, RED, (50, 50, 100, 50))
      # ... 他の処理 ...
      
      # 正しい例
      pygame.draw.ellipse(screen, RED, (50, 50, 100, 50))
      pygame.display.flip() # または pygame.display.update()
      
    • 描画順序の問題: 画面を塗りつぶす screen.fill() などのコマンドを、楕円の描画後に実行している場合、楕円はすぐに上書きされて見えなくなります。描画は常に背景から手前へ、という順序で行う必要があります。

      # 誤った例 (楕円が背景に隠れる)
      pygame.draw.ellipse(screen, RED, (50, 50, 100, 50))
      screen.fill(WHITE) # これで楕円が消える
      
      # 正しい例
      screen.fill(WHITE) # まず背景を塗りつぶす
      pygame.draw.ellipse(screen, RED, (50, 50, 100, 50))
      pygame.display.flip()
      
    • rect 引数の誤り: rect 引数は (x, y, width, height) のタプルまたは pygame.Rect オブジェクトですが、これらの値が画面の外に出ていたり、幅や高さが小さすぎたりすると、期待通りに表示されません。

      • widthheight0 または負の値の場合、楕円は描画されません。
      • 座標が画面の範囲外である場合、もちろん見えません。デバッグのために、いったん画面中央に大きな楕円を描いてみて、表示されるか確認すると良いでしょう。
    • 色の指定ミス: 背景色と同じ色を指定している場合、楕円は背景に溶け込んで見えなくなります。また、RGB値の範囲(0-255)を間違えている場合、予期しない色になることがあります。

  1. TypeError (引数の型が間違っている)

    • color 引数: color はRGB値のタプル(例: (255, 0, 0))である必要があります。個別の整数を渡したり、文字列を渡したりすると TypeError が発生します。

      # 誤った例
      # pygame.draw.ellipse(screen, 255, (50, 50, 100, 50)) # int型を渡している
      # pygame.draw.ellipse(screen, "red", (50, 50, 100, 50)) # 文字列を渡している
      
      # 正しい例
      pygame.draw.ellipse(screen, (255, 0, 0), (50, 50, 100, 50))
      
    • rect 引数: rect(x, y, width, height) の4つの整数を含むタプルか pygame.Rect オブジェクトである必要があります。要素数が異なったり、整数以外の型(例: 浮動小数点数)を渡したりすると TypeError が発生します。

      # 誤った例 (rectの要素数が間違っている)
      # pygame.draw.ellipse(screen, RED, (50, 50, 100))
      # pygame.draw.ellipse(screen, RED, (50, 50.5, 100, 50)) # 浮動小数点数
      
      # 正しい例
      pygame.draw.ellipse(screen, RED, (50, 50, 100, 50))
      # または
      my_rect = pygame.Rect(50, 50, 100, 50)
      pygame.draw.ellipse(screen, RED, my_rect)
      
  2. パフォーマンスの問題 (ラグ)

    • 描画コードがイベントループ内にある: ゲームループのイベント処理部分(for event in pygame.event.get(): の中)に描画コードを置くと、イベントが発生するたびに描画が行われ、パフォーマンスが大幅に低下する可能性があります。描画コードはイベントループの外、メインのゲームループ内で、画面更新の直前に配置する必要があります。

      # 誤った例 (イベントループ内で描画)
      while running:
          for event in pygame.event.get():
              if event.type == pygame.QUIT:
                  running = False
              # ここで描画すると、イベントが発生するたびに描画が走り重くなる
              # pygame.draw.ellipse(screen, RED, (50, 50, 100, 50))
      
          # 正しい場所はここ
          screen.fill(WHITE)
          pygame.draw.ellipse(screen, RED, (50, 50, 100, 50))
          pygame.display.flip()
      
    • 過剰な描画: 多数の複雑な形状を毎フレーム描画すると、Pygameの描画処理が追いつかず、フレームレートが低下することがあります。

      • 必要ないものを描画しない。
      • 静的な背景などは一度描画しておき、変更があった場合のみ再描画する。
      • 複雑な描画が必要な場合は、事前に別の Surface に描画しておき、それを blit する(描画を一度に転送する)方が効率的な場合があります。
  3. Pygameの初期化忘れ

    • pygame.init() を呼び出す前に pygame.draw.ellipse などの Pygame 関数を使用すると、エラーが発生することがあります。常にプログラムの冒頭で pygame.init() を呼び出すようにしてください。

      # 誤った例
      # screen = pygame.display.set_mode((800, 600))
      # pygame.draw.ellipse(screen, RED, (50, 50, 100, 50))
      # pygame.init() # 初期化が遅い
      
      # 正しい例
      import pygame
      pygame.init()
      screen = pygame.display.set_mode((800, 600))
      pygame.draw.ellipse(screen, RED, (50, 50, 100, 50))
      
  • 公式ドキュメントを参照: Pygameの公式ドキュメントは非常に充実しています。関数の使い方や引数の詳細を確認するのに最適です。
  • 座標系の理解: Pygameの座標系は、左上が (0, 0) で、X座標は右へ、Y座標は下へ増加します。数学で習う座標系とはY軸の方向が逆なので注意が必要です。
  • 変数の中身を確認: print() デバッグを使って、surfacecolorrect などの引数に正しい値が渡されているかを確認します。
  • コードの簡素化: 問題が発生している部分だけを切り出し、最小限のコードで再現を試みます。これにより、問題の原因を絞り込むことができます。
  • エラーメッセージをよく読む: Pygameのエラーメッセージは、問題の特定に非常に役立ちます。TypeErrorAttributeError など、どのようなエラーが発生しているかを確認し、メッセージに書かれている内容を理解しようと努めましょう。


基本的な楕円の描画

最も基本的な例です。画面中央に塗りつぶされた赤い楕円を描画します。

import pygame

# Pygameの初期化
pygame.init()

# 画面設定
screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("基本的な楕円")

# 色の定義 (RGB)
RED = (255, 0, 0)
WHITE = (255, 255, 255)

# ゲームループ
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 画面を白で塗りつぶす (毎フレームクリア)
    screen.fill(WHITE)

    # 楕円の描画
    # rect引数: (x, y, width, height)
    # 画面中央付近に幅200、高さ100の楕円
    pygame.draw.ellipse(screen, RED, (300, 250, 200, 100))

    # 画面の更新
    pygame.display.flip()

# Pygameの終了
pygame.quit()

解説

  1. pygame.init(): Pygameのモジュールを初期化します。これはPygameを使用する上で必須です。
  2. pygame.display.set_mode(): ゲームウィンドウを作成し、その Surface オブジェクトを screen に格納します。
  3. pygame.display.set_caption(): ウィンドウのタイトルを設定します。
  4. RED, WHITE: 楕円の色と背景色をRGB形式で定義します。
  5. while running:: ゲームのメインループです。この中でイベント処理、描画、画面更新が行われます。
  6. screen.fill(WHITE): 毎フレーム、画面を白で塗りつぶすことで、前フレームの描画を消去します。これにより、動きのある描画が可能になります。
  7. pygame.draw.ellipse(screen, RED, (300, 250, 200, 100)):
    • screen: 描画対象の Surface
    • RED: 楕円の色。
    • (300, 250, 200, 100): 楕円が内接する四角形を定義します。
      • 300: 四角形の左上X座標。
      • 250: 四角形の左上Y座標。
      • 200: 四角形の幅(楕円の横方向の長さ)。
      • 100: 四角形の高さ(楕円の縦方向の長さ)。
  8. pygame.display.flip(): 描画された内容を実際に画面に表示します。

アウトライン(線の太さ)を指定した楕円の描画

width 引数を指定することで、塗りつぶされた楕円ではなく、線の太さを持った楕円を描画できます。

import pygame

pygame.init()

screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("アウトライン楕円")

GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill(WHITE)

    # 緑色で太さ3ピクセルのアウトライン楕円
    pygame.draw.ellipse(screen, GREEN, (50, 50, 150, 250), 3)

    # 青色で太さ1ピクセルのアウトライン楕円
    pygame.draw.ellipse(screen, BLUE, (250, 100, 300, 150), 1)

    pygame.display.flip()

pygame.quit()

解説

  • pygame.draw.ellipse(screen, GREEN, (50, 50, 150, 250), 3): 最後の引数 3 が線の太さを表します。この値が 0 でない限り、楕円は塗りつぶされずにアウトラインとして描画されます。

pygame.Rect オブジェクトを使用した描画

rect 引数には、タプルだけでなく pygame.Rect オブジェクトも使用できます。これにより、オブジェクト指向的な管理がしやすくなります。

import pygame

pygame.init()

screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Rectオブジェクトで楕円")

ORANGE = (255, 165, 0)
PURPLE = (128, 0, 128)
WHITE = (255, 255, 255)

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill(WHITE)

    # pygame.Rectオブジェクトを作成
    rect1 = pygame.Rect(50, 50, 200, 100)
    pygame.draw.ellipse(screen, ORANGE, rect1)

    # Rectオブジェクトを直接生成して使用
    pygame.draw.ellipse(screen, PURPLE, pygame.Rect(300, 200, 150, 250), 4)

    pygame.display.flip()

pygame.quit()

解説

  • pygame.draw.ellipse(screen, ORANGE, rect1): 作成した rect1 オブジェクトを rect 引数として渡します。
  • rect1 = pygame.Rect(50, 50, 200, 100): pygame.Rect クラスのインスタンスを作成します。これは、四角形の位置とサイズを表現するためのオブジェクトです。

マウスの動きに合わせて楕円を描画

インタラクティブな例として、マウスカーソルの位置に基づいて楕円を動かしてみましょう。

import pygame

pygame.init()

screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("マウスで楕円")

CYAN = (0, 255, 255)
BLACK = (0, 0, 0)

# 楕円の固定サイズ
ellipse_width = 100
ellipse_height = 80

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill(BLACK) # 背景を黒にする

    # マウスの現在位置を取得
    mouse_x, mouse_y = pygame.mouse.get_pos()

    # 楕円の中心をマウスの位置に合わせるようにrectを計算
    # 楕円の左上X座標 = マウスX - 楕円幅 / 2
    # 楕円の左上Y座標 = マウスY - 楕円高さ / 2
    ellipse_x = mouse_x - ellipse_width // 2
    ellipse_y = mouse_y - ellipse_height // 2

    # 楕円を描画
    pygame.draw.ellipse(screen, CYAN, (ellipse_x, ellipse_y, ellipse_width, ellipse_height))

    pygame.display.flip()

pygame.quit()

解説

  • ellipse_x = mouse_x - ellipse_width // 2: 描画される楕円の中心がマウスカーソルの位置になるように、rect の左上座標を計算しています。// 2 は整数除算です。
  • pygame.mouse.get_pos(): マウスカーソルの現在のX, Y座標を取得します。

複数の楕円を描画し、それぞれを異なる速度で動かすことで簡単なアニメーションを作成できます。

import pygame
import random

pygame.init()

screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("複数の動く楕円")

# 色の定義
COLORS = [
    (255, 0, 0),      # 赤
    (0, 255, 0),      # 緑
    (0, 0, 255),      # 青
    (255, 255, 0),    # 黄色
    (255, 0, 255),    # マゼンタ
    (0, 255, 255),    # シアン
]
BLACK = (0, 0, 0)

# 楕円のリストを生成
ellipses = []
for _ in range(10): # 10個の楕円
    x = random.randint(0, screen_width - 50)
    y = random.randint(0, screen_height - 50)
    width = random.randint(30, 80)
    height = random.randint(30, 80)
    color = random.choice(COLORS)
    speed_x = random.choice([-2, -1, 1, 2])
    speed_y = random.choice([-2, -1, 1, 2])
    ellipses.append({'rect': pygame.Rect(x, y, width, height),
                     'color': color,
                     'speed_x': speed_x,
                     'speed_y': speed_y})

clock = pygame.time.Clock() # フレームレート制御用

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill(BLACK)

    # 各楕円を更新して描画
    for ellipse_data in ellipses:
        # 位置を更新
        ellipse_data['rect'].x += ellipse_data['speed_x']
        ellipse_data['rect'].y += ellipse_data['speed_y']

        # 画面端での跳ね返り
        if ellipse_data['rect'].left < 0 or ellipse_data['rect'].right > screen_width:
            ellipse_data['speed_x'] *= -1
        if ellipse_data['rect'].top < 0 or ellipse_data['rect'].bottom > screen_height:
            ellipse_data['speed_y'] *= -1

        # 楕円を描画
        pygame.draw.ellipse(screen, ellipse_data['color'], ellipse_data['rect'])

    pygame.display.flip()
    clock.tick(60) # 1秒間に60フレームに制限

pygame.quit()
  • clock.tick(60): ゲームのフレームレートを毎秒60フレームに制限し、アニメーション速度を一定に保ちます。
  • if ellipse_data['rect'].left < 0 or ellipse_data['rect'].right > screen_width:: 画面の左右の端にぶつかったら、X方向の速度を反転させ、跳ね返りを表現します。Y方向も同様です。
  • ellipse_data['rect'].x += ellipse_data['speed_x']: 楕円のX座標を速度分だけ変化させ、移動させます。
  • random.randint()random.choice(): 楕円の初期位置、サイズ、色、移動速度をランダムに決定するために使用しています。
  • ellipses リスト: 複数の楕円のデータを辞書として格納しています。各辞書は rectpygame.Rectオブジェクト)、colorspeed_xspeed_y を持ちます。


pygame.gfxdraw モジュールを使用する

pygame.gfxdrawは、Pygameに同梱されている描画機能よりも高度な図形描画機能を提供するモジュールです。特に**アンチエイリアシング(ギザギザを滑らかにする処理)**が必要な場合に非常に有用です。

gfxdrawモジュールには、aaellipse(アンチエイリアス楕円)とfilled_ellipse(塗りつぶし楕円)の関数があります。アンチエイリアスされた塗りつぶし楕円を描くには、通常、これら2つの関数を組み合わせて使用します。

import pygame
import pygame.gfxdraw # gfxdrawモジュールをインポート

pygame.init()

screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("gfxdrawでアンチエイリアス楕円")

# 色の定義
RED = (255, 0, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill(WHITE)

    # 標準のpygame.draw.ellipse (ギザギザが見える場合がある)
    pygame.draw.ellipse(screen, RED, (50, 50, 200, 100))

    # gfxdrawでアンチエイリアス楕円 (線の滑らかさが向上)
    # 塗りつぶされたアンチエイリアス楕円を描くには、通常このように2回呼び出す
    # 線の描画 (aaellipse)
    pygame.gfxdraw.aaellipse(screen, 400, 150, 100, 50, BLUE)
    # 塗りつぶし (filled_ellipse)
    pygame.gfxdraw.filled_ellipse(screen, 400, 150, 100, 50, BLUE)

    # gfxdrawの引数は、中心座標と半径であることに注意
    # aaellipse(surface, x_center, y_center, radius_x, radius_y, color)
    # filled_ellipse(surface, x_center, y_center, radius_x, radius_y, color)

    pygame.display.flip()

pygame.quit()

gfxdrawのメリット・デメリット

  • デメリット:
    • pygame.drawに比べて、引数の指定方法が異なる(rectではなく中心と半径)。
    • 塗りつぶされたアンチエイリアス図形を描画するには、aa*filled_*の2つの関数を呼び出す必要がある場合が多い。
    • pygame.drawよりも描画が遅くなる可能性がある(特に多数の図形の場合)。
  • メリット:
    • アンチエイリアスされた滑らかな図形を描画できる。
    • より低レベルな描画オプションを提供し、特定のピクセル操作に柔軟性がある。

画像ファイル(スプライト)を使用する

あらかじめ画像編集ソフトウェア(Photoshop, GIMPなど)で楕円の画像を作成し、それをPygameにロードして表示する方法です。

import pygame

pygame.init()

screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("画像で楕円")

WHITE = (255, 255, 255)

# 楕円の画像をロード (事前に "ellipse.png" というファイルを作成しておく必要があります)
# 例: GIMPなどで背景透明の赤い楕円を作成し、PNG形式で保存
try:
    ellipse_image = pygame.image.load("ellipse.png").convert_alpha()
except pygame.error as e:
    print(f"Error loading image: {e}")
    print("Please create an 'ellipse.png' file in the same directory.")
    pygame.quit()
    exit()

# 画像のサイズを取得
image_width, image_height = ellipse_image.get_size()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill(WHITE)

    # 画像を画面中央に描画
    # blitの第二引数は、画像の左上座標
    screen.blit(ellipse_image, ((screen_width - image_width) // 2, (screen_height - image_height) // 2))

    pygame.display.flip()

pygame.quit()

画像ファイルのメリット・デメリット

  • デメリット:
    • 楕円のサイズや色を動的に変更する場合、毎回画像を生成し直すか、複数の画像を用意する必要があり、管理が複雑になる。
    • 単純な図形にはオーバーヘッドが大きい。
  • メリット:
    • 高品質なグラフィック(アンチエイリアス、グラデーション、テクスチャなど)を簡単に実現できる。
    • 一度ロードすれば、何度も再利用できるため、静的な図形や複雑な図形には効率が良い。
    • アーティストがデザインしたものを直接ゲームに組み込める。

ピクセル単位で手動で描画する (低レベル)

これは非常に低レベルな方法であり、通常は推奨されませんが、Pygameの描画の仕組みを理解するためや、非常に特殊な描画アルゴリズムを実装する場合に検討できます。楕円の数学的な方程式を利用して、個々のピクセルを直接設定します。

import pygame
import math

pygame.init()

screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("ピクセル単位で楕円")

BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)

# 楕円の中心と半径
center_x, center_y = screen_width // 2, screen_height // 2
radius_x = 150
radius_y = 75

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill(BLACK)

    # 楕円のピクセルを計算して描画 (簡略化されたアルゴリズム)
    # これは完全な楕円を描くための効率的なアルゴリズムではありませんが、概念を示す
    for angle in range(0, 361):
        rad_angle = math.radians(angle)
        x = int(center_x + radius_x * math.cos(rad_angle))
        y = int(center_y + radius_y * math.cos(rad_angle)) # 楕円のY軸はcosまたはsinで制御
        # 一般的な楕円の式は x = cx + a*cos(t), y = cy + b*sin(t)
        # 上記のコードはY座標の計算が誤っている。以下が正しい。
        y = int(center_y + radius_y * math.sin(rad_angle)) # 正しいY座標

        # 画面内に収まるようにクリッピング
        if 0 <= x < screen_width and 0 <= y < screen_height:
            screen.set_at((x, y), YELLOW) # 個々のピクセルを設定

    pygame.display.flip()

pygame.quit()

ピクセル単位描画のメリット・デメリット

  • デメリット:
    • 非常に複雑で、実装が難しい。
    • 非常に遅く、ほとんどの用途で非効率的。
    • アンチエイリアシングなどを自分で実装する必要がある。
  • メリット:
    • 理論上、最も柔軟性が高く、独自の描画アルゴリズムを完全に制御できる。
    • 非常に特殊な効果や、ピクセルレベルのカスタマイズが必要な場合に有効。

Python Imaging Library (PIL) のフォークである Pillow を使用してメモリ上に画像を生成し、それをPygameのSurfaceに変換して表示する方法です。これは、pygame.drawgfxdrawにはない高度な画像処理機能(例えば、より柔軟な図形描画オプション、フィルター、テキストレンダリングなど)を必要とする場合に特に有効です。

import pygame
from PIL import Image, ImageDraw

pygame.init()

screen_width = 800
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Pillowで楕円")

# 色の定義
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)

# Pillowで画像と描画オブジェクトを作成
# PygameのSurfaceと同じサイズの透明な画像を作成
pil_image = Image.new("RGBA", (screen_width, screen_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(pil_image)

# Pillowのellipse()関数で楕円を描画
# 引数は (x0, y0, x1, y1) で、長方形の左上と右下座標
draw.ellipse((100, 100, 300, 200), fill=GREEN, outline=(0, 128, 0), width=3)
# fill: 塗りつぶしの色
# outline: 線の色
# width: 線の太さ (Pillowの描画関数はアンチエイリアスではない場合がある)

# PIL画像をPygameのSurfaceに変換
# これは一度だけ行い、必要に応じて画像を更新して再度変換
pygame_surface = pygame.image.fromstring(pil_image.tobytes(), pil_image.size, pil_image.mode)


running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill(BLACK)

    # Pygame Surfaceとして描画
    screen.blit(pygame_surface, (0, 0))

    pygame.display.flip()

pygame.quit()
  • デメリット:
    • PillowとPygameの間でデータを変換するオーバーヘッドがある。
    • リアルタイムでの頻繁な動的な描画には向かない。静的な画像や、たまに更新される画像に適している。
    • Pygame単体よりも学習コストが増える。
  • メリット:
    • Pillowが提供する豊富な画像処理機能(フィルタリング、テキスト、より複雑な図形など)を利用できる。
    • Pygameの描画機能では難しい、高品質な静的または半静的なグラフィックを作成できる。
  • Pillowとの連携: Pygameの描画機能では難しい高度な画像処理や、より柔軟な図形描画が必要な場合。リアルタイムの動的な描画には不向き。
  • ピクセル単位描画: 非常に特殊な描画アルゴリズムを実装する場合や、Pygameの描画の仕組みを深く理解したい場合。通常は非推奨。
  • 画像ファイル(スプライト): 高品質な静的または半静的な楕円(グラデーション、テクスチャなど)が必要な場合。一度作成すれば再利用が容易。
  • pygame.gfxdraw.aaellipse/filled_ellipse: アンチエイリアスされた滑らかな楕円が必要な場合。パフォーマンスはpygame.drawより劣る可能性がある。
  • pygame.draw.ellipse: 最もシンプルで、基本的な楕円の描画に最適。速度と使いやすさのバランスが良い。