質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Pygame

Pygameは、ビデオゲームの製作用に設計されたクロスプラットフォームのPythonモジュールセットです。Pythonでコンピューターグラフィックスと音声を扱うためのライブラリが含まれています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

683閲覧

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

08noboreni

総合スコア47

Pygame

Pygameは、ビデオゲームの製作用に設計されたクロスプラットフォームのPythonモジュールセットです。Pythonでコンピューターグラフィックスと音声を扱うためのライブラリが含まれています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

1グッド

1クリップ

投稿2023/11/22 10:21

編集2023/11/23 03:45

更新:
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

すべて再描画しないと表示がバグる可能性

の詳細について

例えば、下記のようなゲーム画面があるとします。
左上の女の子の動きのみ頻繁に更新する必要があるとします。

chatGPTで出力した適当なゲーム画面例

その際に、できるだけ処理を省こうとすると、

  1. 背景を緑色でfill
  2. 新しい女の子の画像を配置
  3. 女の子のウィンドウ部分のみ更新

としたいと思います。
<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)

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TakaiY

2023/11/22 12:35

> 更新範囲が指定箇所から逸脱するのであれば、すべて再描画しないと表示がバグる可能性がある とういうのはどのような状況でしょうか? 回答はここではなく、質問を編集してください。
08noboreni

2023/11/22 16:23

@TakaY様 追記いたしました。不明瞭な部分があり申し訳ございません。 @quickquip様 マルチポスト対応を行いました。申し訳ございません。
quickquip

2023/11/22 22:43

こちらに追記された内容があちらに追記されてないですよね。 質問サイトを使うと「回答に必要な情報の追記依頼」や「質問で不明瞭な部分への質問」が往々にしてあります。それは「回答するのに必要なこと」なので、どちらかで言われた「必要なこと」はもう一方でも必要なことが普通です。 あちらだけを見ている人に必要な情報が届いてないです。 つまりマルチポストは「質問者の手間を増やし、回答者が既存の議論を参照できない、もしくは参照するのに一手間かかる」という誰も得をしないという行為なのです。 だから「嫌われて」いるわけですし、Tetratailの運営もそう分かっていて「非推奨」としているのだろうと思っています。(なぜ嫌われるのかをよく分かっている人が書いたガイドだな、と思っています)
TakaiY

2023/11/23 02:53

quickquipさんから良回答出ていますのでそれでいいと思いますが、そのコメントを見て確認です。 > 横着せずにすべてを再描画した上で、 とありますが、質問者さんは、画面をupdate()やflip()のような再描画のコマンドを実行する前には、全ての画面の部品を改めて自分で描画する必要があると思っていた(る?)ということでしょうか? で、それは非効率なので、「背景を緑色でfill」のような簡単な処理にして、更新したところだけupdateすればいいと考えたということですか? 実際は、質問のコードのscreen.fill(WHITE)や、例の「背景を緑色でfill」のような処理は不要で、システムはそれまで描画した内容を「覚えている」ので、再描画しても現状の画面は残るのです。 そのときにどのようなことが起きるのかについてはOSやドライバやライブラリに依るのであまり気にしなくてもOKということです。
08noboreni

2023/11/23 03:27 編集

@quickquip様 ご指摘ありがとうございます。 すべての情報をリアルタイムで更新するのは、時間的に難しい点がございますので、リンクを先方にも追加いたします。
08noboreni

2023/11/23 03:46 編集

@TakaiY様 ご確認いただきありがとうございます。 >全ての画面の部品を改めて自分で描画する必要があると思っていた(る?)ということでしょうか? いえ、その必要がない(したくない)と考えておりました。しかし、「予想外の場所も画面更新される可能性がある」ということがわかり、更新しない予定だった場所も再描画して更新されても問題ない状態にしておくのが良いかと思いました。 しかし今は、quickquip様の追記で触れられた通り、「1.背景を緑色でfill」が問題だと感じています。 代わりに、「1. (本文の例で言う)女の子エリア(=女の子画像のrect)の領域のみを緑色で塗りつぶす」が一番正しいと思っております。
TakaiY

2023/11/23 04:26

