【Pygame入門】ゲームプログラミング【Python】

Python用ゲームモジュール「Pygame」でゲームプログラミングする方法を入門者向けに解説しています。

【Pygameとは】できること

Pythonで2D/3Dゲームを作るには、ゲームライブラリを使うのが一般的です。
Pygameは、Pythonで2Dゲームを制作する際の最も有名なゲームライブラリです。

例えば次のようなブロック崩しゲームならば160行くらいのソースコードで作成できます。

ソースコード・解説 → 【Pygame】ブロック崩しの作り方

動画解説版

関連ページ
Pythonの導入方法 【Windows10】Pythonの環境構築法まとめ
Pygameの導入方法 Pygameのインストール方法
他のライブラリ 【Python】ゲームプログラミング・ライブラリ比較

【画面作成】サイズ、タイトル、背景色、テキスト、フルスクリーンの設定

Pythonモジュール「pygame」でゲームを作成するには、まず画面を作成します。

基本的な項目 メソッド
画面の背景色設定 screen.fill
画面サイズ設定 pygame.display.set_mode((width, height)) ※width:幅[px], height:高さ[px]
テキスト font.render、screen.blit
画面のタイトル設定 pygame.display.set_caption
フルスクリーン表示 pygame.display.set_mode((width, height), FULLSCREEN)※「オプションFULLSCREEN」を指定
# -*- coding:utf-8 -*-
import pygame
from pygame.locals import *
import sys

def main():
    pygame.init()                                             # Pygameの初期化
    screen = pygame.display.set_mode((400, 300))  # 400*300の画面
    # screen = pygame.display.set_mode((400, 300), FULLSCREEN)  # フルスクリーン(解除時は大きさ400*300の画面)
    pygame.display.set_caption("Test")                        # タイトルバーに表示する文字

    while (1):
        screen.fill((0,0,0))        # 画面を黒色(#000000)に塗りつぶし
        pygame.display.update()     # 画面を更新

        # イベント処理
        for event in pygame.event.get():
            if event.type == QUIT:  # 閉じるボタンが押されたら終了
                pygame.quit()       # Pygameの終了(画面閉じられる)
                sys.exit()


if __name__ == "__main__":
    main()

関連記事
【Pygame】画面設定(サイズ、タイトル、背景色、文字、フルスクリーン)

【図形描画】直線、長方形、円

直線、長方形、円などの図形を描画します。

# -*- coding:utf-8 -*-
import pygame
from pygame.locals import *
import sys


def main():
    pygame.init()                                               # Pygameの初期化
    screen = pygame.display.set_mode((300, 200))                # 大きさ600*500の画面を生成
    pygame.display.set_caption("GAME")                          # タイトルバーに表示する文字

    while (1):
        screen.fill((0,0,0))                                    # 画面を黒色に塗りつぶし

        # (0,0)から(80,80)まで線幅5pxで緑色(R=0, G=95, B=0)の直線を描く
        pygame.draw.line(screen, (0,95,0), (0,0), (80,80), 5)   # 直線の描画

        # 左上座標(10,10)、幅80px、高さ50pxの長方形を線幅5pxの緑色(R=0, G=80, B=0)で描く
        pygame.draw.rect(screen,(0,80,0),Rect(10,10,80,50),5)   # 四角形を描画(塗りつぶしなし)
        #pygame.draw.rect(screen,(0,80,0),Rect(10,10,80,50))    # 四角形を描画(塗りつぶし)
        
        # 左上の座標が(50,50)、幅が150、高さが50の矩形に内接する楕円を線幅5pxの緑色(R=0, G=100, B=0)で描く
        pygame.draw.ellipse(screen,(0,100,0),(50,50,200,100),5) # 円を描画(塗りつぶしなし)
        #pygame.draw.ellipse(screen,(0,100,0),(50,50,200,100))     # 円を描画(塗りつぶし)
    
        pygame.display.update()                                 # 画面を更新
        # イベント処理
        for event in pygame.event.get():
            if event.type == QUIT:                              # 閉じるボタンが押されたら終了
                pygame.quit()                                   # Pygameの終了(画面閉じられる)
                sys.exit()


if __name__ == "__main__":
    main()

関連記事
【Pygame】円の描画
【Pygame】直線の描画
【Pygame】長方形の描画

【画像描画】背景と主人公

Pygameでは「pygame.image.load()」で画像を読み込めます。
また、「screen.blit」で読み込んだ画像をウィンドウに貼り付けることができます。
今回はこれらを用いて、背景画像を読み込んでゲーム画面に主人公と背景を描画してみました。

# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import sys

def main():
    (w,h) = (400, 400)   # 画面サイズ
    (x,y) = (200, 200)   # プレイヤー画像の初期位置(画面中央)
    pygame.init()       # pygame初期化
    pygame.display.set_mode((w, h), 0, 32)  # 画面設定
    screen = pygame.display.get_surface()
    
    # 背景画像(bg.jpg)の取得
    bg = pygame.image.load("C:\prog\python\pygame\\bg.png").convert_alpha()    
    rect_bg = bg.get_rect()

    # プレイヤー画像(player.png)の取得
    player = pygame.image.load("C:\prog\python\pygame\player.png").convert_alpha()    
    rect_player = player.get_rect()
    rect_player.center = (x, y) # プレイヤー画像の初期位置

    while (1):
        pygame.display.update()             # 画面更新
        pygame.time.wait(30)                # 更新時間間隔
        screen.fill((0, 20, 0, 0))          # 画面の背景色
        screen.blit(bg, rect_bg)            # 背景画像の描画
        screen.blit(player, rect_player)    # プレイヤー画像の描画

        # 終了用のイベント処理
        for event in pygame.event.get():
            # 閉じるボタンが押されたとき
            if event.type == QUIT:          
                pygame.quit()
                sys.exit()

            # キーを押したとき
            if event.type == KEYDOWN:       
                if event.key == K_ESCAPE:   # Escキーが押されたとき
                    pygame.quit()
                    sys.exit()

if __name__ == "__main__":
        main()

【実行結果】
画面に背景画像(bg.jpg)と主人公画像(player.png)を描画できました。

【素材】背景画像:bg.png、背景画像:player.png

関連記事
【Pygame】背景・主人公の表示

【イベント】キーとマウスでキャラクタの操作

キーイベント編

Python用ゲームモジュール「Pygame」のキーイベント処理機能「pygame.event.get()」を用いて、矢印キー長押しでキャラクター画像を動かしてみました。

# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import sys

def main():
    (w,h) = (400, 400)  # 画面サイズ
    (x,y) = (200, 200)  # プレイヤー画像の初期配置座標(画面中央)
    pygame.init()       # pygame初期化
    pygame.display.set_mode((w, h), 0, 32)  # 画面設定
    screen = pygame.display.get_surface()

    # プレイヤー画像の取得
    player = pygame.image.load("C:\prog\python\pygame\player.png").convert_alpha()   
    rect_player = player.get_rect()
    rect_player.center = (x, y) # プレイヤー画像の初期位置

    # 背景画像(bg.jpg)の取得
    bg = pygame.image.load("C:\prog\python\pygame\\bg.png").convert_alpha()    
    rect_bg = bg.get_rect()

    while (1):
        # キーイベント処理(キャラクタ画像の移動)
        pressed_key = pygame.key.get_pressed()
        # 「←」キーが押されたらx座標を-5に移動
        if pressed_key[K_LEFT]:
            rect_player.move_ip(-5, 0)
        # 「→」キーが押されたらx座標を+5移動
        if pressed_key[K_RIGHT]:
            rect_player.move_ip(5, 0)
        # 「↑」キーが押されたらy座標を-5移動
        if pressed_key[K_UP]:
            rect_player.move_ip(0, -5)
        # 「↓」キーが押されたらy座標を+5移動
        if pressed_key[K_DOWN]:
            rect_player.move_ip(0, 5)

        pygame.display.update()     # 画面更新
        pygame.time.wait(30)        # 更新時間間隔
        screen.fill((0, 20, 0, 0))  # 画面の背景色

        # 背景画像の描画
        screen.blit(bg, rect_bg) 
        
        # プレイヤー画像の描画         
        screen.blit(player, rect_player)       

        # 終了用のイベント処理
        for event in pygame.event.get():
            # 閉じるボタンが押されたとき
            if event.type == QUIT:          
                pygame.quit()
                sys.exit()
            # キーを押したとき
            if event.type == KEYDOWN:       
                if event.key == K_ESCAPE:   # Escキーが押されたとき
                    pygame.quit()
                    sys.exit()

if __name__ == "__main__":
    main()

矢印キーでキャラクターを動かすことができました。

詳細ページ
詳細 矢印キー操作矢印キー操作(長押し)矢印キーでキャラクター操作矢印キー(長押し)でキャラクター操作

マウスイベント編

