更新:
2023/11/23
- ベストプラクティスについて:すべて再描画しないと表示がバグる可能性の詳細について を追記
- 新たにわかった情報:(末尾に追加)
- マルチポストであることを追記
- 実行環境にvideo driverの情報を追記
- 新たにわかった情報に、stockoverflowで頂いた情報の要約を追記
ーーーーーーー
マルチポスト:
https://ja.stackoverflow.com/questions/96883/python%e3%81%aepygame%e3%81%a7pygame-display-updaterect%e3%81%a7%e6%ad%a3%e3%81%97%e3%81%84%e7%af%84%e5%9b%b2%e3%81%ae%e3%81%bf%e3%81%8c%e6%9b%b4%e6%96%b0%e3%81%95%e3%82%8c%e3%81%aa%e3%81%84%e3%81%ae%e3%81%af%e4%bb%95%e6%a7%98%e3%81%aa%e3%81%ae%e3%81%8b
理由:先に投稿したstockoverflowのpygameタグの質問数が少なく、pygameに関する回答を得られる可能性が低いと感じたため。
対応:新たに判明したことを更新し、解決した際にはこちらにも情報を記載し終了いたします。
数時間ネットで調査しましたが、わかりませんでした。申し訳ございませんが、教えてください。
達成したいこと
掲題の件が仕様なのか確認したい。
pygameで再描画範囲を限定し軽いゲームを目指す際のベストプラクティスを知りたい(「背景と該当のオブジェクトのみを描画して部分的にupdateすれば良いじゃん」ができないため)
何について、仕様なのか確認したいのか
pygame.display.update(rects)
で画面の一部のみを更新しようとしたときに、rectsで指定した範囲以外の場所も更新されること。
例
初期処理で黒背景にして、繰り返しの中で白背景を全体描画し、青い四角形を描画し、青い四角形の部分だけupdateした場合
青い四角形が表示されるのは正しいが、白い部分は更新していないため、黒いままなのでは?
青い四角形を表示するコード
Python
1import pygame 2 3# 初期化 4pygame.init() 5 6# 定数 7WIDTH, HEIGHT = 800, 600 8WHITE = (255, 255, 255) 9BLACK = (0, 0, 0) 10GREY = (100, 100, 100) 11BLUE = (0, 0, 255) 12 13# ウィンドウの設定 14screen = pygame.display.set_mode((WIDTH, HEIGHT)) 15pygame.display.set_caption("青い四角形") 16 17# ゲームループ 18running = True 19 20# 初期の黒塗り 21screen.fill(BLACK) 22pygame.display.flip() 23 24 25while running: 26 dirty_rects = [] 27 28 # 背景全体を白に塗りつぶし 29 screen.fill(WHITE) 30 31 # イベント処理 32 for event in pygame.event.get(): 33 if event.type == pygame.QUIT: 34 running = False 35 36 # 四角形の描画 37 rect_square = pygame.draw.rect(screen, BLUE, (100, 100, 100, 100)) 38 dirty_rects.append(rect_square) 39 40 # 画面の更新 41 pygame.display.update(dirty_rects) 42 print(dirty_rects) 43 44# Pygameを終了 45pygame.quit()
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
ベストプラクティスについて
元々は、変更箇所が一部であれば、その場所に描画したいものだけを描画して、その場所のみを更新しようと思っていました。
しかし、更新範囲が指定箇所から逸脱するのであれば、すべて再描画しないと表示がバグる可能性があると思います。
ーーーー
追記 2023/11/23
すべて再描画しないと表示がバグる可能性
の詳細について
例えば、下記のようなゲーム画面があるとします。
左上の女の子の動きのみ頻繁に更新する必要があるとします。
その際に、できるだけ処理を省こうとすると、
- 背景を緑色でfill
- 新しい女の子の画像を配置
- 女の子のウィンドウ部分のみ更新
としたいと思います。
<br>
しかし、今回問題にしている挙動により、右側のセーブ、ロード、オプション等のボタンたちの部分まで更新されてしまいます。
一見、更新が必要ない、動きがない部分についても、再描画は行っておかないと予想外のタイミングで更新されてしまう、= 表示がバグる、ということでした。
わかりにくい内容で申し訳ないです。
ーーーー
文章や、ロゴマーク、セーブ/ロードボタンなど、更新回数が明らかに少ない場所で範囲に一見含まれていなくても再描画するのが正しい、ということになるのでしょうか?
そもそも、pygameでは、「変更箇所が一部であれば、その場所に描画したいものだけを描画して、その場所のみを更新する」という想定はしておらず、「毎回すべてを再描画した上で、一部を更新する」という方針なのでしょうか。
全く変更のない箇所(例えば、ゲーム画面上のメニューボタン群など)を毎回描画し直すのは、無駄だと感じます。
新たにわかったこと
- windows環境だと、正しく?動く(青い四角形の例で、白い部分が表示されない)場合もある情報をいただきました
- SDLの関係という情報もいただきました : https://github.com/pygame/pygame/pull/1773
- SDLのUpdateWindowFramebuffer() というOSによって処理が変わる関数が呼び出されていて、pygameではなく、少なくともSDL以降のバグor仕様のようである:https://github.com/pygame/pygame/issues/251#issuecomment-619552646
- 過去に、windows版でupdate(rect)で画面全体が更新されるバグがあり、SDL側で修正が行われたこともある
- 他の方の報告:linuxで同様の事象の報告がありました。(https://ja.stackoverflow.com/a/96898/60497)
- 青い四角形の例をpygameで解決しようとする場合、special_flagsを設定することでも対応できる報告
screen.fill(WHITE, special_flags=pygame.BLEND_MIN)
BLEND_MINは、そのピクセルのRGBのそれぞれについて、小さい方の値を採用するようです。(参照:https://stackoverflow.com/a/625476/22964188)
(報告元 : https://ja.stackoverflow.com/a/96898/60497)
回答1件
あなたの回答
tips
プレビュー