SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

レトロ風ゲームを作って学ぶPython入門

【作って学ぶPython】プログラムを書いて、モジュールやパッケージで整理しよう!

レトロ風ゲームを作って学ぶPython入門 第3回


  • X ポスト
  • このエントリーをはてなブックマークに追加

文字描画

 Pygameでは、文字を描画することができます。システム フォントを使うだけでなく、フォントのファイルを指定して使うこともできます。

 Pygameには、2系統の文字の描画方法があります。1つ目はpygame.fontの系統です。こちらは古くからある基本の文字描画です。2つ目はpygame.freetypeの系統です。こちらは新しくて高機能です。

 今回は、新しいpygame.freetypeの方を使って、文字描画を説明します。

 それでは、スクリーンショットとプログラムの例を示します。デフォルトのシステム フォントを使った描画と、読み込んだフォントを使った描画です。

スクリーンショット
スクリーンショット
main.py
import pygame   # Pygameをインポート
import pygame.freetype as freetype  # Pygameのfreetypeをインポート

pygame.init()   # Pygameを初期化
screen = pygame.display.set_mode((800, 600))    # 画面作成
running = True  # 実行継続フラグ

sysfont = freetype.SysFont("", 12)  # システム フォント
p = "font/PixelMplus12-Regular.ttf" # フォントのパス
myfont = freetype.Font(p, 12)   # フォント読み込み

# 文字の描画
def draw():
    bgcol = (0, 255, 0)         # 背景色
    fgcol = (255, 255, 255)     # 文字色
    sysfont.render_to(          # システム フォント描画
        screen, (50, 50), "ABCdefあいう",
        fgcolor=fgcol, bgcolor=bgcol, size=80)
    myfont.render_to(           # 読み込みフォント描画
        screen, (50, 300), "ABCdefあいう",
        fgcolor=fgcol, size=120)

while running:
    for event in pygame.event.get():    # イベント
        if event.type == pygame.QUIT:   # 種類がQUITなら
            running = False   # 終了

    screen.fill((0, 0, 0))  # 画面を塗りつぶす
    draw()  # 図形の描画
    pygame.display.flip()   # 画面フリップ
pygame.quit()   # Pygameを終了

 インポート部分では、pygame.freetypefreetypeという名前で読み込んでいます。import pygameだけでは、pygame.freetypeは使えないので注意が必要です。

 フォントの描画では、まずフォントを読み込みます。システム フォントを利用する場合はfreetype.SysFont()を、ファイルから読み込む場合はfreetype.Font()で読み込みます。

 freetype.SysFont()の第1引数はフォント名、第2引数はデフォルトのサイズです。使用できるシステム フォントの一覧はprint(pygame.font.get_fonts())で得られます。

 freetype.SysFont()の第1引数が空文字("")の場合は、デフォルトのシステム フォントが用いられます。デフォルトのシステム フォントは、日本語が使えないので注意が必要です。

 freetype.Font()の第1引数はフォントのパス、第2引数はデフォルトのサイズです。こちらではデフォルトのサイズは省略できますが、付けておいた方がよいです。

 描画はいくつか方法があるのですが、render_to()関数が最も楽です。描画対象のSurfaceオブジェクト、描画位置のタプル、文字列が必須の引数です。sizeの値は、フォント生成時にデフォルトのサイズを指定していない場合は必須です。生成時も描画時もsizeを指定しなかった場合は、エラーが発生します。

render_to()関数
render_to(
    surface,    # 描画対象のSurfaceオブジェクト
    dest,       # 描画位置のタプル、例: (50, 50)
    text,       # 描画する文字列
    fgcolor=None,   # 文字色
    bgcolor=None,   # 背景色
    style=STYLE_DEFAULT,    # スタイル
    rotation=0, # 回転
    size=0      # 描画サイズ
    )

 スタイルには、次のような値があります。

文字描画のスタイル
freetype.STYLE_NORMAL       # 通常
freetype.STYLE_UNDERLINE    # 下線
freetype.STYLE_OBLIQUE      # 斜体
freetype.STYLE_STRONG       # 強調
freetype.STYLE_WIDE         # ワイド
freetype.STYLE_DEFAULT      # 変更なし

キー入力

 キー入力の検知には2つの方法があります。1つ目は「キーを押した瞬間」を検出する方法です。2つ目は「キーが現在押されているか」を検出する方法です。キーを押した瞬間の検出は、メニューの選択や決定の操作に向いています。キーが現在押されているかの検出は、キャラクターの移動などに向いています。

 それでは、スクリーンショットとプログラムの例を示します。上下左右のキーで勇者を移動させて、Rキーで位置をリセットします。

