【検証】Pythonでゲームが作れるライブラリ「Pygame」を使ってみた

Python

皆さん、こんにちは!
上越市を拠点にし、「FA設備・装置開発」と「画像処理」に強い会社、NSIです!
私達は豊富な経験と専門知識で、各種業界の自動化・システム化のお手伝いをしています。

先日、ブログに掲載できそうなネタを探していたところ…
ちょっと面白そうなPythonライブラリを発見したので、調査してみました。

というわけで、今回は Pythonでゲームが作れるライブラリ についてご紹介。
興味がある方、今後使ってみたい方の参考になれば嬉しいです。

Pygameとは?

Pygameは、Pythonでゲーム開発を行うためのライブラリです。
主な機能として、GUIの作成, 音声の利用, キーボードやマウスイベントの取得といった機能があります。
これらの機能を利用して、ゲーム開発はもちろん、GUIアプリケーションやデジタルアート作品の制作などにも活用されています。

Pygame
https://www.pygame.org/news

べんぞうくん
べんぞうくん

オープンソースなので情報が多く、初心者でも学びやすいのが特徴だよ。

Pygameを使ってみる

それでは、Pygameを使用してアプリを作成してみましょう!
今回は実装が比較的簡単なピンポンゲームを作成します。
なお、ここからはPythonがインストールされていることを前提に説明していきます。

1. Pygameのインストール

コマンドプロンプトを起動し、以下のコマンドを実行します。
※ 以下はPythonがインストールされている場合のコマンドです。別の開発環境(Anaconda)等を使っている場合は、それに合わせて変更してください。

pip install pygame

補足:Anacondaとは?
簡単に言うと、Pythonでの開発に必要な「ライブラリ」や「ツール」などをセットにした実行環境。
Pythonでは、必要なライブラリを「pip install ~」で1つずつインストールする必要があるが、Anacondaはインストール時にある程度のライブラリも含まれているため、わざわざインストールする手間がないのがメリット。
多様な機能が含まれている分、Pythonよりも容量を必要とするのがデメリット。

2. 処理を作成する

処理がやや長いため、何段階かに分けて説明していきます。
ソースコートの全文は最後に記載してあるため、まずコピペして実行したい!という方はそちらをご参照ください。

まず、画面やパドル, ボールのサイズなどを定義します。
事前にまとめて定義しておくことで、後々調整が楽に行えるようになります。

import pygame
import sys

# ゲーム画面のサイズ
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

# パドルのサイズ,速度
PADDLE_WIDTH = 10
PADDLE_HEIGHT = 100
PADDLE_SPEED = 10

# ボールのサイズ,速度
BALL_SIZE = 15
BALL_SPEED_X = 3
BALL_SPEED_Y = 3

# 色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
CYAN = (255, 0, 255)
MAGENTA = (0, 255, 255)

Mainとなる関数を作成し、その中に画面に関する設定を定義します。

# Main関数
def main():
    # Pygame関連
    pygame.init()                                                   # 初期化
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) # 画面作成
    pygame.display.set_caption("PygameSample")                      # 画面タイトル 
    clock = pygame.time.Clock()                                     # 時計作成(FPS制御用)
    font = pygame.font.Font(None, 36)                               # 文字定義作成

# Main関数呼び出し
if __name__ == "__main__":
    main()

Main関数の上に、パドル(ボールを打ち返す棒)に関する処理を行うクラスを作成します。
ここではパドルの初期化(生成)、上移動、下移動時の処理を定義します。

# パドルクラス
class Paddle(pygame.sprite.Sprite):   
    # 初期化
    def __init__(self, x, y, color):
        super().__init__()
        self.image = pygame.Surface((PADDLE_WIDTH, PADDLE_HEIGHT))
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.speed = PADDLE_SPEED

    # 上移動したとき
    def move_up(self):
        self.rect.y -= self.speed
        if self.rect.top < 0:
            self.rect.top = 0

    # 下移動したとき
    def move_down(self):
        self.rect.y += self.speed
        if self.rect.bottom > SCREEN_HEIGHT:
            self.rect.bottom = SCREEN_HEIGHT

続けて、ボールに関する処理を行うクラスを作成します。
ここでは、ボールの初期化(生成)、表示更新時の処理を定義します。

