文字描画
Pygameでは、文字を描画することができます。システム フォントを使うだけでなく、フォントのファイルを指定して使うこともできます。
Pygameには、2系統の文字の描画方法があります。1つ目はpygame.fontの系統です。こちらは古くからある基本の文字描画です。2つ目はpygame.freetypeの系統です。こちらは新しくて高機能です。
今回は、新しいpygame.freetypeの方を使って、文字描画を説明します。
それでは、スクリーンショットとプログラムの例を示します。デフォルトのシステム フォントを使った描画と、読み込んだフォントを使った描画です。
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.freetypeをfreetypeという名前で読み込んでいます。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(
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キーで位置をリセットします。
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座標を表す変数xとyを用意しています。それぞれ初期値は0です。
キーを押した瞬間の検出は、for event in pygame.event.get():のループ内でおこないます。
キーを押した瞬間は、event.typeがpygame.KEYDOWNのときにevent.keyの値を確かめることで検出できます。ここではevent.keyの値がpygame.K_r([R]キー)の値と等しいときに、XY座標を0にリセットしています。
キーが現在押されているかの検出は、pygame.key.get_pressed()関数の戻り値でおこないます。
ここでは戻り値を、変数keysに代入しています。このkeysに対して、keys[pygame.K_LEFT]([←]キー)のように要素位置を指定して、その値がTrueかFalseで判定します。
キーの種類は、公式ドキュメントのkeyのページに表があります。


マウス入力
マウス入力はキー入力と似ています。マウス入力の検知には2つの方法があります。1つ目は「マウスを操作した瞬間」を検出する方法です。2つ目は「マウスのボタンが現在押されているか」を検出する方法です。
それでは、スクリーンショットとプログラムの例を示します。
マウスの位置に勇者を移動させて、左ボタンをクリックすると半透明になります。また、ボタンを上げ下げした瞬間やマウス移動の情報をターミナルに出力します。
条件分岐が多いので、見た目は少し複雑になっています。しかし、処理自体は単純です。
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.typeがpygame.MOUSEBUTTONDOWNのときはボタンが下げられています。pygame.MOUSEBUTTONUPのときはボタンが上げられています。pygame.MOUSEMOTIONのときはマウスが移動しています。
ボタンが上下したときは、event.buttonでボタンの種類が分かります。pygame.BUTTON_LEFTのときは左ボタンが操作されています。イベントが発生した位置はevent.posのタプルで分かります。このタプルは、要素0がX位置、要素1がY位置です。
マウスのボタンが現在押されているかの検出は、pygame.mouse.get_pressed()関数で得たタプルを使っておこないます。ここでは変数pressedにタプルを代入しています。このタプルpressedに対して、pressed[0]のように要素位置を指定して、その値をTrueかFalseで判定します。要素0は左ボタン、要素1は中ボタン、要素2は右ボタンです。

音声
ここではBGMとSEをあつかいます。ゲームでは、背景で流れ続ける音声のBGMと、ユーザーのアクションに合わせて瞬間的に鳴るSE(効果音)を分けています。なぜ分けているかというと、ファイルサイズとタイミングの問題があるからです。
BGMはファイル サイズが大きいので、事前にいくつも読み込んでおくのには向いていません。また、再生タイミングが少しずれても問題がありません。そのため、必要に応じて読み込み、再生します。
対してSEはファイル サイズが小さいので、事前にいくつも読み込んでおけます。そしてSEは瞬間的にタイミングよく鳴ることを求められます。ファイルを読み込むタイムラグも許されません。そのため事前に読み込んでおき、素早く再生します。
Pygameには、こうしたBGMとSEの特性に合わせた音声再生方法が用意されています。
それでは、プログラムの例を示します。音声は「魔王魂」さんの音声ファイルを利用します。
[f]キーを押すとフィールドのBGMが、[b]キーを押すとバトルのBGMが、[d]キーを押すとダメージのSEが鳴ります。
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_battle、p_field、p_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を開発していきましょう。