スクリーンショット
スクリーンショット
main.py
import pygame   # Pygameをインポート

pygame.init()   # Pygameを初期化
screen = pygame.display.set_mode((800, 600))    # 画面作成
image = pygame.image.load("icon.png")     # 画像読み込み
x = 0   # XY座標
y = 0   # XY座標
running = True  # 実行継続フラグ

while running:
    for event in pygame.event.get():    # イベント
        if event.type == pygame.QUIT:   # 種類がQUITなら
            running = False   # 終了
        if event.type == pygame.KEYDOWN:    # キー押下
            if event.key == pygame.K_r:     # Rでリセット
                x = 0   # X座標を0にリセット
                y = 0   # Y座標を0にリセット

    keys = pygame.key.get_pressed() # キー保持
    if keys[pygame.K_LEFT]:  x -= 0.5   # 左
    if keys[pygame.K_RIGHT]: x += 0.5   # 左
    if keys[pygame.K_UP]:    y -= 0.5   # 上
    if keys[pygame.K_DOWN]:  y += 0.5   # 下

    screen.fill((0, 0, 0))  # 画面を塗りつぶす
    screen.blit(image, (x, y))  # 描画
    pygame.display.flip()   # 画面フリップ
pygame.quit()   # Pygameを終了

 キャラクターを移動させるために、位置のXY座標を表す変数xyを用意しています。それぞれ初期値は0です。

 キーを押した瞬間の検出は、for event in pygame.event.get():のループ内でおこないます。

 キーを押した瞬間は、event.typepygame.KEYDOWNのときにevent.keyの値を確かめることで検出できます。ここではevent.keyの値がpygame.K_r([R]キー)の値と等しいときに、XY座標を0にリセットしています。

 キーが現在押されているかの検出は、pygame.key.get_pressed()関数の戻り値でおこないます。

 ここでは戻り値を、変数keysに代入しています。このkeysに対して、keys[pygame.K_LEFT]([←]キー)のように要素位置を指定して、その値がTrueFalseで判定します。

 キーの種類は、公式ドキュメントのkeyのページに表があります。

マウス入力

 マウス入力はキー入力と似ています。マウス入力の検知には2つの方法があります。1つ目は「マウスを操作した瞬間」を検出する方法です。2つ目は「マウスのボタンが現在押されているか」を検出する方法です。

 それでは、スクリーンショットとプログラムの例を示します。

 マウスの位置に勇者を移動させて、左ボタンをクリックすると半透明になります。また、ボタンを上げ下げした瞬間やマウス移動の情報をターミナルに出力します。

 条件分岐が多いので、見た目は少し複雑になっています。しかし、処理自体は単純です。

スクリーンショット
スクリーンショット
main.py
import pygame   # Pygameをインポート

pygame.init()   # Pygameを初期化
screen = pygame.display.set_mode((800, 600))    # 画面作成
image = pygame.image.load("icon.png")     # 画像読み込み
running = True  # 実行継続フラグ

while running:
    for event in pygame.event.get():    # イベント
        if event.type == pygame.QUIT:   # 種類がQUITなら
            running = False   # 終了
        if event.type == pygame.MOUSEBUTTONDOWN:    # ボタン下げ
            if event.button == pygame.BUTTON_LEFT:  # 左ボタン
                print(event.pos, "DOWN")
        if event.type == pygame.MOUSEBUTTONUP:      # ボタン上げ
            if event.button == pygame.BUTTON_LEFT:  # 左ボタン
                print(event.pos, "UP")
        if event.type == pygame.MOUSEMOTION:    # マウス移動
            print(event.pos)

    screen.fill((0, 0, 0))  # 画面を塗りつぶす
    pressed = pygame.mouse.get_pressed()
    if pressed[0]:  # 左クリックあり
        image.set_alpha(128)   # 半透明
    else:           # 左クリックなし
        image.set_alpha(255)   # 不透明
    screen.blit(image, pygame.mouse.get_pos())  # 描画
    pygame.display.flip()   # 画面フリップ