Pygameではイベントハンドラ「pygame.event.get()」でマウスポインタの移動を判定できます。
また、「event.pos」でマウスポインタの画面上の座標を取得できます。
今回はこれらを用いて、マウスポインタに追従させるようにキャラクターを移動させてみました。

# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import sys

def main():
    (w,h) = (400,400)   # 画面サイズ
    (x,y) = (200, 200)   # プレイヤー画像の初期位置(画面中央)
    pygame.init()       # pygame初期化
    pygame.display.set_mode((w, h), 0, 32)  # 画面設定
    screen = pygame.display.get_surface()

    # プレイヤー画像の取得
    player = pygame.image.load("C:\prog\python\pygame\player.png").convert_alpha()   
    rect_player = player.get_rect()
    rect_player.center = (x, y) # プレイヤー画像の初期位置

    # 背景画像(bg.jpg)の取得
    bg = pygame.image.load("C:\prog\python\pygame\\bg.png").convert_alpha()    
    rect_bg = bg.get_rect()

    while (1):
        pygame.display.update()             # 画面更新
        pygame.time.wait(30)                # 更新時間間隔
        screen.fill((0, 20, 0, 0))          # 画面の背景色
        screen.blit(bg, rect_bg)            # 背景画像の描画
        screen.blit(player, (x, y))         # プレイヤー画像の描画

        for event in pygame.event.get():
            # マウスポインタで画像も移動
            if event.type == MOUSEMOTION:
                x, y = event.pos
                x -= int(player.get_width() / 2)
                y -= int(player.get_height() / 2)
            # 終了用のイベント処理
            if event.type == QUIT:          # 閉じるボタンが押されたとき
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN:       # キーを押したとき
                if event.key == K_ESCAPE:   # Escキーが押されたとき
                    pygame.quit()
                    sys.exit()

if __name__ == "__main__":
        main()

マウスポインタのある場所にキャラクタ画像(player.png)を描画できました。

詳細ページ
詳細 マウスクリックでキャラクタ操作マウスクリック(長押し)でキャラクタ操作マウスポインタでキャラクタ操作

【スプライト】グループで高速描画

スプライトとは、キャラクターを低いCPU負荷で滑らかに動かすグラフィック技術のことです。
これまでにもキャラクターの操作(移動や回転など)を行ってきましたが、複数のキャラクターを扱うとコード量がどんどん増えていく問題があります。
Pygameでは「pygame.sprite」を使うとスプライトの管理、描画、衝突判定等を簡単にできます。
キャラクター操作に関する各種機能をスプライトクラスとして1つにまとめ、複数のキャラクターを扱いやすくしてみました。

# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import sys

SCREEN = Rect(0, 0, 400, 400)   # 画面サイズ

# スプライトのクラス
class Sprite(pygame.sprite.Sprite):
    # スプライトを作成(画像ファイル名, 位置(x, y), 速さ(vx, vy), 回転angle)
    def __init__(self, filename, (x, y), (vx, vy), angle=0):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(filename).convert_alpha()
        if angle != 0: self.image = pygame.transform.rotate(self.image, angle)
        w = self.image.get_width()
        h = self.image.get_height()
        self.rect = Rect(x, y, w, h)
        self.vx = vx
        self.vy = vy
        self.angle = angle

    def update(self):
        self.rect.move_ip(self.vx, self.vy)
        # 壁と衝突時の処理(跳ね返り)
        if self.rect.left < 0 or self.rect.right > SCREEN.width:
            self.vx = -self.vx
        if self.rect.top < 0 or self.rect.bottom > SCREEN.height:
            self.vy = -self.vy
        # 壁と衝突時の処理(壁を超えないように)
        self.rect = self.rect.clamp(SCREEN)


# メイン
def main():
    pygame.init()
    screen = pygame.display.set_mode(SCREEN.size)
    # スプライトを作成(画像ファイル名, 位置(x, y), 速さ(vx, vy), 回転angle)
    player = Sprite("player.png", (200, 200), (2, 0), 0)
    enemy1 = Sprite("enemy1.png", (200, 200), (0, 2), 0)
    enemy2 = Sprite("enemy2.png", (200, 200), (2, 2), 10)
    # スプライトグループの作成
    group = pygame.sprite.RenderUpdates()
    # スプライトの追加
    group.add(player)
    group.add(enemy1)
    group.add(enemy2)
    clock = pygame.time.Clock()

    while (1):
        clock.tick(30)  # フレームレート(30fps)
        screen.fill((0, 20, 0)) # 画面の背景色
        # スプライトグループを更新
        group.update()
        # スプライトを描画
        group.draw(screen)
        # 画面更新
        pygame.display.update()
        # イベント処理
        for event in pygame.event.get():
            # 終了用のイベント処理
            if event.type == QUIT:          # 閉じるボタンが押されたとき
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN:       # キーを押したとき
                if event.key == K_ESCAPE:   # Escキーが押されたとき
                    pygame.quit()
                    sys.exit()

