はじめに
現在開発中の戦略RTSですが、開発が進むにつれてある問題に直面しました。 「オブジェクト(領土やユニット)が増えると、描画がカクつく」
Python自体の処理速度の問題もありますが、実はGUIライブラリである「PySide6 (Qt)」の描画の仕組みに大きな理由がありました。 今回は、ハードウェア(CPU・GPU)の視点から、アプリが重くなる原因と対策を考察します。
§1. 「QPainter」は誰が仕事をしているのか?
PySide6で図形を描くときに使う QPainter。 実は標準設定では、この描画処理のほとんどを「CPU(中央演算処理装置)」が行っています(ラスタライズ処理)。
CPUの得意技: 複雑な計算、条件分岐(「もし人口が〇〇なら…」というロジック)。
CPUの苦手技: 何万個ものピクセルの色を単純に塗りつぶす作業。
RTSのマップ描画のように「数千個のボロノイ図形を、毎秒60回塗りつぶす」という処理は、CPUにとっては「天才数学者に、広大な壁のペンキ塗りをさせている」ようなものです。これでは過労(高負荷)で動作が遅れてしまいます。
§2. GPU(グラフィックボード)に仕事を投げたい!
本来、こうした「単純かつ大量の描画」は、GPUの専門分野です。 GPUには数千個の小さなコアがあり、「一斉にペンキを塗る」作業が得意だからです。
PySide6でGPUを使うには、大きく分けて2つのアプローチがあります。
アプローチA: QOpenGLWidgetを使う(本格派)
通常の QWidget の代わりに、QOpenGLWidget を継承して画面を作ります。 こうすると、Qtは描画命令をOpenGL(GPUへの命令)に変換してくれます。
メリット: 爆速になります。数万ポリゴンでもヌルヌル動く可能性があります。
デメリット: 実装難易度が高いです。座標系が変わったり、いつものQPainterの機能が一部制限されたりします。
アプローチB: ハードウェアアクセラレーションを有効化する(お手軽派)
コードの冒頭に数行追加して、レンダリングエンジンを切り替える方法です。
# 例:OpenGLを使用するようにQtに指示する
app.setAttribute(Qt.AA_UseDesktopOpenGL)
これだけで改善する場合もありますが、ドライバとの相性などで表示が崩れるリスクもあります。
3. 今すぐできる「描画のキャッシュ化」(現実的な解決策)
GPUへの完全移行は工事が大変ですが、CPUのままでも劇的に軽くする方法があります。 それは「QPixmapへのキャッシュ(焼き付け)」です。
重い処理: 毎フレーム paintEvent で「ボロノイ図の計算」と「塗りつぶし」をやり直す。
軽い処理:
最初に1回だけ計算して、結果を QPixmap(画像データ)としてメモリに保存する。
次のフレームからは、計算せずに「さっきの画像を貼り付ける」だけにする。
これなら、CPUの負荷は「画像のコピー」だけになり、後半になっても重くなりません。 「動かない地形」は画像化し、「動くユニット」だけをその上に描画する。これがRTS軽量化の定石です。
まとめ
PySide6の標準描画はCPU依存なので、オブジェクトが増えると限界が来る。
GPU(OpenGL)を使えば解決するが、実装コストが高い。
まずは「再描画を減らす(キャッシュ化)」ことから始めるのが、最適化の第一歩。
今後のRTS開発では、この「キャッシュ化」技術を導入して、数千プロヴィンスの大規模マップでもサクサク動く環境を目指します。
編集後記:知らなかった過去への自戒
正直なところ、このGPU処理の仕組みを知る前の私は、今までのゲーム制作において「なんか処理が重くなるなー」と漠然と思いながら作っていました。
「Pythonだから仕方ない」とか「PCのスペックの問題かな」と片付けてしまっていましたが、原因はもっと根本的な描画の仕組みにあったわけです。 理由が分かれば、対策も打てます。
無知とは恐ろしいものですが、同時に伸びしろでもあります。 今後は、この反省を活かして積極的にGPU処理を取り入れ、軽量でサクサク動くゲーム作りを目指していきたいと思います。

0 件のコメント:
コメントを投稿