pythonのpygameでpygame.display.update(rect)で正しい範囲のみが更新されないのは仕様なのか?


マルチポストについて  
回答がいただけていないタイミングで、stockoverflowにpygameタグの投稿が少ないことに気づき回答をいただけるか不安だったため、teratailにも同様の質問をさせていただきました。  
使い慣れておらず、お手数をおかけしてしまう形にしてしまい申し訳ございません。  
先方の要約については、こちらにも回答の形で後ほど追記いたします。

<https://teratail.com/questions/6ibljaslxoffl6?_from_e=e_qctq_showq>

* * *

数時間ネットで調査しましたが、わかりませんでした。申し訳ございませんが、教えてください。

# 達成したいこと

  * 掲題の件が仕様なのか確認したい。
  * pygameで再描画範囲を限定し軽いゲームを目指す際のベストプラクティスを知りたい(「背景と該当のオブジェクトのみを描画して部分的にupdateすれば良いじゃん」ができないため)

# 何について、仕様なのか確認したいのか

pygame.display.update(rects)  
で画面の一部のみを更新しようとしたときに、rectsで指定した範囲以外の場所も更新されること。

## 例

初期処理で黒背景にして、繰り返しの中で白背景を全体描画し、青い四角形を描画し、青い四角形の部分だけupdateした場合

[![1](https://i.stack.imgur.com/Fgrc6.png)](https://i.stack.imgur.com/Fgrc6.png)

  
青い四角形が表示されるのは正しいが、白い部分は更新していないため、黒いままなのでは？

## 青い四角形を表示するコード

[code]

    import pygame
    
    # 初期化
    pygame.init()
    
    # 定数
    WIDTH, HEIGHT = 800, 600
    WHITE = (255, 255, 255)
    BLACK = (0, 0, 0)
    GREY = (100, 100, 100)
    BLUE = (0, 0, 255)
    
    # ウィンドウの設定
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("青い四角形")
    
    # ゲームループ
    running = True
    
    # 初期の黒塗り
    screen.fill(BLACK)
    pygame.display.flip()
    
    
    while running:
        dirty_rects = []
    
        # 背景全体を白に塗りつぶし
        screen.fill(WHITE)
    
        # イベント処理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
    
        # 四角形の描画
        rect_square = pygame.draw.rect(screen, BLUE, (100, 100, 100, 100))
        dirty_rects.append(rect_square)
    
        # 画面の更新
        pygame.display.update(dirty_rects)
        print(dirty_rects)
    
    # Pygameを終了
    pygame.quit()
    
[/code]

printの出力は  
[<rect(100, 100, 100, 100)>]  
[<rect(100, 100, 100, 100)>]  
[<rect(100, 100, 100, 100)>]  
...

## 実行環境

M1 Macbook  
Python 3.10.12  
pygame 2.5.2

> > > pygame.display.get_driver()  
>  'cocoa'

## なぜこうなっているかの想定

pygame.display.update(rect)は、指定されたrectの範囲のピクセル行をすべて更新する、という仕様だから？  
しかしドキュメント(2.6ver向け)には特に書いていません。  
<https://www.pygame.org/docs/ref/display.html#pygame.display.update>  
  

## ベストプラクティスについて

更新範囲が指定箇所から逸脱するため、すべて再描画しないと表示がバグる可能性があると思います。  
文章や、ロゴマーク、セーブ/ロードボタンなど、更新回数が明らかに少ない場所で範囲に一見含まれていなくても再描画するのが正しい、ということになるのでしょうか？




PyGame の内部仕様まではわからないですが、おそらく想定通りの結果のはずです。

> 白い部分は更新していないため、黒いままなのでは？
[code]

    # 背景全体を白に塗りつぶし
        screen.fill(WHITE)
    
[/code]

いいえ、ここで更新しているので、まず、このコードの挙動に対するあなたの想定が間違っていると思います。

リファレンスに述べられているように、`flip` する範囲を絞り込むためのメソッドが `update`
ということなので、基本は、バッファに描画したものを（範囲を絞って）`flip` するということになります。

先にバッファに対して、WHITE を `fill` しているのですから、次の `flip` / `update`
のタイミングでサーフェイスに反映されます。それが水平の範囲全体になるという細かい仕様まではリファレンスに書かれていませんから、推量するしかないですが、実際にそのようになるのであれば、そういう仕様なのでしょう。

なので、`flip` / `update` で最終的にサーフェイスに反映されたくないものは、バッファにそもそも描画しなければいいだけです。というかむしろ、

[code]

    # 背景全体を白に塗りつぶし
        screen.fill(WHITE)
        pygame.display.flip()
    
[/code]

とするか、どちらかです。表示したくないのに、バッファには描き込みたい、というのが矛盾しているのです。




PyGame の内部仕様まではわからないですが、おそらく想定通りの結果のはずです。

> 白い部分は更新していないため、黒いままなのでは？
[code]

    # 背景全体を白に塗りつぶし
        screen.fill(WHITE)
    
[/code]

いいえ、ここで更新しているので、まず、このコードの挙動に対するあなたの想定が間違っていると思います。

リファレンスに述べられているように、`flip` する範囲を絞り込むためのメソッドが `update`
ということなので、基本は、バッファに描画したものを（範囲を絞って）`flip` するということになります。

先にバッファに対して、WHITE を `fill` しているのですから、次の `flip` / `update`
のタイミングでサーフェイスに反映されます。それが水平の範囲全体になるという細かい仕様まではリファレンスに書かれていませんから、推量するしかないですが、実際にそのようになるのであれば、そういう仕様なのでしょう。

なので、`flip` / `update` で最終的にサーフェイスに反映されたくないものは、バッファにそもそも描画しなければいいだけです。というかむしろ、

[code]

    # 背景全体を白に塗りつぶし
        screen.fill(WHITE)
        pygame.display.flip()
    
[/code]

とするか、どちらかです。表示したくないのに、バッファには描き込みたい、というのが矛盾しているのです。