if __name__ == "__main__":
    main()

【実行結果】
3人のキャラクターを描画できました。

グループへの追加を簡潔化

「pygame.sprite.Sprite.init(self, self.containers)」を使うと、スプライトクラス内であらかじめグループを指定しているため、スプライトの作成するとそのままグループに追加されます。
(いちいち追加処理をせずに済みます)

# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import sys

SCREEN = Rect(0, 0, 400, 400)   # 画面サイズ

# スプライトのクラス
class Sprite(pygame.sprite.Sprite):
    def __init__(self, filename, (x, y), (vx, vy), angle=0):
        # デフォルトグループをセット
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = pygame.image.load(filename).convert_alpha()
        if angle != 0: self.image = pygame.transform.rotate(self.image, angle)
        w = self.image.get_width()
        h = self.image.get_height()
        self.rect = Rect(x, y, w, h)
        self.vx = vx
        self.vy = vy
        self.angle = angle

    def update(self):
        self.rect.move_ip(self.vx, self.vy)
        # 壁と衝突時の処理(跳ね返り)
        if self.rect.left < 0 or self.rect.right > SCREEN.width:
            self.vx = -self.vx
        if self.rect.top < 0 or self.rect.bottom > SCREEN.height:
            self.vy = -self.vy
        # 壁と衝突時の処理(壁を超えないように)
        self.rect = self.rect.clamp(SCREEN)


# メイン
def main():
    pygame.init()
    screen = pygame.display.set_mode(SCREEN.size)
    # スプライトグループを作成して
    group = pygame.sprite.RenderUpdates()
    # スプライトクラスにスプライトグループを割り当てる
    Sprite.containers = group
    # スプライトを作成(画像ファイル名, 位置(x, y), 速さ(vx, vy), 回転angle)
    player = Sprite("player.png", (200, 200), (2, 0), 0)
    enemy1 = Sprite("enemy1.png", (200, 200), (0, 2), 0)
    enemy2 = Sprite("enemy2.png", (200, 200), (2, 2), 10)
    clock = pygame.time.Clock()

    while (1):
        clock.tick(30)  # フレームレート(30fps)
        screen.fill((0, 20, 0)) # 画面の背景色
        # スプライトグループを更新
        group.update()
        # スプライトを描画
        group.draw(screen)
        # 画面更新
        pygame.display.update()
        # イベント処理
        for event in pygame.event.get():
            # 終了用のイベント処理
            if event.type == QUIT:          # 閉じるボタンが押されたとき
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN:       # キーを押したとき
                if event.key == K_ESCAPE:   # Escキーが押されたとき
                    pygame.quit()
                    sys.exit()

if __name__ == "__main__":
    main()

高速化

スプライトの描画を高速に行うために汚れたRectのアニメーション(dirty rect animation)という手法があります。 この手法は、画面を更新するときに、全体を描き換えずにスプライトが更新された一部分だを描き換えます。

# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import sys

SCREEN = Rect(0, 0, 400, 400)

# スプライトのクラス
class Sprite(pygame.sprite.Sprite):
    # スプライトを作成(画像ファイル名, 位置(x, y), 速さ(vx, vy), 回転angle)
    def __init__(self, filename, (x, y), (vx, vy), angle=0):
        pygame.sprite.Sprite.__init__(self, self.containers)
        self.image = pygame.image.load(filename).convert_alpha()
        if angle != 0: self.img = pygame.transform.rotate(self.image, angle)
        w = self.image.get_width()
        h = self.image.get_height()
        self.rect = Rect(x, y, w, h)
        self.vx = vx
        self.vy = vy
        self.angle = angle

    def update(self):
        self.rect.move_ip(self.vx, self.vy)
        # 壁と衝突時の処理(跳ね返り)
        if self.rect.left < 0 or self.rect.right > SCREEN.width:
            self.vx = -self.vx
        if self.rect.top < 0 or self.rect.bottom > SCREEN.height:
            self.vy = -self.vy
        # 壁と衝突時の処理(壁を超えないように)
        self.rect = self.rect.clamp(SCREEN)

    def draw(self, screen):
        screen.blit(self.img, self.rect)