> 女の子エリア(=女の子画像のrect)の領域のみを緑色で塗りつぶす」 これも不要だと思いますけど。 女の子の画像が透明部分を含んでいるのであれば必要かもしれませんが、その場合はそもそも透明部分を含まないものにすればいいわけで。
08noboreni

2023/11/23 09:48

@TakaiY様 おっしゃるとおり、透過でなければ不要ですね。 例で言うと、女の子自体が上下左右に動く、的な効果がない部分については、いただいたアドバイスの通り、透過を含まないものでいこうと思います。
guest

回答1

0

ベストアンサー

"指定された矩形の範囲を再描画する"ことは保証されていますし、保証されていないと困ります。
ですが、裏の、"指定した矩形の範囲外を再描画しない"ことは保証されていません。また保証されていないことが問題になることはないはずです。

あるプラットフォームで、指定された矩形群だけ再描画することがパフォーマンス的に不利だということはありえるでしょう。その場合、指定された矩形だけを無理に再描画する理由はないです。指定された矩形を含む範囲が、パフォーマンス的に有利な方法で再描画されれば十分なはずです。

TakaiY さんがコメントしていることにまったく同意で、いま「白で描画せよと指定した部分が白で再描画されている」ことがどういう問題になるのか分かりませんでした。
不思議だと思うのは理解できますが、"表示がバグる可能性がある"というのがどういう状況か説明が必要かと思います。


ちなみに、Windowsでは白の部分が再描画されず、期待しているであろう結果になると思います。

いまmacOSで青色四角の左右が白で再描画されているのが、バグなのか、実装として妥当なのか(つまりパフォーマンス的に有利だという理由で意図的にそうしているのか)の判断はつきませんでした。
この部分はSDL ( https://www.libsdl.org/ , https://github.com/libsdl-org/SDL )の範疇なので、SDL,C言語,Objective-C のタグを付ける方が回答できる人に届くと思います。


(追記)
グラフィックス系のライブラリで、"指定矩形の範囲外を再描画しないとは限らない"と明記されているものを扱ったことがあって、実際の再描画と再描画の間に「複数回のupdateRectがあって最大外接矩形が全体に対してある程度以上の比率になったら自動で全体の再描画になる」と経験的に推測できる実装などを見てきたので、質問をみて、そういうことはあるだろう、それは問題にはならないはず、と思って回答しました。


(追記)

その際に、できるだけ処理を省こうとすると、

  1. 背景を緑色でfill
  2. 新しい女の子の画像を配置
  3. 女の子のウィンドウ部分のみ更新

としたいと思います。

  1. 背景を緑色でfill

が無駄です。

  1. 新しい女の子の画像を配置
  2. 女の子のウィンドウ部分のみ更新

でいいのです。
これであれば「女の子のウィンドウ部分のみ更新」の時に指定した矩形範囲以外が(パフォーマンス等の理由で)再描画されたとしても、そこに表示されるべき内容で描画されるので何も問題ないわけです。

"背景を緑色でfill"は 描画されるべきでない内容をバッファに書きこんでいるということなので、完全に無駄な処理なのです。

投稿2023/11/22 14:59

編集2023/11/22 22:26
quickquip

総合スコア11040

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

08noboreni

2023/11/22 16:15

過去の類似した事例という、とても貴重な知見をいただきまして、誠にありがとうございます。 大変勉強になりました。 また、SDLの範疇になるということで、そちらも大変ありがたい情報です。 おっしゃるとおり、ガッツリ深く突っ込まずとも対応できるものですので、横着せずにすべてを再描画した上で、一部を更新することを考えています。
quickquip

2023/11/22 22:49

追記しました。(素の)pygameをちゃんと使ったことはないので、たぶんこういうことだと思いますよ、程度の話で捉えておいてください。
08noboreni

2023/11/23 03:35

ありがとうございます。 個人的に腑に落ちる理解までたどり着くことができました。 おっしゃるとおり、「1. 背景を緑色でfill」が不適切だと思います。 ただ、女の子に表情以外の体の動きが発生する場合、一度塗りつぶさないと残像が発生すると思います。 ですので、代わりに「1. (本文の例で言う)女の子のウィンドウ部分のみを緑色で塗りつぶす」を採用しようと思っています。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問