pygame.quit()   # Pygameを終了

 マウスを操作した瞬間の検出は、for event in pygame.event.get():のループ内でおこないます。

 event.typepygame.MOUSEBUTTONDOWNのときはボタンが下げられています。pygame.MOUSEBUTTONUPのときはボタンが上げられています。pygame.MOUSEMOTIONのときはマウスが移動しています。

 ボタンが上下したときは、event.buttonでボタンの種類が分かります。pygame.BUTTON_LEFTのときは左ボタンが操作されています。イベントが発生した位置はevent.posのタプルで分かります。このタプルは、要素0がX位置、要素1がY位置です。

 マウスのボタンが現在押されているかの検出は、pygame.mouse.get_pressed()関数で得たタプルを使っておこないます。ここでは変数pressedにタプルを代入しています。このタプルpressedに対して、pressed[0]のように要素位置を指定して、その値をTrueFalseで判定します。要素0は左ボタン、要素1は中ボタン、要素2は右ボタンです。

音声

 ここではBGMとSEをあつかいます。ゲームでは、背景で流れ続ける音声のBGMと、ユーザーのアクションに合わせて瞬間的に鳴るSE(効果音)を分けています。なぜ分けているかというと、ファイルサイズとタイミングの問題があるからです。

 BGMはファイル サイズが大きいので、事前にいくつも読み込んでおくのには向いていません。また、再生タイミングが少しずれても問題がありません。そのため、必要に応じて読み込み、再生します。

 対してSEはファイル サイズが小さいので、事前にいくつも読み込んでおけます。そしてSEは瞬間的にタイミングよく鳴ることを求められます。ファイルを読み込むタイムラグも許されません。そのため事前に読み込んでおき、素早く再生します。

 Pygameには、こうしたBGMとSEの特性に合わせた音声再生方法が用意されています。

 それでは、プログラムの例を示します。音声は「魔王魂」さんの音声ファイルを利用します。

 [f]キーを押すとフィールドのBGMが、[b]キーを押すとバトルのBGMが、[d]キーを押すとダメージのSEが鳴ります。

main.py
import pygame   # Pygameをインポート

pygame.init()   # Pygameを初期化
screen = pygame.display.set_mode((800, 600))    # 画面作成
running = True  # 実行継続フラグ

p_battle = "maou_bgm_8bit18.mp3"    # バトルBGM
p_field  = "maou_bgm_8bit01.mp3"    # フィールドBGM
p_damage = "maou_se_8bit22.wav"     # ダメージSE
se = pygame.mixer.Sound(p_damage)   # SE読み込み

while running:
    for event in pygame.event.get():    # イベント
        if event.type == pygame.QUIT:   # 種類がQUITなら
            running = False   # 終了
        if event.type == pygame.KEYDOWN:    # キー押下
            if event.key == pygame.K_b:     # b = バトル
                pygame.mixer.music.load(p_battle)   # BGM読み込み
                pygame.mixer.music.play(loops = -1) # BGM再生
            if event.key == pygame.K_f:     # f = フィールド
                pygame.mixer.music.load(p_field)    # BGM読み込み
                pygame.mixer.music.play(loops = -1) # BGM再生
            if event.key == pygame.K_d:     # d = ダメージ
                se.play()       # SE再生
pygame.quit()   # Pygameを終了

 まず、変数p_battlep_fieldp_damageに、各音声ファイルのパスを用意しておきます。

 SEはpygame.mixer.Sound()で事前に読み込み、Soundオブジェクトを生成します。ここでは変数seに代入しておき、[d]キー操作のタイミングでse.play()関数で再生します。

 BGMは、[b]キー、[f]キーを押したタイミングで、pygame.mixer.music.load()関数で読み込み、pygame.mixer.music.play()関数で再生します。

 新しいファイルを読み込んで再生すると、以前のファイルの再生は自動で止まります。play()関数の引数にloops = -1を指定すると、繰り返し再生になります。

まとめと次回

 関数、モジュールとパッケージ、そしてPygameのさまざまな機能を見てきました。ここまでの知識を利用すれば、もうゲームを作ることができます。

 それでは次回から、非常に小さなレトロ風RPGを開発していきましょう。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
レトロ風ゲームを作って学ぶPython入門連載記事一覧

もっと読む

この記事の著者

柳井 政和(ヤナイ マサカズ)

クロノス・クラウン合同会社 代表社員http://crocro.com/オンラインソフトを多数公開。プログラムを書いたり、ゲームを作ったり、記事を執筆したり、マンガを描いたり、小説を書いたりしています。「めもりーくりーなー」でオンラインソフト大賞に入賞。最近は、小説家デビューして小説も書いています(『裏切りのプログラム』他)。面白いことなら何でもOKのさすらいの企画屋です。 

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/19457 2024/06/10 12:26

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング