- Python
Pygameとは?使い方入門|よけゲーム全コードとFPS実測・pygame-ce比較
結論
Pygameは、Pythonで2Dゲームを作るためのライブラリです。pip install pygame で入れ、「入力→更新→描画」を1秒に60回くり返すゲームループを書けば、図形・画像・文字・音・キー入力を扱えます。
この記事でわかること
- インストールから描画・入力・当たり判定・Sprite・音までの使い方
- よけゲーム1本の全コードと、画面の無い環境で動かした結果
- FPSの実測、エラー原文、pygame-ce・Arcade・Pyxel・Godotとの判断表
Pygameのプログラムは、どれも同じ形のくり返し(ゲームループ)でできています。1回のくり返しが1フレームで、これを1秒に60回まわすと60FPSです。
flowchart TD
A[pygame.init と set_mode] --> B[イベントを読む<br>キー・マウス・閉じる]
B --> C[状態を更新する<br>移動・当たり判定・得点]
C --> D[描画する<br>fill → blit → flip]
D --> E[clock.tick 60<br>1フレーム待つ]
E --> F{終了?}
F -->|いいえ| B
F -->|はい| G[pygame.quit]
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| Pygameとは | SDLを土台にした、Pythonの2Dゲーム用ライブラリ(無料・LGPL) | Pygameとは |
| pygame インストール | venvを作って pip install pygame。Python 3.14ならpygame-ce |
インストール方法 |
| pygame 使い方の基本 | init→set_mode→ループ(event.get・更新・描画・tick)→quit | ゲームループの書き方 |
| 日本語が四角になる | 既定のフォントは日本語を持たない。日本語フォントのファイルを指定する | 図形・画像・文字の描画 |
| 当たり判定 | Rectの colliderect、Spriteの spritecollide、形どおりならmask |
当たり判定とSprite |
| python ゲーム 作り方 | よけゲーム(112行)の全コードを写して動かし、改造する | よけゲームの全コード |
| FPSを決める | clock.tick(60)。手元の実測は61.0FPS |
FPSとclock.tickの実測 |
| pygameとpygame-ceの違い | 同じ import pygame で使えるコミュニティ版。更新が多い |
pygame-ceとの違い |
| exeにしたい | PyInstallerで、配る OS の上で作る | exe・ブラウザで配る |
確認環境:Ubuntu 24.04.5 LTS(2 CPU・共有のマシン)の上で、Python 3.13.18 の venv を2つ作り、pygame 2.6.1(SDL 2.28.4)と pygame-ce 2.5.8(SDL 2.32.10)を pip 26.2.2 で別々に入れました。画面の無い環境なので、SDL_VIDEODRIVER=dummy と SDL_AUDIODRIVER=dummy を付けてゲームループを動かし、描画は pygame.image.save で PNG に保存して、画像と画素の色(get_at)で確かめました。キー入力は pygame.event.post で送り、音は自作の WAV を読み込むところまで(鳴らしていません)です。ゲームの素材はすべて図形と自作の画像です。日本語の表示には Noto Sans CJK JP を使いました。Python 3.14 でのインストールの失敗は、uv で入れた Python 3.14.6 で確かめています。pygame-ce の違い、PyInstaller、pygbag、Arcade・Pyxel・Godot は各公式ドキュメントと PyPI で確かめました。Windows・Mac の画面での動作は手元で動かしていません。図は mermaid 11.16.0 で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Pygameとは?使い方入門|よけゲーム全コードとFPS実測・pygame-ce比較」
Pygameとは(できること・作れるゲーム)
この節では、Pygameが何をするライブラリで、どんなゲームに向くのかが分かります。
Pygameは、Pythonから画面の描画・キーボードやマウスの入力・音の再生を扱うためのライブラリです。 中では SDL(Simple DirectMedia Layer)という C 言語のマルチメディア用ライブラリが動いていて、Pygame はそれを Python から使いやすくした形です。ライセンスは LGPL で、無料で使えます。pygame-ce の README には、ライブラリを変更しなければ、ソースを公開しないゲームや商用のゲームにも使えると書かれています。
Pygame は「ゲームエンジン」ではありません。シーンの編集画面や物理エンジンは無く、ゲームループを自分で書き、毎フレーム何を描くかを自分で決めます。 pygame-ce の公式ドキュメントも、ほかのライブラリと違ってプログラムの実行を完全に自分で制御できる点を特徴に挙げています。手間は増えますが、ゲームが動く仕組みがそのまま見えるので、プログラミングの学習に向いています。
1描く
四角・円・線・多角形、PNG などの画像、文字。拡大・回転・透過も transform でできる。
2受け取る
キーボード・マウス・ゲームパッド・ウィンドウを閉じる操作を、イベントとして順に受け取る。
3鳴らす
効果音(mixer.Sound)とBGM(mixer.music)。WAV・OGG・MP3 などを読み込む。
どんなゲームが作れるか
向いているのは、2Dのアクション・シューティング・パズル・ブロック崩し・ピンポン・すごろく・簡単な RPG などです。1画面に数百〜数千の絵を動かす程度なら、後で測るとおり一般的な PC で60FPSを保てます。3D や大規模なゲーム、スマホのアプリとして出したいゲームは、ほかの道具のほうが向きます(判断表の節で比べます)。
Pygameで身に付くこと
- くり返しと状態の管理:毎フレーム変わる座標・得点・場面(タイトル→プレイ→ゲームオーバー)を変数で持つ。
- クラスの使い方:プレイヤーや敵を Sprite のクラスにすると、クラスと継承が自然に身に付く。
- 座標と当たり判定:左上が (0, 0)、右が x の正、下が y の正という画面の座標の考え方。
- 処理の速さを測る:1フレーム16.7ms の中に収める、という予算で考える習慣。
クラスの書き方そのものは、関連記事「Pythonクラスの使い方|__init__とselfを実行結果つきで図解」で扱っています。
Pygameのインストール方法(pip・venv・動作確認)
この節では、Pygameを入れる手順と、入ったことを確かめる方法、Python の版で失敗する例が分かります。
プロジェクトごとに venv(仮想環境)を作り、その中で pip install pygame を実行します。 Windows・Mac・Linux とも、PyPI に用意された配布物(wheel)が使えれば、追加の準備はありません。
venv を作る
python3 -m venv .venv(Windows は py -m venv .venv)。ゲームごとにフォルダーを分けると、版の食い違いが起きにくい。
有効にする
Mac・Linux は source .venv/bin/activate、Windows の PowerShell は .venv\Scripts\Activate.ps1。
pygame を入れる
python -m pip install pygame。「python -m pip」と書くと、いま使っている Python に確実に入る。
版を確かめる
python -c "import pygame; print(pygame.version.ver)"。最初の import であいさつの2行が出る。
見本のゲームを動かす
python -m pygame.examples.aliens。同梱の見本(aliens・chimp・stars など39本)でウィンドウと音を確かめる。
手元で入れたときの出力です。
$ python3 -m venv v1
$ v1/bin/pip install pygame
$ v1/bin/python -c "import pygame, sys; print(pygame.version.ver, pygame.get_sdl_version())"
pygame 2.6.1 (SDL 2.28.4, Python 3.13.18)
Hello from the pygame community. https://www.pygame.org/contribute.html
2.6.1 (2, 28, 4)
入った大きさは、pygame フォルダーが18MB、同梱の SDL などのライブラリ(pygame.libs)が15MB でした。最初の import で出る2行は、環境変数 PYGAME_HIDE_SUPPORT_PROMPT=1 を付けると消えます。
Python 3.14 では pygame が入らない(2026年10月29日時点)
2026年10月29日時点の PyPI で、pygame の最新版 2.6.1(2024年9月29日公開)の wheel は Python 3.13 まで(cp36〜cp313)です。 Python 3.14 で入れようとすると、ソースからのビルドが始まり、SDL の開発用ファイルが無いと止まります。手元の Python 3.14.6 での出力です。
$ python3.14 -m venv v4
$ v4/bin/pip install pygame
Collecting pygame
Downloading pygame-2.6.1.tar.gz (14.8 MB)
Getting requirements to build wheel: finished with status 'error'
error: subprocess-exited-with-error
/bin/sh: 1: sdl2-config: not found
WARNING: "sdl2-config" failed!
RuntimeError: Unable to run "sdl-config". Please make sure a development version of SDL is installed.
ERROR: Failed to build 'pygame' when getting requirements to build wheel
同じ Python 3.14.6 で pip install pygame-ce は wheel が使われてそのまま入り、pygame-ce 2.5.8 (SDL 2.32.10, Python 3.14.6) と表示されました。pygame-ce 2.5.8 の wheel は cp310〜cp315 です。Python 3.14 を使うなら pygame-ce、pygame を使うなら Python 3.13 以前、が2026年10月29日時点の分かれ目です。
venv の作り方と有効にできないときの直し方は関連記事「Python venvの使い方|作成・有効化コマンド早見表と有効化できない時の直し方」、Linux で pip が externally-managed-environment と断る場合は関連記事「externally-managed-environmentの直し方|pip」で扱っています。Anaconda を使っている場合も、その環境の python -m pip install pygame で入れられます。
Pygameの基本の使い方:ゲームループの書き方
この節では、ウィンドウを開いて四角を動かす最小のプログラムと、各行の役割が分かります。
Pygameのプログラムは「初期化 → ウィンドウを開く → ループ → 終了」の4つでできています。 ループの中は、毎回「入力 → 更新 → 描画 → 待つ」の順です。
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 320)) # ウィンドウを開く
pygame.display.set_caption("はじめての Pygame")
clock = pygame.time.Clock()
x = 0
running = True
while running:
for event in pygame.event.get(): # 1. 入力
if event.type == pygame.QUIT: # ×ボタン
running = False
x = (x + 4) % 480 # 2. 更新
screen.fill((30, 30, 40)) # 3. 描画(毎回まず塗りつぶす)
pygame.draw.rect(screen, (90, 200, 255), (x, 140, 40, 40))
pygame.display.flip() # 画面に反映
clock.tick(60) # 1秒に60回まで
pygame.quit()
| 行 | 役割 | 書き忘れると |
|---|---|---|
pygame.init() |
画面・音・フォントなどを使える状態にする | pygame.error: video system not initialized |
display.set_mode |
ウィンドウ(描く先の Surface)を作る | 画像の convert() で No video mode has been set |
event.get() |
たまったイベントを取り出す | OS から「応答なし」と見なされ、閉じられない |
screen.fill |
前のフレームの絵を消す | 動いた跡が帯になって残る |
display.flip() |
描いた内容を画面に出す | 何も表示されない(黒いまま) |
clock.tick(60) |
1秒に60回を超えないよう待つ | CPU を使い切り、PC によって速さが変わる |
このプログラムを、1秒後に「閉じる」イベントが届くようにして動かしたところ、四角の x 座標は256(4ピクセル×64フレーム)、経過時間は1.07秒でした。1秒にほぼ60回まわっています。screen.fill を消して0.5秒動かすと、y=160 の行の480画素のうち164画素が四角の色のまま残り、四角が通った跡が帯になりました。毎フレーム、背景から描き直すのが基本です。
描く順番=重なる順番
後から blit・draw したものが上に重なります。背景画像→キャラクター→得点の文字の順に描きます。背景を最後に描くと、キャラクターが隠れて見えなくなります。
画面の無い環境で動かす(SDL_VIDEODRIVER=dummy)
サーバーや CI(自動テスト)のように画面の無い環境では、環境変数 SDL_VIDEODRIVER=dummy を付けると、ウィンドウを出さずに同じコードが動きます。描いた結果は pygame.image.save(screen, "shot.png") で画像にして確かめられます。この記事の画面の確認はすべてこの方法です。
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy python game.py
手元で、環境変数を付けない・違う値を付けたときの結果です。
| 付けた値 | 結果 |
|---|---|
dummy |
動く。display.get_driver() は dummy |
| 付けない | error: XDG_RUNTIME_DIR is invalid or not set in the environment. と出たあと、SDL が offscreen を選んで動いた |
x11(X サーバーが無い) |
pygame.error: x11 not available で止まる |
音の指定なしで mixer.init() |
pygame.error: ALSA: Couldn't open audio device: No such file or directory |
音の装置が無い環境では、pygame.init() のときに ALSA lib ... Unknown PCM default という行がいくつも出ますが、止まらずに先へ進みました。pygame.init() の戻り値は (5, 0)(成功したモジュール数・失敗数)でした。
Pygameで図形・画像・文字(日本語)を描画する
この節では、図形・画像・文字を描く関数と、日本語が四角(□)になる理由と直し方が分かります。
描画は「Surface(絵を描く板)に描いて、最後に flip で画面へ出す」流れです。 set_mode が返す screen も Surface で、画像を読み込んだものも、文字を render したものも Surface です。
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 320))
# 図形
screen.fill((245, 245, 240))
pygame.draw.rect(screen, (220, 80, 60), pygame.Rect(30, 30, 120, 80))
pygame.draw.rect(screen, (40, 40, 40), pygame.Rect(30, 30, 120, 80), width=3) # 枠だけ
pygame.draw.circle(screen, (60, 120, 220), (240, 70), 45)
pygame.draw.line(screen, (30, 30, 30), (320, 30), (450, 110), 5)
pygame.draw.polygon(screen, (80, 170, 90), [(60, 290), (130, 160), (200, 290)])
pygame.draw.rect(screen, (150, 90, 200), (390, 160, 70, 130), border_radius=14)
# 画像(自作の PNG を読み込み、拡大・回転)
img = pygame.image.load("icon.png").convert_alpha()
screen.blit(img, (300, 250))
screen.blit(pygame.transform.scale(img, (64, 64)), (330, 236))
screen.blit(pygame.transform.rotate(img, 45), (405, 20))
# 文字(日本語は日本語の入ったフォントファイルを指定する)
jp = pygame.font.Font("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", 22)
screen.blit(jp.render("こんにちは Pygame", True, (20, 20, 20)), (20, 125))
pygame.display.flip()
pygame.image.save(screen, "basics.png") # 画面を画像に保存
pygame.quit()
保存した basics.png(480×320)には、左上に黒い枠の付いた赤い四角、その右に青い円、右上に斜めの線と45度回した顔の画像、下に緑の三角・黄色の楕円・角の丸い紫の四角、右下に32ピクセルと64ピクセルの顔の画像が描かれていました。円の中心 (240, 70) の画素は (60, 120, 220, 255) で、指定した色そのものです。
| 描きたいもの | 関数 | メモ |
|---|---|---|
| 四角 | draw.rect(面, 色, Rect, width=0) |
width を付けると枠だけ。border_radius で角丸 |
| 円・楕円 | draw.circle・draw.ellipse |
円は中心と半径、楕円は外側の Rect |
| 線・多角形 | draw.line・draw.polygon |
なめらかな線は draw.aaline |
| 画像 | image.load(...).convert_alpha() と blit |
透過 PNG は convert_alpha、透過なしは convert |
| 拡大・回転 | transform.scale・rotate |
ドット絵をくっきり拡大するなら scale(補間なし) |
| 文字 | font.Font(ファイル, 大きさ).render(文字, True, 色) |
True はなめらかに描く指定 |
日本語が□(豆腐)になる理由と直し方
pygame.font.Font(None, 28) の既定のフォントは freesansbold.ttf で、日本語の字形を持っていません。 手元で「Default: こんにちは」を描くと、英字は表示され、日本語の5文字は四角が5つ並びました。日本語の入ったフォントのファイル(.ttf・.otf・.ttc)を指定すると正しく表示されます。
日本語の文字を出すとき
Font(None, 28) や SysFont("meiryo", 28) だけで、ほかの PC でも出ると考えるFont("fonts/xxx.ttf", 28) でファイルを直接指定するSysFont はその PC に入っているフォントを探すだけ。無ければ既定のフォントに戻り、□になる
手元の Linux で pygame.font.match_font("meiryo") は None(メイリオは Windows のフォント)、match_font("notosanscjkjp") は太さ Black の NotoSansCJK-Black.ttc を返しました。名前で探すと、意図しない太さや別のフォントが選ばれることがあります。 公式ドキュメントも、SysFont は見つからなければ代わりのフォント、最後は既定のフォントを読み込むと説明しています。フォントを同梱するときは、そのフォントのライセンスが再配布を認めているかを確かめます。
もう1つの注意は改行です。pygame 2.6.1 の render は1行だけで、公式ドキュメントにも「改行文字は描かれない」とあります。"a\nb" を描くと高さ13ピクセルの1行になりました。複数行は自分で split("\n") して1行ずつ描きます。pygame-ce は 2.1.4 から改行で行が分かれ、同じ文字列が高さ30ピクセルの2行になりました(wraplength で幅を決めた折り返しもできます)。
キー入力・マウスのイベント処理
この節では、キーやマウスの操作を受け取る2つの方法と、使い分けが分かります。
「押した瞬間」はイベント(KEYDOWN)で、「押している間」は key.get_pressed() か、自分で押下中のキーを記録して判定します。
| 知りたいこと | 使うもの | 向く操作 |
|---|---|---|
| キーを押した・離した瞬間 | event.type == pygame.KEYDOWN / KEYUP と event.key |
ジャンプ・決定・メニューの移動・一時停止 |
| 押し続けているか | pygame.key.get_pressed()[pygame.K_LEFT] |
左右の移動・加速 |
| マウスのクリック | MOUSEBUTTONDOWN と event.pos・event.button |
ボタン・タイル選択 |
| マウスの位置 | pygame.mouse.get_pos() |
照準・ドラッグ |
| ウィンドウを閉じた | event.type == pygame.QUIT |
終了処理 |
画面の無い環境では本物のキーを押せないので、pygame.event.post でイベントを送って確かめました。
pygame.event.post(pygame.event.Event(pygame.KEYDOWN, key=pygame.K_RIGHT))
pygame.event.post(pygame.event.Event(pygame.KEYUP, key=pygame.K_RIGHT))
pygame.event.post(pygame.event.Event(pygame.MOUSEBUTTONDOWN, pos=(50, 40), button=1))
for e in pygame.event.get():
print(pygame.event.event_name(e.type), e.dict)
pygame.event.post(pygame.event.Event(pygame.KEYDOWN, key=pygame.K_RIGHT))
pygame.event.pump()
print("get_pressed()[K_RIGHT] =", pygame.key.get_pressed()[pygame.K_RIGHT])
AudioDeviceAdded {'which': 0, 'iscapture': 0}
AudioDeviceAdded {'which': 0, 'iscapture': 1}
WindowShown {'window': None}
KeyDown {'key': 1073741903}
KeyUp {'key': 1073741903}
MouseButtonDown {'pos': (50, 40), 'button': 1}
get_pressed()[K_RIGHT] = False
分かったことが2つあります。1つ目は、自分が送ったイベントの前に、音の装置やウィンドウ表示のイベントも届くことです。event.type で必要なものだけを選ぶ書き方にします。2つ目は、送った KEYDOWN は get_pressed() に反映されないことです。get_pressed() は OS から見たキーの状態を返すためです。後のよけゲームでは、KEYDOWN で集合に足し KEYUP で外す形で押下中のキーを自分で持ち、本物のキーでも自動操作でも同じに動くようにしています。
当たり判定とSprite・Group(キャラクターの管理)
この節では、Rect と mask の当たり判定の違い、Sprite と Group でキャラクターをまとめて動かす方法、判定の速さが分かります。
当たり判定の基本は四角どうしの重なり(Rect.colliderect)です。 円や透過のある絵で、見た目どおりに判定したいときだけ mask(画素単位)を使います。
a = pygame.Surface((40, 40), pygame.SRCALPHA); pygame.draw.circle(a, (255, 0, 0), (20, 20), 20)
b = pygame.Surface((40, 40), pygame.SRCALPHA); pygame.draw.circle(b, (0, 0, 255), (20, 20), 20)
ra = a.get_rect(topleft=(0, 0))
rb = b.get_rect(topleft=(34, 34)) # 右下の角どうしだけ重なる位置
ma, mb = pygame.mask.from_surface(a), pygame.mask.from_surface(b)
print(ra.colliderect(rb), ma.overlap(mb, (rb.x - ra.x, rb.y - ra.y)))
| 2つの円(直径40)の位置 | colliderect(四角) | mask.overlap(形どおり) |
|---|---|---|
| (0,0) と (34,34):角だけ重なる | True(当たり) | None(当たっていない) |
| (0,0) と (26,26):円が重なる | True | (37, 28)(重なった最初の画素) |
四角の判定では、円の外の透明な角が触れただけで当たりになりました。弾がかすっただけで当たる、と感じたら mask か、Rect を少し小さくした判定用の四角(rect.inflate(-10, -10))を使います。 なお、Rect(0, 0, 40, 40) の collidepoint(39, 39) は True、collidepoint(40, 40) は False でした。右端・下端の座標は Rect の外です。
Sprite と Group の書き方
pygame.sprite.Sprite を継承したクラスに image(見た目)と rect(位置と大きさ)を持たせると、Group にまとめて update()・draw()・当たり判定ができます。
class Block(pygame.sprite.Sprite):
def __init__(self, x):
super().__init__() # これを忘れると Group に入れたときにエラー
self.image = pygame.Surface((20, 20))
self.image.fill((250, 110, 80))
self.rect = self.image.get_rect(midbottom=(x, 0))
def update(self):
self.rect.y += 3
if self.rect.top > 360:
self.kill() # 所属するすべての Group から外れる
blocks = pygame.sprite.Group(Block(100), Block(200))
blocks.update() # 全員の update() を呼ぶ
blocks.draw(screen) # 全員の image を rect の位置に描く
hit = pygame.sprite.spritecollideany(player, blocks) # 当たった1つ(無ければ None)
spritecollideany
当たった Sprite を1つ返す。当たったかどうかだけ知りたいときに速い。
ゲームオーバー判定
spritecollide
当たった Sprite の一覧を返す。第3引数を True にすると当たったものを消す。
アイテム回収
groupcollide
Group どうしの当たりを辞書で返す。弾の Group と敵の Group など。
シューティング
collide_mask
上の関数の collided 引数に渡すと、形どおりの判定になる。
丸い・細い絵
1,000個の Sprite と1つのプレイヤーの判定を200回くり返して1回あたりの時間を測りました(2回測った範囲)。
| 判定の方法 | pygame 2.6.1 | pygame-ce 2.5.8 |
|---|---|---|
spritecollide(Rect) |
38〜40µs | 49〜132µs |
spritecollide+collide_mask |
293〜389µs | 321〜861µs |
Rect.collidelistall |
29〜70µs | 34µs |
共有のマシンなので数値はぶれますが、mask の判定は Rect の約7〜10倍(pygame-ce では約7倍)の時間がかかりました。 それでも1,000個で1ms 未満なので、1フレーム16.7ms の予算の中には収まります。先に Rect で候補を絞り、当たった候補だけ mask で確かめるのが一般的です(spritecollide に collide_mask を渡すと、この順で判定されます)。
Pygameで音を鳴らす(mixer:効果音とBGM)
この節では、効果音と BGM の読み込み・再生の書き方と、音が出ないときの確かめ方が分かります。
短い効果音は pygame.mixer.Sound、長い BGM は pygame.mixer.music を使います。 Sound はファイル全体をメモリに読み込んで何個も重ねて鳴らせ、music はファイルを少しずつ読みながら1曲だけ流します。
import math, struct, wave
# 自作の効果音:440Hz・0.2秒の WAV を作る
with wave.open("beep.wav", "wb") as w:
w.setnchannels(1); w.setsampwidth(2); w.setframerate(22050)
w.writeframes(b"".join(struct.pack("<h", int(8000 * math.sin(2 * math.pi * 440 * i / 22050)))
for i in range(int(22050 * 0.2))))
pygame.mixer.init()
beep = pygame.mixer.Sound("beep.wav")
print(pygame.mixer.get_init(), round(beep.get_length(), 3), beep.get_volume())
beep.play() # 効果音(重ねて鳴らせる)
pygame.mixer.music.load("beep.wav") # BGM(ゲームでは OGG などの長い曲)
pygame.mixer.music.play(-1) # -1 でくり返し
(44100, -16, 2) 0.2 1.0
手元は音の装置が無いので、SDL_AUDIODRIVER=dummy で読み込みまでを確かめました。mixer は 44,100Hz・16ビット・ステレオで初期化され、自作の WAV は長さ0.2秒・音量1.0として読み込まれました。音が出ないときは、まず pygame.mixer.get_init() が None でないかを見ます。 None なら初期化に失敗しています。mixer.init() を呼ばずに Sound を作ると pygame.error: mixer not initialized になりました。
Pythonでゲームを作る:よけゲームの全コード(112行)
この節では、ここまでの部品を組み合わせた小さなゲーム1本の全コードと、動かした結果が分かります。
上から落ちてくるブロックを左右の矢印キーでよけ、生き残ったフレーム数が得点になるゲームです。 10秒ごとにブロックの出る間隔が短くなり、落ちる速さも上がります。当たると「GAME OVER」と出て、R キーでやり直し、ESC で終了します。素材は図形だけです。
"""よけゲーム:上から落ちてくるブロックを左右キーでよける"""
import os
import random
import sys
import pygame
W, H = 480, 360
FPS = 60
BG = (24, 28, 40)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((36, 20), pygame.SRCALPHA)
pygame.draw.rect(self.image, (90, 200, 255), self.image.get_rect(), border_radius=6)
self.rect = self.image.get_rect(midbottom=(W // 2, H - 12))
self.speed = 5
def update(self, held):
dx = (pygame.K_RIGHT in held) - (pygame.K_LEFT in held)
self.rect.x += dx * self.speed
self.rect.clamp_ip(pygame.Rect(0, 0, W, H))
class Block(pygame.sprite.Sprite):
def __init__(self, speed):
super().__init__()
size = random.randint(16, 40)
self.image = pygame.Surface((size, size))
self.image.fill((250, 110, 80))
self.rect = self.image.get_rect(midbottom=(random.randint(size, W - size), 0))
self.speed = speed
def update(self, held):
self.rect.y += self.speed
if self.rect.top > H:
self.kill() # 画面外に出たら Group から外す
def main(autoplay=None, max_frames=None, shot_at=(), shot_dir="."):
pygame.init()
screen = pygame.display.set_mode((W, H))
pygame.display.set_caption("よけゲーム")
clock = pygame.time.Clock()
font_path = os.environ.get("JP_FONT") # 日本語フォントのファイル(無ければ英字だけ)
font = pygame.font.Font(font_path, 20)
player = Player()
blocks = pygame.sprite.Group()
all_sprites = pygame.sprite.Group(player)
held = set() # 押されているキー(KEYDOWN/KEYUP から自分で管理)
frame = score = 0
game_over = False
spawn_every = 30
running = True
while running:
# 1. 入力
if autoplay:
autoplay(frame, player, blocks)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
held.add(event.key)
if event.key == pygame.K_ESCAPE:
running = False
if game_over and event.key == pygame.K_r:
return main(autoplay, max_frames, shot_at, shot_dir)
elif event.type == pygame.KEYUP:
held.discard(event.key)
# 2. 更新
if not game_over:
frame += 1
score += 1
if frame % 600 == 0:
spawn_every = max(8, spawn_every - 4) # 10秒ごとに難しく
if frame % spawn_every == 0:
b = Block(speed=3 + frame // 600)
blocks.add(b)
all_sprites.add(b)
all_sprites.update(held)
if pygame.sprite.spritecollideany(player, blocks):
game_over = True
# 3. 描画
screen.fill(BG)
all_sprites.draw(screen)
hud = font.render(f"SCORE {score} FPS {clock.get_fps():.0f}", True, (230, 230, 230))
screen.blit(hud, (10, 8))
if game_over:
msg = font.render("GAME OVER R:もう一度 ESC:終了", True, (255, 220, 80))
screen.blit(msg, msg.get_rect(center=(W // 2, H // 2)))
pygame.display.flip()
if frame in shot_at:
pygame.image.save(screen, f"{shot_dir}/frame_{frame:05d}.png")
if max_frames and (frame >= max_frames or game_over):
pygame.image.save(screen, f"{shot_dir}/last.png")
break
clock.tick(FPS)
pygame.quit()
return score
if __name__ == "__main__":
print("score:", main())
sys.exit()
保存して python dodge.py で起動します。日本語の「もう一度」「終了」を表示するには、環境変数 JP_FONT に日本語フォントのファイルの場所を入れます(入れないと英字以外は□になります)。
コードの読み方
- Player・Block:Sprite を継承し、
update(held)で自分を動かす。ブロックは画面の下に出たらkill()で消え、Group が大きくなり続けない。 - held:KEYDOWN で足し KEYUP で外す、押しているキーの集合。
(K_RIGHT in held) - (K_LEFT in held)で -1・0・1 の向きにする。 - clamp_ip:プレイヤーを画面の中に押し戻す。
- spritecollideany:プレイヤーとブロックの Group の当たりを1回で判定する。
- autoplay 引数:ふだんは None。テストのときに、自動でキーを送る関数を渡せる。
画面の無い環境で自動操作して動かした結果
自分の真上120ピクセル以内に近いブロックがあれば反対へ逃げる、という簡単な自動操作(pygame.event.post でキーを送る)を作り、乱数の種を変えて3回動かしました。
import os, sys, random, time
os.environ["SDL_VIDEODRIVER"] = "dummy"
os.environ["SDL_AUDIODRIVER"] = "dummy"
os.environ.setdefault("JP_FONT", "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc")
import pygame
import dodge
seed = int(sys.argv[1]) if len(sys.argv) > 1 else 1
random.seed(seed)
state = {"key": None}
log = []
def press(key):
if state["key"] != key:
if state["key"] is not None:
pygame.event.post(pygame.event.Event(pygame.KEYUP, key=state["key"]))
if key is not None:
pygame.event.post(pygame.event.Event(pygame.KEYDOWN, key=key))
state["key"] = key
def bot(frame, player, blocks):
# 自分の真上 120px 以内で近いブロックを避ける簡単な操作
danger = [b for b in blocks if b.rect.bottom > player.rect.top - 120 and abs(b.rect.centerx - player.rect.centerx) < 50]
if danger:
d = min(danger, key=lambda b: player.rect.top - b.rect.bottom)
key = pygame.K_LEFT if d.rect.centerx >= player.rect.centerx else pygame.K_RIGHT
if player.rect.left < 40: key = pygame.K_RIGHT
if player.rect.right > dodge.W - 40: key = pygame.K_LEFT
else:
key = None
press(key)
t = time.perf_counter()
score = dodge.main(autoplay=bot, max_frames=int(os.environ.get("MAXF", "3600")),
shot_at=(60, 300, 900), shot_dir="out")
print("seed", seed, "score", score, "sec %.1f" % (time.perf_counter() - t))
$ for s in 1 2 3; do SDL_VIDEODRIVER=dummy MAXF=3600 python autoplay.py $s; done
seed 1 score 1044 sec 17.0
seed 2 score 1649 sec 27.0
seed 3 score 2538 sec 41.3
| 保存した画面 | 写っていたもの(480×360) |
|---|---|
| 60フレーム目 | 左上に「SCORE 60 FPS 61」、ブロックが2つ、下の中央に水色のプレイヤー |
| 300フレーム目 | 「SCORE 300 FPS 61」、ブロックが5つ。プレイヤーはまだ中央付近 |
| 900フレーム目 | 「SCORE 900 FPS 62」、ブロックが5つ。プレイヤーは左寄りに逃げている |
| ゲームオーバー | 「SCORE 2538」、中央に黄色で「GAME OVER R:もう一度 ESC:終了」、左下でプレイヤーにブロックが重なっている |
得点(=フレーム数)を60で割ると、経過時間とほぼ一致しました(1044÷60=17.4秒に対して17.0秒)。ゲームは60FPSで動いています。同じ dodge.py を pygame-ce 2.5.8 でも1行も変えずに動かせ、600フレームが9.8秒でした。このように、イベントを送って画面を画像に保存する形にしておくと、ゲームのテストを自動で回せます。
改造のアイデア
- 1ハイスコアを残すjson でファイルに書き、起動時に読む
- 2効果音を付ける当たったときに mixer.Sound を play
- 3画像に差し替えるSurface の代わりに image.load(...).convert_alpha()
- 4タイトル画面を作るscene 変数で「タイトル・プレイ・終了」を切り替える
- 5時間で動かすtick の戻り値(ms)を掛けて、FPS が落ちても速さを保つ
FPSとclock.tickの実測(何FPSで動くか)
この節では、clock.tick の種類ごとの実際の FPS と CPU の使い方、絵の数と1フレームの時間の関係が分かります。
clock.tick(60) は「1秒に60回を超えない」ように待つ関数で、ちょうど60回にそろえる関数ではありません。 公式ドキュメントは、1フレームに1回呼ぶこと、get_fps() は直近10回の tick の平均であることを説明しています。
画面を塗って flip するだけのループを、tick の呼び方を変えて300フレームずつ測りました。
| 呼び方 | 実測 FPS(pygame) | 実測 FPS(pygame-ce) | 1フレームの中央値・最大 |
|---|---|---|---|
tick(60) |
61.0 | 61.2 | 16.18ms・27.84ms |
tick(30) |
30.2 | 30.2 | 33.19ms・37.14ms |
tick_busy_loop(60) |
48.4 | 42.7 | 16.00ms・178.78ms |
tick()(上限なし) |
10,686 | 10,858 | 0.04ms・10.99ms |
- 60ではなく61になる理由:tick はミリ秒の整数で待つので、1フレームが16ms(62.5FPS)と17ms(58.8FPS)のあいだになります。戻り値も
16・16と整数のミリ秒でした。 - tick_busy_loop は正確だが重い:中央値は16.00ms とぴったりでしたが、待つ間も CPU を使い続け、CPU 時間÷経過時間は pygame で45%、pygame-ce で96% でした(tick(60) は1%)。共有のマシンでは、ほかの処理に押されて178ms 止まるフレームが出て、平均では48.4FPS に落ちました。公式ドキュメントも、CPU を多く使うと注意しています。ノート PC の電池やほかのアプリを考えると、ふつうは
tickを使います。 - 上限なしは1万FPS を超える:何もしないループは速すぎて PC ごとに速さが変わります。上限を決めるか、次の「時間で動かす」書き方にします。
FPSが落ちても同じ速さで動かす(dt)
dt = clock.tick(60) / 1000 で前のフレームからの秒数が分かります。x += 240 * dt(1秒に240ピクセル)のように「秒あたりの速さ×dt」で動かすと、FPS が下がってもキャラクターの速さは変わりません。
小数の速さで動かすとき
rect.x += 0.4 のように Rect に小数を足し続けるpygame.math.Vector2 など)で持ち、描く直前に rect.center = pos とする。pygame-ce なら FRectRect の座標は整数。手元で 0.4 を10回足すと x は0のまま、0.5 を10回足すと10になった
Vector2 の x に 0.4 を10回足すと 3.9999999999999996(ほぼ4)になり、正しく進みました。
描く絵の数と1フレームの時間
24×24ピクセルの透過 PNG(自作)を Sprite にして、数を変えて update と draw の時間を60フレーム測りました(中央値。2回測った範囲)。
| Sprite の数 | load のまま(pygame) | convert_alpha()(pygame) | convert_alpha()(pygame-ce) |
|---|---|---|---|
| 100個 | 1.00〜3.62ms | 0.29ms | 0.22〜0.29ms |
| 1,000個 | 22.41〜35.26ms | 1.32〜1.34ms | 0.81〜1.41ms |
| 5,000個 | 45.43〜93.39ms | 5.37〜5.45ms | 3.40〜7.56ms |
画像を読み込んだあと convert_alpha() を付けるだけで、1,000個の描画が約17〜27倍速くなりました。 付けないと、毎回の blit のたびに画面の画素の形式へ変換が走るためです。付けない場合は1,000個で60FPS の予算(16.7ms)を超えました。pygame と pygame-ce の差は、この環境では測るたびのぶれと同じくらいで、どちらが速いとは言えませんでした。
pygameとpygame-ceの違い(2026年10月29日時点)
この節では、pygame-ce が何で、pygame とどちらを選べばよいか、同じ環境に入れたときに起きることが分かります。
pygame-ce(pygame Community Edition)は、pygame の中心の開発者たちが元のプロジェクトで開発を続けられなくなったあとに分かれた(フォークした)版です。 公式の README は、より頻繁なリリース、継続的な不具合の修正と機能の追加、より民主的な運営を目標に挙げています。パッケージ名は pygame-ce ですが、import 名は同じ pygame で、多くのコードはそのまま動きます。
pygame(2.6.1)
PyPI の最新は2024年9月29日公開の 2.6.1
wheel は Python 3.13 まで
解説記事・書籍・質問の答えが多い
同梱の SDL は 2.28.4(手元)
pygame-ce(2.5.8)
PyPI の最新は2026年8月9日公開の 2.5.8
wheel は Python 3.10〜3.15
FRect・文字の改行・IS_CE など追加あり
同梱の SDL は 2.32.10(手元)
版の番号は pygame-ce のほうが小さく見えますが、別のプロジェクトなので番号は比べられません。ライセンスはどちらも LGPL(pygame-ce は LGPL-2.1-or-later)です。
| 違い | pygame 2.6.1 | pygame-ce 2.5.8 |
|---|---|---|
| 小数の座標の Rect | 無い(Rect は整数) | FRect(pygame-ce 2.2 で追加) |
| render の改行 | 描かれない(1行) | 行が分かれる・wraplength(2.1.4 から) |
| どちらかを調べる | IS_CE が無い |
pygame.IS_CE が 1 |
| Python 3.14 | wheel が無く、ビルドで失敗 | wheel があり入る |
| import の表示 | pygame 2.6.1 (SDL 2.28.4, ...) |
pygame-ce 2.5.8 (SDL 2.32.10, ...) |
どちらで動いているかは、公式ドキュメントのとおり getattr(pygame, "IS_CE", False) で分かります。両方で動かすコードを書くときの分岐に使えます。
両方を同じ環境に入れてはいけない
pygame と pygame-ce は、どちらも pygame というフォルダーに入ります。 同じ venv に両方入れてから片方を消すと、もう片方のファイルまで消えます。手元で pygame→pygame-ce の順に入れ、pygame を pip uninstall した直後の結果です。
$ pip install pygame
$ pip install pygame-ce
$ python -c "import pygame; print(pygame.IS_CE, pygame.version.ver)"
pygame-ce 2.5.8 (SDL 2.32.10, Python 3.13.18)
1 2.5.8
$ pip uninstall -y pygame
Successfully uninstalled pygame-2.6.1
$ python -c "import pygame; print(pygame.version.ver)"
AttributeError: module 'pygame' has no attribute 'version'
$ pip list | grep -i pygame
pygame-ce 2.5.8
pip list には pygame-ce が残っているのに、中身が壊れています。pip install --force-reinstall pygame-ce で入れ直すと、import pygame が再び動きました。切り替えるときは両方を uninstall してから片方だけ入れるか、venv を作り直します。
Pygameのゲームをexe・ブラウザで配る(PyInstaller・pygbag)
この節では、作ったゲームを Python の入っていない PC やブラウザで遊んでもらう方法と、注意点が分かります。この節は公式ドキュメントで確かめた範囲です。
Windows の exe にするなら PyInstaller、ブラウザで遊べる形にするなら pygbag が使われます。 PyInstaller 6.22.3(2026年9月12日公開)の公式ドキュメントで確かめた主なオプションです。
pip install pyinstaller
pyinstaller --onefile --windowed --add-data "fonts:fonts" dodge.py
# 出来上がりは dist/ フォルダーの中
| オプション | 意味(公式の説明) |
|---|---|
--onefile |
1つの実行ファイルにまとめる |
--windowed |
Windows・macOS で黒いコンソール画面を出さない(macOS では .app を作る) |
--add-data 元:先 |
画像・音・フォントなどのファイルを一緒に入れる。区切りはコロン |
- 1配る OS の上で作るPyInstaller はクロスコンパイルをしない。Windows 用は Windows で、Mac 用は Mac で作ると公式に書かれている
- 2素材の場所は __file__ から求める公式は
Path(__file__).resolve().with_name(...)の形を勧めている。作業フォルダーからの相対パスだと FileNotFoundError になる - 3フォントと素材のライセンスを確かめるexe に同梱すると再配布になる。自作か、再配布を認める素材だけを入れる
- 4Python を入れていない PC で起動してみる作った PC では、入っている Python が使われて動いてしまうことがある
公式の説明では、onefile のときバンドルは起動のたびに一時フォルダーへ展開され、その場所が sys._MEIPASS に入ります。ただし素材の場所は __file__ から求める方法が推奨されています。
ブラウザで遊べるようにする(pygbag)
pygbag は、pygame のコードをブラウザで動かすためのツールです。公式のページでは、メインのファイルを main.py にすること、ゲームループを async def main() にして毎フレーム await asyncio.sleep(0) でブラウザに制御を返すこと、音は OGG・画像は PNG・WEBP・JPG を使うこと、を求めています。既存のコードはいくらか書き換えが要ると公式も書いています。async と await の考え方は、関連記事「Python asyncioの使い方|TaskGroupとtimeoutを実測で解説」で扱っています。
Pygame・Arcade・Pyxel・Godotの判断表(どれを選ぶか)
この節では、Python で2Dゲームを作るときの主な選択肢の違いと、目的別の選び方が分かります。
「Python でゲームの仕組みから学ぶ」なら Pygame、「レトロ風を手早く」なら Pyxel、「Python で新しめの描画機能」なら Arcade、「本格的に作って多くの機種へ出す」なら Godot が目安です。版とライセンスは2026年10月29日時点の PyPI と各公式サイトで確かめました。
| 項目 | Pygame | Arcade | Pyxel | Godot |
|---|---|---|---|---|
| 種類 | ライブラリ(SDL) | ライブラリ(2D) | レトロゲームエンジン | ゲームエンジン(エディター付き) |
| 言語 | Python | Python | Python | GDScript・C#・C++ |
| 最新版 | 2.6.1(ce は 2.5.8) | 3.3.3 | 2.9.9 | 4.7.2 |
| Python の版(PyPI) | 3.6以上(wheel は3.13まで) | 3.10以上 | 3.11以上 | 使わない |
| ライセンス | LGPL | MIT | MIT | MIT |
| 特徴・制約 | ループを自分で書く。自由度が高い | シェーダーも使える。公式は Web・モバイルは対象外 | 16色・同時に4音までの決まり | デスクトップ・モバイル・Web に出せる(ゲーム機は他社を通じて)。C# は Web 不可 |
| やりたいこと | 向く道具 | 理由 |
|---|---|---|
| Python の授業・学習でゲームを作る | Pygame | ループ・座標・クラスを自分で書くので仕組みが見える。資料が多い |
| ドット絵のミニゲームを短時間で | Pyxel | 16色・4音の決まりの中で作るので、作る範囲が絞られる |
| Python で GPU を使った表現も | Arcade | シェーダーなど新しめの描画機能を Python から使える |
| スマホ・ブラウザへ出す | Godot | エクスポートの仕組みがあり、エディターで場面を組める |
| Python 3.14 で Pygame の書き方 | pygame-ce | wheel があり、同じ import pygame で使える |
| 3D のゲーム | Godot など | Pygame・Pyxel は2D向け |
Godot のライセンスについて、公式は「作ったゲームの内容には Godot のライセンスや著作権は及ばない」とし、配布物の説明書きなどに著作権表示とライセンス文を載せることを求めています。
Pygameのよくあるエラーと直し方
この節では、Pygame で出やすいエラーの原文と、原因・直し方が分かります。どれも手元で起こした原文です。
| エラー原文 | 原因 | 直し方 |
|---|---|---|
ModuleNotFoundError: No module named 'pygame' |
pygame を入れていない Python で動かした | venv を有効にして python -m pip install pygame |
pygame.error: video system not initialized |
pygame.init() の前に event.get() を呼んだ |
最初に pygame.init() |
pygame.error: No video mode has been set |
set_mode の前に convert() を呼んだ |
画像の読み込みを set_mode の後へ |
FileNotFoundError: No file 'player.png' found in working directory '...' |
作業フォルダーからの相対パスで探した | Path(__file__).with_name("player.png") で指定 |
pygame.error: font not initialized |
init 前に font.Font を作った |
pygame.init() の後で作る |
ValueError: invalid color name |
色の名前の綴り違い(例:'blu') |
'blue' か (0, 0, 255) |
pygame.error: display Surface quit |
pygame.quit() の後に描いた |
quit はループを抜けた最後に1回 |
pygame.error: mixer not initialized |
mixer を初期化せずに Sound を作った | pygame.init() か mixer.init() を先に |
AttributeError: 'A' object has no attribute '_Sprite__g' |
Sprite の __init__ で super().__init__() を呼んでいない |
最初の行に super().__init__() |
AttributeError: 'A' object has no attribute 'image' |
Sprite に image か rect が無いまま draw した |
self.image と self.rect を必ず作る |
RuntimeError: Unable to run "sdl-config"... |
wheel の無い Python の版(3.14 など)に pygame を入れた | pygame-ce を入れるか、Python 3.13 以前で venv を作る |
AttributeError: module 'pygame' has no attribute 'version' |
pygame と pygame-ce を同じ環境に入れ、片方を消した | 両方 uninstall して入れ直す |
ModuleNotFoundError の原因の切り分けは、関連記事「ModuleNotFoundErrorの原因と直し方|No module named」で扱っています。
エラーにならないつまずき
| 症状 | 原因 | 直し方 |
|---|---|---|
| ウィンドウが「応答なし」になる | ループで event.get() を呼んでいない |
毎フレーム必ずイベントを取り出す |
| 日本語が□になる | 既定のフォントに日本語が無い | 日本語フォントのファイルを指定 |
| キャラクターが動かない・止まる | Rect に小数を足している | 位置は float で持つ・FRect |
| 動いた跡が残る | 毎フレームの fill が無い |
描く前に背景を塗る |
| PC によって速さが違う | tick が無い・dt を使っていない |
tick(60) と dt |
| 絵が多いと重い | 読み込んだ画像を convert していない | convert_alpha()(1,000個で約17〜27倍速くなった) |
よくある質問
Q. Pygameとは何ですか? A. Python で2Dゲームやマルチメディアのアプリを作るためのライブラリです。中で SDL が動いていて、画面の描画・キーボードやマウスの入力・音の再生を Python から扱えます。無料(LGPL)です。
Q. Pygameは無料ですか?作ったゲームを売れますか? A. 無料です。pygame-ce の README には、ライブラリそのものを変更しなければ、ソースを公開しないゲームや商用のゲームにも使えると書かれています。同梱する画像・音・フォントは、それぞれのライセンスを別に確かめます。
Q. pygame と pygame-ce のどちらを入れればよいですか?
A. Python 3.14 を使うなら pygame-ce です(2026年10月29日時点で pygame の wheel は 3.13 まで)。教材が pygame 前提で Python 3.13 以前なら pygame でも動きます。どちらも import pygame なので、同じ環境に両方は入れません。
Q. Pygameの使い方は何から覚えればよいですか? A. ゲームループ(event.get・更新・描画・tick)、Rect と座標、draw と blit、KEYDOWN と get_pressed、Sprite と Group、の順です。この記事のよけゲームを写して動かし、1か所ずつ改造すると身に付きます。
Q. 何FPSで作ればよいですか?
A. 動きのあるゲームは60FPS が一般的です。clock.tick(60) を1フレームに1回呼びます。手元の実測では61.0FPS(1フレーム中央値16.18ms)でした。パズルなど動きの少ないゲームは30FPS でも足ります。
Q. 日本語を表示するにはどうしますか?
A. 日本語の入ったフォントのファイルを pygame.font.Font("ファイルの場所", 大きさ) で指定します。Font(None, ...) の既定のフォントや、その PC に無い名前の SysFont では□になります。
Q. Pygameで3Dゲームは作れますか?
A. Pygame の描画は2D向けです。同梱の見本 glcube.py のように OpenGL と組み合わせて3Dを描く例はありますが、3D を本格的に作るなら Godot などのゲームエンジンが向きます。
Q. スマホで動くゲームは作れますか? A. Pygame はデスクトップ向けが中心です。ブラウザで遊べる形にする pygbag があり、スマホのブラウザから遊べるようにする道はあります。アプリストアに出すなら、エクスポートの仕組みがある Godot が向きます。
Q. exe にするにはどうしますか?
A. PyInstaller で pyinstaller --onefile --windowed ゲーム.py を実行します。Windows 用の exe は Windows の上で作ります。素材は --add-data で同梱し、場所は __file__ から求めます。
Q. 画面の無いサーバーや CI で Pygame を動かせますか?
A. 動かせます。SDL_VIDEODRIVER=dummy(音も使うなら SDL_AUDIODRIVER=dummy)を付けると、ウィンドウを出さずに同じコードが動きます。描いた画面は pygame.image.save で画像にして確かめられ、キー入力は pygame.event.post で送れます。
Q. Pygameは遅いですか? A. 手元では、convert_alpha 済みの小さな画像なら1,000個を1.3ms、5,000個を5.4ms 前後で描けました。1画面の2Dゲームなら60FPS を保てます。重いときは、画像の convert を忘れていないか、毎フレーム画像を読み込んでいないかを先に確かめます。
まとめ
- Pygame は SDL を土台にした Python の2Dゲーム用ライブラリ。venv で
pip install pygame。2026年10月29日時点で pygame 2.6.1 の wheel は Python 3.13 まで、3.14 なら pygame-ce。 - プログラムは「init → set_mode → ループ(event.get・更新・fill と描画・flip・tick)→ quit」。画面の無い環境でも
SDL_VIDEODRIVER=dummyで同じコードが動き、画像に保存して確かめられる。 - 日本語は日本語フォントのファイルを指定する。Rect は整数なので小数の移動は float か FRect。送ったキーのイベントは get_pressed に反映されないので、押下中のキーを自分で持つと自動テストもできる。
- tick(60) の実測は61.0FPS・CPU 1%。tick_busy_loop は CPU を45〜96% 使った。画像は convert_alpha するだけで1,000個の描画が約17〜27倍速くなった。
- 学習なら Pygame、レトロ風なら Pyxel、Python で新しめの描画なら Arcade、多くの機種へ出すなら Godot。pygame と pygame-ce は同じ環境に入れない。
次に読むとよい記事です。pip の基本は関連記事「pipコマンド一覧|installのバージョン指定とrequirements.txt」で扱っています。
参考・出典
確認日はいずれも 2026年10月29日です。
- pygame「pygame.time」(Clock.tick・tick_busy_loop・get_fps):https://www.pygame.org/docs/ref/time.html
- pygame「pygame.font」(Font.render・SysFont・match_font):https://www.pygame.org/docs/ref/font.html
- pygame-ce「README」:https://github.com/pygame-community/pygame-ce
- pygame-ce documentation:https://pyga.me/docs/
- pygame-ce「pygame.Rect / FRect」:https://pyga.me/docs/ref/rect.html
- pygame-ce「pygame.font」(改行・wraplength):https://pyga.me/docs/ref/font.html
- pygame-ce「pygame」(IS_CE):https://pyga.me/docs/ref/pygame.html
- PyPI「pygame」:https://pypi.org/project/pygame/
- PyPI「pygame-ce」:https://pypi.org/project/pygame-ce/
- PyInstaller「Using PyInstaller」:https://pyinstaller.org/en/stable/usage.html
- PyInstaller「Run-time Information」:https://pyinstaller.org/en/stable/runtime-information.html
- pygbag(pygame-web):https://pygame-web.github.io/
- Python Arcade Library:https://api.arcade.academy/en/latest/
- Python Arcade Library「Install」:https://api.arcade.academy/en/latest/get_started/install.html
- Pyxel「README」:https://github.com/kitao/pyxel
- Godot Engine:https://godotengine.org/
- Godot Engine「License」:https://godotengine.org/license/
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Pygameとは?使い方入門|よけゲーム全コードとFPS実測・pygame-ce比較」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。