96890


マルチポストについて  
回答がいただけていないタイミングで、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>  
  

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

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


CC BY-SA 4.0
2023-11-22T07:04:03.650
96883
2023-11-23T04:15:36.590
2023-11-23T03:15:29.850
60497
60497
question
python pygame
pythonのpygameでpygame.display.update(rect)で正しい範囲のみが更新されないのは仕様なのか?


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

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

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

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

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

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

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

[code]

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

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


CC BY-SA 4.0
2023-11-22T11:20:43.623
96890
2023-11-22T11:20:43.623
7290
96883
answer


既に解決済みですが、参考までに回答します。  
手元の環境は `Ubuntu Linux 23.10` で、`Python 3.11.6/PyGame 2.5.2` を使用しています。PyGame の
video driver は `x11`(X Window System) です。質問にあるコードを実行すると同様の現象になります。

[code]

    >>> import pygame
    >>> pygame.init()
    >>> pygame.display.get_driver()
    'x11'
    
[/code]

[![画像の説明をここに入力](https://i.stack.imgur.com/PrXPr.png)](https://i.stack.imgur.com/PrXPr.png)

以下の様に
[`pygame.Surface.fill()`](https://www.pygame.org/docs/ref/surface.html#pygame.Surface.fill)
の `special_flags` に `pygame.BLEND_MIN`
を指定すると、`pygame.display.update(dirty_rects)` が想定通りに機能します。

[code]

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

※ 時間が取れれば `pygame.Surface.fill()` のソースコードを確認してみます

[![画像の説明をここに入力](https://i.stack.imgur.com/C1eXI.png)](https://i.stack.imgur.com/C1eXI.png)


CC BY-SA 4.0
2023-11-23T02:08:47.373
96898
2023-11-23T02:08:47.373
47127
96883
answer


teratailでの回答の要約(2023/11/23 12:47時点)  
無知によりマルチポストでお手数をおかけしてしまい申し訳ございません。  
現状、teratail(<https://teratail.com/questions/6ibljaslxoffl6>)の方でご回答いただいたことをこちらでも要約します。  
また、teratailの方の質問は解決済みとなっております。

  * "指定矩形の範囲外を再描画しないとは限らない"グラフィックス系のライブラリもある
  * 青い四角形の例における「全体を白で塗りつぶす」は描画されるべきでない内容をバッファに書きこんでいるので無駄である

結果として、現在は以下の処理を実装しようと考えています。(未トライ)

  0. (すでに青い四角形が表示されている前提)
  1. 青い四角形のrectを白で塗りつぶす
  2. 青い四角形のrectを描画
  3. 青い四角形のrectをupdate

何をしたいかわからない実装ですが、元々の目的は「残像を発生させない、かつ、描画や更新範囲が少ない物体の移動表示」でした。  
(そこで本文の問題にぶつかり、「ライブラリとしての欠陥なのではないか」というモヤモヤもあり起票いたしました。)  
ですので、青い図形を動かすとすると、

  0. (すでに青い四角形が表示されている前提)
  1. 青い四角形のrectを白で塗りつぶす
  2. 青い四角形の新しい位置を計算
  3. 青い四角形を新しい位置で描画
  4. pygame.display.update([前の位置のrect, 新しい位置のrect])

となると思います。


CC BY-SA 4.0
2023-11-23T03:08:09.963
96899
2023-11-23T04:15:36.590
2023-11-23T04:15:36.590
60497
60497
96883
answer