2025年12月11日木曜日

【Pythonゲーム開発 】#4.ゲームの心臓「メインループ」を作ろう(QTimer編)

 はじめに

 前回、画面に四角形を描くことができましたが、まだ動きません。 ゲームとは、パラパラ漫画のように「少しずつ動かした絵」を高速で切り替えることで成立しています。多くの初心者はここで「while Trueループで回せばいいんでしょ?」と考えますが、GUIアプリでそれをやると画面がフリーズして死にます。今回は、PySide6における正しいループの作り方、「QTimer」の使い方をマスターしましょう。


§1. なぜ「while True」はダメなのか?

 #2で解説した通り、GUIアプリは常に「イベントループ(待機状態)」の中にいます。もし自分で while True: を書いてしまうと、プログラムがそこで全力疾走を始めてしまい、ウィンドウの描画やクリックを受け付ける余裕がなくなります。結果、アプリは「応答なし」になります。


§2. 正解は「QTimer(キッチンタイマー)」

そこで使うのが QTimer です。 これは、「指定した時間ごとに、ベル(合図)を鳴らす」機能です。

設定: 「0.016秒(約16ミリ秒)ごとに合図して!」

動作:0.016秒経つ。

合図が鳴る → 「キャラの座標を+1する」

合図が鳴る → 「画面を書き直す(再描画)」

(待機)

また0.016秒経つ...

この隙間があるおかげで、アプリはフリーズせずに動き続けることができます。 ちなみに 1秒 ÷ 60回 ≒ 0.016秒 なので、これで滑らかな60FPSが実現できます。


§3. 実践コード:四角形を走らせろ!

四角形が左から右へ自動で動くコードです。 コピーして実行してみてください。

pythonコード

はじまり---------------------------------------------

import sys

from PySide6.QtWidgets import QApplication, QWidget

from PySide6.QtGui import QPainter, QBrush, QColor

from PySide6.QtCore import QTimer, Qt

class GameLoopWindow(QWidget):

    def __init__(self):

        super().__init__()

        self.setWindowTitle("動け!四角形")

        self.resize(500, 300)

        # ★ 変数の準備

        self.rect_x = 0  # 四角形のX座標(最初は0)

        # ★ 1. タイマーの準備

        self.timer = QTimer(self)

        self.timer.timeout.connect(self.update_game) # 時間が来たら呼ぶ関数を指定

        self.timer.start(16) # 16ミリ秒ごとにスタート!

    # ★ 2. 定期的に実行される「ゲームの更新処理」

    def update_game(self):

        # 座標を少し右にずらす

        self.rect_x += 2

        # 画面の端まで行ったら左に戻す(ループ)

        if self.rect_x > 500:

            self.rect_x = -50

        # 重要:「画面を書き直して!」とOSに依頼する

        self.update() 

    # ★ 3. 描画処理(update()されると呼ばれます)

    def paintEvent(self, event):

        painter = QPainter(self)

        # 背景を塗りつぶす(これがないと残像が残る!)

        painter.fillRect(self.rect(), Qt.white)

        # 変数(self.rect_x)の位置に描く

        painter.setBrush(QBrush(Qt.green))

        painter.drawRect(self.rect_x, 100, 50, 50)

if __name__ == "__main__":

    app = QApplication(sys.argv)

    window = GameLoopWindow()

    window.show()

    sys.exit(app.exec())

おわり---------------------------------------------


§4. コードの仕組み(リスト解説)

今回追加された「動き」の仕組みを表にまとめました。

ブロック役割コードの要点解説
準備タイマー設置

self.timer = QTimer()


self.timer.start(16)

「16ミリ秒ごとに update_game を実行せよ」とセットします。


これがゲームの心臓の鼓動になります。

計算座標の更新

def update_game(self):


self.rect_x += 2

変数の数字だけを変化させます。


※ここでは描画はしません。

通知再描画の依頼self.update()

「データが変わったから、画面を描き直して!」という命令です。


これを呼ぶと、自動的に paintEvent が実行されます。

描画表示

def paintEvent...


drawRect(self.rect_x...)

変化した新しい self.rect_x を使って、四角形を描きます。

今日のまとめ

  • 「変数」を変える。

  • 「QTimer」で定期的に変える。

  • 「update()」で画面に反映する。

この3ステップの繰り返しが、どんな複雑なゲームでも共通する「メインループ」の正体です。


次回予告

勝手に動く四角形は作れましたが、まだ操作できません。 次回は、あなたのキーボード操作でキャラを動かす「入力処理(Input)」を実装します。

Next Step: 【Vol.5】WASDで自由自在!キー入力でキャラを動かそう


2025年12月8日月曜日

【Pythonゲーム開発】#3.四角形や円を描いてみよう(QPainter基礎編)

はじめに

ゲーム画面というのは、突き詰めれば「四角形(地形)」や「画像(キャラ)」の集まりです。 つまり、「好きな場所に、好きな色で、好きな図形を描く」ことができれば、画面作りは8割完了したも同然です。今回は、PySide6の描画職人である「QPainter」を使って、ウィンドウに絵を描いてみましょう。


§1. 座標系を知ろう:Y軸は「下」へ

コードを書く前に、ゲームの世界の「地図(座標)」を理解する必要があります。 学校の数学とは少し違います。

  • (0, 0) 原点: ウィンドウの「左上」です。

  • X軸: 右に行くと増えます(数学と同じ)。

  • Y軸: 「下」に行くと増えます(数学と逆!)。

「Yが増える=下に落ちる」とイメージしてください。


§2. 描画のルール:「paintEvent」

ここが少し難しいポイントです。 PySide6では、「描きたいときに勝手に描く」ことはできません。 「OSから『画面を更新していいよ』と言われたタイミング」でのみ描画が許されています。

そのタイミングで自動的に呼ばれる関数(イベント)が、paintEvent です。 私たちは、この paintEvent の中身を書き換える(オーバーライドする)ことで、絵を描きます。


§3. 実践!赤と青の図形を描く

では、実際にコードを書いてみましょう。 今回は「ただのウィンドウ(QWidget)」ではなく、「お絵かき機能付きのウィンドウ(MyGameWindow)」という自作クラスを作ります。

pythonコード

はじまり---------------------------------------------

import sys

from PySide6.QtWidgets import QApplication, QWidget

from PySide6.QtGui import QPainter, QColor, QPen, QBrush

from PySide6.QtCore import Qt

# 1. QWidgetを改造して、自分だけのウィンドウクラスを作る

class MyGameWindow(QWidget):

    def __init__(self):

        super().__init__()

        self.setWindowTitle("お絵かきテスト")

        self.resize(400, 300)

    # 2. 画面描画のタイミングで自動的に呼ばれる関数

    def paintEvent(self, event):

        # 画家(Painter)を呼ぶ

        painter = QPainter(self)

        # --- ここからお絵かきタイム ---

        # ■ 四角形を描く(枠線:黒、中身:赤)

        painter.setPen(QPen(Qt.black, 3))       # ペン(枠線)の設定:黒色、太さ3px

        painter.setBrush(QBrush(Qt.red))        # ブラシ(塗りつぶし)の設定:赤色

        painter.drawRect(50, 50, 100, 100)      # (x=50, y=50)に、幅100, 高さ100の四角を描く

        # ● 円を描く(枠線:なし、中身:青)

        painter.setPen(Qt.NoPen)                # ペンなし(枠線を描かない)

        painter.setBrush(QBrush(Qt.blue))       # 青色で塗りつぶす

        painter.drawEllipse(200, 50, 100, 100)  # (x=200, y=50)に、幅100, 高さ100の円を描く

# --- ここからメイン処理 ---

app = QApplication(sys.argv)

window = MyGameWindow() # 自作したクラスを使う

window.show()

sys.exit(app.exec())

おわり---------------------------------------------

ウィンドウに図形が出現!!
なお、ウィンドウはまだ動かない...

 §4.コードの解説

重要なのは paintEvent の中身です。

  • painter = QPainter(self)

    • 「私(このウィンドウ)に絵を描くための筆をください」という宣言です。

  • painter.setPen(...)

    • 「枠線」の色や太さを決めます。Qt.NoPen にすると枠線が消えます。

  • painter.setBrush(...)

    • 「中身(塗りつぶし)」の色を決めます。

  • painter.drawRect(x, y, w, h)

    • 指定した座標とサイズで四角形(Rectangle)を描きます。

  • painter.drawEllipse(x, y, w, h)

    • 指定した四角形の中に収まるような楕円(Ellipse)を描きます。正方形なら円になります。

次回予告

静止画を描くことはできました。 しかし、ゲームは「動いて」ナンボです。 次回は、この描いた四角形をアニメーションさせてみましょう。

SAPOの活動概況:GCS-97を支える"多方面創作活動"個人プロジェクト(SAPO_CREATE_STATION)

SAPOとは? GCS-97における役割

 SAPOは、任意創作団体GCS-97の副管理人を務めており、実質的に共同管理者に近い役割を担っています。団体全体の運営と創作活動の推進において中心的な役割を果たしています。

 SAPO個人の創作プロジェクト名がSAPO_CREATE_STATIONであり、GCS-97に所属しながら個人プロジェクトを進めるという体制をとっています。具体的には、ソフトウェア開発をSAPO_CREATE_STATIONとして行い、その成果物(アプリなど)のデバッグをGCS-97のソフトウェア部門GCOTとして協力しています。GCS-97との共同制作も積極的に行っています。

SAPOが管理するGCS-97の主要部門一覧

SAPOは、GCS-97内の本部運営から具体的な創作部門に至るまで、以下の通り重要な責任者(副管理人/管理人)を兼任しています。

部門名SAPOの役割活動内容
GCS-97本部副管理人団体全体の運営・統括
GCS-97広報部門副管理人団体およびコンテンツの広報活動の企画・推進
GCOT (ソフトウェア部門)副管理人ゲーム開発、ランチャーアプリなどソフトウェアの制作・管理
GCSノベリスト (小説部門)管理人小説作品の制作・管理
RCW(レジンクリスタルワークス)管理人UVレジン作品の制作・管理(現在は休止中の部門)
フォトファンタジア (カメラ・映像部門)管理人写真、映像作品の制作・管理

SAPO_CREATE_STATIONの主な創作活動

SAPO_CREATE_STATIONとしての主な活動は、上記の部門を横断する形で展開されており、特に以下の分野に注力しています。

  • ゲーム制作(SAPO主導)

    • 例:新作リメイクプロジェクト SAPORE:CODE-PROJECT の始動(第一弾:『SAPO-RE PONG』をリリース)。

  • 統合アプリ開発

    • GCS-97のコンテンツ管理を支える統合ランチャーアプリ 「Launcherアプリ-The Junction-97-」の開発。

  • 小説制作

    • GCSノベリスト部門における創作活動。

【Pythonゲーム開発 】#4.ゲームの心臓「メインループ」を作ろう(QTimer編)

 はじめに  前回、画面に四角形を描くことができましたが、まだ動きません。 ゲームとは、パラパラ漫画のように「少しずつ動かした絵」を高速で切り替えることで成立しています。多くの初心者はここで「while Trueループで回せばいいんでしょ?」と考えますが、GUIアプリでそれをやる...