# ボールクラス
class Ball(pygame.sprite.Sprite):
    # 初期化
    def __init__(self, color):
        super().__init__()
        self.image = pygame.Surface((BALL_SIZE, BALL_SIZE), pygame.SRCALPHA)
        pygame.draw.circle(self.image, color, (BALL_SIZE // 2, BALL_SIZE // 2), BALL_SIZE // 2)
        self.rect = self.image.get_rect()
        self.rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
        self.speed_x = BALL_SPEED_X
        self.speed_y = BALL_SPEED_Y

    # 表示更新
    def update(self):
        self.rect.x += self.speed_x
        self.rect.y += self.speed_y

        if self.rect.top <= 0 or self.rect.bottom >= SCREEN_HEIGHT:
            self.speed_y = -self.speed_y

今回使用するクラスは上記2つとなります。
Main関数に戻り、「#Pygame関連」に続けて、先ほど作成したクラスを利用し、画面に表示するアイテムを定義します。

    # 画面アイテム関連
    paddle1 = Paddle(30, SCREEN_HEIGHT // 2, CYAN)
    paddle2 = Paddle(SCREEN_WIDTH - 30, SCREEN_HEIGHT // 2, MAGENTA)
    ball = Ball(WHITE)
    all_sprites = pygame.sprite.Group()
    all_sprites.add(paddle1, paddle2, ball)
    score1 = 0
    score2 = 0

ここまで作成したら、一回実行してみましょう。
黒いウィンドウが一瞬表示されたのち、閉じると思います。これはまだ中身の処理が記載されていないためです。
ここから、ゲームの中身に関する処理を作成していきます。

【現在の画面イメージ】

先ほどの処理に続けて、準備画面を作成します。
画面上に「Press SPACE to start(スペースキーを押すと開始します)」というメッセージを表示する画面を作成します。

    running = False
    # runningがFalseのとき:準備画面
    while not running:
        screen.fill(BLACK)                                                          # 画面初期化
        text = font.render("Press SPACE to start", True, WHITE)                     # テキスト定義
        text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))   # 描画位置定義
        screen.blit(text, text_rect)                                                # 描画
        pygame.display.flip()                                                       # 画面更新

        # イベント取得
        for event in pygame.event.get():
            # 閉じるボタン:終了
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

            # スペースキーを押した:開始 
            if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                running = True

再度実行すると「Press SPACE to start」と書かれた画面が表示され、スペースキーを押すと閉じます。
スペースキーを押した後、プレイ画面に切り替わるよう処理を追加していきましょう。

【現在の画面イメージ】

先ほどの処理に続けて、以下の処理を記述します。
スペースキーが押された際に表示する画面となります。

    # runningがTrueのとき:プレイ画面
    while running:
        # イベント取得 
        for event in pygame.event.get():
            # 閉じるボタン:終了
            if event.type == pygame.QUIT:
                running = False

        # 画面初期化
        screen.fill(BLACK)

        # 画面更新
        pygame.display.flip()
        clock.tick(60)

実行すると、スペースキーを押した後、真っ黒な画面が表示されます。
画面の切替は問題なさそうですね。

描画処理を追加します。「#画面初期化」と「#画面更新」の間に処理を追加します。(以下参照)

        # 画面初期化
        screen.fill(BLACK)

        # 中心線を描画
        pygame.draw.line(screen, WHITE, (SCREEN_WIDTH // 2, 0), (SCREEN_WIDTH // 2, SCREEN_HEIGHT), 2)
        
        # アイテム描画
        all_sprites.draw(screen)

        # 得点描画
        score1_text = font.render("Player 1: {}".format(score1), True, CYAN)    # テキスト定義
        score1_rect = score1_text.get_rect(center=(SCREEN_WIDTH // 4, 20))      # 描画位置定義
        screen.blit(score1_text, score1_rect)                                   # 描画

        score2_text = font.render("Player 2: {}".format(score2), True, MAGENTA) # テキスト定義
        score2_rect = score2_text.get_rect(center=(SCREEN_WIDTH * 3 // 4, 20))  # 描画位置定義
        screen.blit(score2_text, score2_rect) 

        # 画面更新
        pygame.display.flip()
        clock.tick(60)

実行すると、スペースキーを押した後、各アイテムが描画された画面が表示されます。
これだけだとただ表示されているだけとなるため、パドルやボールを動かす処理を追加していきます。

【現在の画面イメージ】

「#画面初期化」の上に以下の処理を追加します。

        # キー判定
        keys = pygame.key.get_pressed()
        if keys[pygame.K_w]:    # Wキー:パドル1を上移動
            paddle1.move_up()
        if keys[pygame.K_s]:    # Sキー:パドル1を下移動
            paddle1.move_down()
        if keys[pygame.K_UP]:
            paddle2.move_up()   # ↑キー:パドル2を上移動
        if keys[pygame.K_DOWN]:
            paddle2.move_down() # ↓キー:パドル2を下移動
        all_sprites.update()    # アイテム描画

        # ボールとパドルの衝突判定
        if pygame.sprite.spritecollide(ball, [paddle1], False) or pygame.sprite.spritecollide(ball, [paddle2], False):
            ball.speed_x = -ball.speed_x

実行すると、パドルにボールが衝突した際に、跳ね返るようになりました!
ただし、このままだとボールが画面外に行ってもリセットされません。
そこで、得点が入ったらボール位置をリセットする処理を追加します。

【現在の画面イメージ】

「#ボールとパドルの衝突判定」の下に以下の処理を追加します。

        # ボールがゴールに入ったら得点を加算
        if ball.rect.left <= 0:
            score2 += 1                                                 # 得点加算
            ball.rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)  # ボール位置リセット
        elif ball.rect.right >= SCREEN_WIDTH:
            score1 += 1                                                 # 得点加算
            ball.rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)  # ボール位置リセット

これで処理は完成です!早速実行してみましょう。

3. 実行してみる

操作方法
Player1は「W」で上方向、「S」で下方向へパドルが移動します。
Player2は「↑」で上方向、「↓」で下方向へパドルが移動します。

バッチリ動作していますね。
効果音を入れたり、一時停止機能を追加したりするとより良くなりそうです。

おまけ. ソースコード全文

最後に、今回作成したソースコード全文を掲載します。
Pygameをインストールしたのち、以下のソースコードをコピペすれば実行できます。

import pygame
import sys

# ゲーム画面のサイズ
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

# パドルのサイズ,速度
PADDLE_WIDTH = 10
PADDLE_HEIGHT = 100
PADDLE_SPEED = 10

# ボールのサイズ,速度
BALL_SIZE = 15
BALL_SPEED_X = 3
BALL_SPEED_Y = 3

# 色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
CYAN = (255, 0, 255)
MAGENTA = (0, 255, 255)

# パドルクラス
class Paddle(pygame.sprite.Sprite):   
    # 初期化
    def __init__(self, x, y, color):
        super().__init__()
        self.image = pygame.Surface((PADDLE_WIDTH, PADDLE_HEIGHT))
        self.image.fill(color)
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.speed = PADDLE_SPEED

    # 上移動したとき
    def move_up(self):
        self.rect.y -= self.speed
        if self.rect.top < 0:
            self.rect.top = 0

    # 下移動したとき
    def move_down(self):
        self.rect.y += self.speed
        if self.rect.bottom > SCREEN_HEIGHT:
            self.rect.bottom = SCREEN_HEIGHT

# ボールクラス
class Ball(pygame.sprite.Sprite):
    # 初期化
    def __init__(self, color):
        super().__init__()
        self.image = pygame.Surface((BALL_SIZE, BALL_SIZE), pygame.SRCALPHA)
        pygame.draw.circle(self.image, color, (BALL_SIZE // 2, BALL_SIZE // 2), BALL_SIZE // 2)
        self.rect = self.image.get_rect()
        self.rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
        self.speed_x = BALL_SPEED_X
        self.speed_y = BALL_SPEED_Y

    # 表示更新
    def update(self):
        self.rect.x += self.speed_x
        self.rect.y += self.speed_y

        if self.rect.top <= 0 or self.rect.bottom >= SCREEN_HEIGHT:
            self.speed_y = -self.speed_y

# Main関数
def main():
    # Pygame関連
    pygame.init()                                                   # 初期化
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) # 画面作成
    pygame.display.set_caption("PygameSample")                      # 画面タイトル 
    clock = pygame.time.Clock()                                     # 時計作成(FPS制御用)
    font = pygame.font.Font(None, 36)                               # 文字定義作成

    # 画面アイテム関連
    paddle1 = Paddle(30, SCREEN_HEIGHT // 2, CYAN)
    paddle2 = Paddle(SCREEN_WIDTH - 30, SCREEN_HEIGHT // 2, MAGENTA)
    ball = Ball(WHITE)
    all_sprites = pygame.sprite.Group()
    all_sprites.add(paddle1, paddle2, ball)
    score1 = 0
    score2 = 0
    
    running = False
    # runningがFalseのとき:準備画面
    while not running:
        screen.fill(BLACK)                                                          # 画面初期化
        text = font.render("Press SPACE to start", True, WHITE)                     # テキスト定義
        text_rect = text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))   # 描画位置定義
        screen.blit(text, text_rect)                                                # 描画
        pygame.display.flip()                                                       # 画面更新

        # イベント取得
        for event in pygame.event.get():
            # 閉じるボタン:終了
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

            # スペースキーを押した:開始 
            if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                running = True

    # runningがTrueのとき:プレイ画面
    while running:
        # イベント取得 
        for event in pygame.event.get():
            # 閉じるボタン:終了
            if event.type == pygame.QUIT:
                running = False

        # キー判定
        keys = pygame.key.get_pressed()
        if keys[pygame.K_w]:    # Wキー:パドル1を上移動
            paddle1.move_up()
        if keys[pygame.K_s]:    # Sキー:パドル1を下移動
            paddle1.move_down()
        if keys[pygame.K_UP]:
            paddle2.move_up()   # ↑キー:パドル2を上移動
        if keys[pygame.K_DOWN]:
            paddle2.move_down() # ↓キー:パドル2を下移動
        all_sprites.update()    # アイテム描画

        # ボールとパドルの衝突判定
        if pygame.sprite.spritecollide(ball, [paddle1], False) or pygame.sprite.spritecollide(ball, [paddle2], False):
            ball.speed_x = -ball.speed_x

        # ボールがゴールに入ったら得点を加算
        if ball.rect.left <= 0:
            score2 += 1                                                 # 得点加算
            ball.rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)  # ボール位置リセット
        elif ball.rect.right >= SCREEN_WIDTH:
            score1 += 1                                                 # 得点加算
            ball.rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)  # ボール位置リセット

        # 画面初期化
        screen.fill(BLACK)

        # 中心線を描画
        pygame.draw.line(screen, WHITE, (SCREEN_WIDTH // 2, 0), (SCREEN_WIDTH // 2, SCREEN_HEIGHT), 2)
        
        # アイテム描画
        all_sprites.draw(screen)

        # 得点表示
        score1_text = font.render("Player 1: {}".format(score1), True, CYAN)    # テキスト定義
        score1_rect = score1_text.get_rect(center=(SCREEN_WIDTH // 4, 20))      # 描画位置定義
        screen.blit(score1_text, score1_rect)                                   # 描画

        score2_text = font.render("Player 2: {}".format(score2), True, MAGENTA) # テキスト定義
        score2_rect = score2_text.get_rect(center=(SCREEN_WIDTH * 3 // 4, 20))  # 描画位置定義
        screen.blit(score2_text, score2_rect)                                   # 描画

        # 画面更新
        pygame.display.flip()
        clock.tick(60)

    # 終了
    pygame.quit()
    sys.exit()

# Main関数呼び出し
if __name__ == "__main__":
    main()

最後に

今回は Pythonでゲームが作れるライブラリ について調査してみました。
GUIが作成できるため、ゲームを作成する用途以外にも活用できそうです。
皆さんも息抜きに作成してみてはいかがでしょうか?

ここまで読んでいただき、ありがとうございました。
ご質問・ご要望・ご相談などは、下記お問い合わせフォームからお気軽にご連絡ください。
http://www.net-nsi.co.jp/toiawase.html

べんぞうくん
べんぞうくん

この記事が面白いと思ったらGoodボタンを押してね~

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