# メイン
def main():
    pygame.init()
    screen = pygame.display.set_mode(SCREEN.size)

    # スプライトグループを作成
    group = pygame.sprite.RenderUpdates()
    Sprite.containers = group

    # スプライトを作成(画像ファイル名, 位置(x, y), 速さ(vx, vy), 回転angle)
    player = Sprite("C:\prog\python\pygame\player.png", (200, 200), (2, 0), 0)
    enemy1 = Sprite("C:\prog\python\pygame\enemy1.png", (200, 200), (0, 2), 0)
    enemy2 = Sprite("C:\prog\python\pygame\enemy2.png", (200, 200), (2, 2), 10)
    clock = pygame.time.Clock()

    # 背景の作成と描画(背景は最初に1回だけ描画)
    bg = pygame.Surface(SCREEN.size)
    bg.fill((0, 20, 0)) # 画面の背景色
    screen.blit(bg, (0,0))
    pygame.display.update()

    while (1):
        clock.tick(30)  # フレームレート(30fps)
        group.clear(screen, bg)

        # スプライトグループを更新(キャラクタ3体を一括して更新)
        group.update()

        # スプライトを更新
        dirty_rects = group.draw(screen)

        # updateにdirty rectを渡すとその部分だけ更新するので効率よい
        # 画面更新
        pygame.display.update(dirty_rects)

        # イベント処理
        for event in pygame.event.get():
            # 終了用のイベント処理
            if event.type == QUIT:          # 閉じるボタンが押されたとき
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN:       # キーを押したとき
                if event.key == K_ESCAPE:   # Escキーが押されたとき
                    pygame.quit()
                    sys.exit()

if __name__ == "__main__":
    main()
詳細ページ
詳細 スプライトスプライトグループスプライトグループ(デフォルト)スプライトグループ(高速描画)

【音楽】MP3ファイル、WAVファイルの再生

「pygame.mixer.music」を使うと、mp3等の音楽ファイルを再生できます。

# -*- coding: utf-8 -*-
import pygame.mixer
import time

# メイン
def main():
    pygame.mixer.init(frequency = 44100)    # 初期設定
    pygame.mixer.music.load("test.mp3")     # 音楽ファイルの読み込み(wavファイルも読み込み可能)
    pygame.mixer.music.play(1)              # 音楽の再生回数(1回) ※-1を指定するとループ
    time.sleep(100)                         # 音楽の再生時間
    pygame.mixer.music.stop()               # 再生の終了


if __name__ == '__main__':
    main()
詳細ページ
詳細 MP3ファイルの再生WAVファイルの再生

【制作例】ブロック崩し、卓球ゲーム(PONG)など

ゲーム制作例を以下にまとめました(それぞれの詳細ページに解説とソースコードがあります)。

ブロック崩し

■詳細→【Pygame】ブロック崩しの作り方

PONG

■詳細→【Pygame】卓球ゲーム(PONG風)

デジタル時計

■詳細→デジタル時計

RPGゲーム

■詳細→ RPGゲーム(マップ作成)

レーダー画面

■詳細→ レーダー

参考文献
Pygame関連 Pygame公式サイトPythonでゲーム作りますが何か?
ゲーム素材(画像) RPGツクール2000無料素材旅のヤドカリシアンのゆりかごRPGツクール素材支部
ゲーム素材(BGM) H/MIX GALLERY煉獄庭園DOVA甘茶の音楽工房無料効果音素材
Python
西住工房

コメント

  1. ss より:

    Pygameの基本操作の各々にサンプルプログラム、の項がありますが
    多くのものが記載されていません。
    サンプルプログラム見たいのですが、記載していただけないでしょうか??

    • 管理人 より:

      ※ss様
      コメントありがとうございます。
      ソースコードを表示するリンクが崩れてておりました。
      申し訳ありません、今から修正します。
      ご連絡ありがとうございました。

  2. papipo より:

    「Pygameの基本操作」のサンプルプログラムに player.png 等の画像が必要ですが、これらはどこで入手できますか。

    • 管理人 より:

      ※papipo様
      コメントありがとうございます。
      「ゲーム素材(画像)」に記載されている外部サイト様より入手可能です。

  3. tk より:

    「Pygameの基本操作<」が「フルスクリーン表示」が「フリスクリーン表示」になってますよ

  4. はやたか より:

    Spriteクラスの__init__の引数はPython3ではエラーになります。注釈をつけるか、今はPython3が主流なのでPython3対応のコードに書き換えていただけると困る人が減ると思います。

    https://teratail.com/questions/195209

タイトルとURLをコピーしました