SHOEISHA iD

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

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

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

【作って学ぶPython】ゲームを開発してみよう!タイトル、マップ画面の実装編

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


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

4つの補助的モジュール

 次のマップ画面に進む前に、タイトル画面では用意しなかった補助的なモジュールを4つ作ります。

作成するモジュール
モジュール 内容
hero.py 主人公のデータ
map.py マップのデータ
enemy.py 敵のデータ
dialog.py ダイアログ表示の処理

 ファイルは全て、main.pyと同じ階層に作ります。

ファイル構成
hero.py
map.py
enemy.py
dialog.py

hero.py

 主人公のデータと、その処理をまとめたhero.pyモジュールです。プログラムを示します。

hero.py
# 主人公
start_x = 4     # 開始X位置
start_y = 3     # 開始Y位置
x = start_x     # X位置
y = start_y     # Y位置
next_x = x      # 次回X位置
next_y = y      # 次回Y位置
move_rate = 0.0 # 移動到達比率
hp = 100        # HP
hp_max = 100    # HP最大値
mp = 4          # MP
mp_max = 4      # MP最大値
at = 10         # 攻撃力
df = 10         # 防御力
exp = 0         # 経験値
level = 1       # レベル
iref = 0        # 画像参照

# 経験値獲得
def add_exp(n):
    global exp, level, hp   # 代入可能に
    exp = min(exp + n, 999)     # 経験値獲得
    level_tmp = 1 + exp // 50   # レベル計算
    if level != level_tmp:  # レベルが更新されているか確認
        # レベルアップ
        level = level_tmp   # レベル反映
        calc()          # 能力値の計算
        hp = hp_max     # HPのみ回復
        return True     # レベルアップあり
    return False    # レベルアップなし

# 能力値の計算
def calc():
    global hp_max, mp_max, at, df   # 代入可能に
    hp_max = 100 + (level - 1) * 50 # HP最大値
    mp_max = 4 + (level - 1) * 2    # MP最大値
    at = 10 + (level - 1) * 5       # 攻撃力
    df = 10 + (level - 1) * 5       # 防御力

 前半は主人公のデータで、後半は成長のための処理です。

 start_xstart_yxynext_xnext_ymove_rateは、移動のための情報です。それぞれ、開始XY位置、現在XY位置、次回XY位置、移動到達比率になります。

 hphp_maxmpmp_maxatdfexplevelは、主人公の能力値です。HP/最大、MP/最大、攻撃力、防御力、経験値、レベルになります。

 irefは、chara.pngを分割したリストの参照位置です。iref0なので、分割したリストの要素0を参照します。

iref = 0
iref = 0

 irefの値に、0、1、0、1、……と交互に足していくことで、画像2枚によるアニメーションをおこなえます。

 関数の説明に入る前に、global文の解説をします。

 Pythonの関数外の変数は、値を利用することは可能ですが、値を代入することはできません。関数内でglobalと書き、,区切りで変数名を列挙すると、その名前の変数をグローバル変数としてあつかい、代入可能にします。

 add_exp()関数は、経験値の獲得です。現在の経験値から仮のレベルを計算して、現在のレベルと異なっていればレベルアップ処理をおこないます。

 レベルアップした場合は、calc()関数で能力値の計算をおこない、HPを最大値に回復させます。MPは回復しません。この関数では、最後にレベルアップしたかを真偽値で返します。レベルアップしたときはTrueを返し、そうでないときはFalseを返します。

 calc()関数は、能力値の計算です。レベルをもとに、HP最大値、MP最大値、攻撃力、防御力を決定します。

map.py

 マップのデータをまとめたmap.pyモジュールです。

map.py
# マップ
w = 30  # 横幅
h = 30  # 高さ
data = [    # データ
2,2,2,0,2,0,2,0,2,0,3,0,1,0,0,3,3,3,0,0,0,1,1,0,0,1,0,1,2,2,
2,1,0,2,0,0,0,0,0,0,0,1,0,3,1,0,3,0,1,0,0,0,0,1,1,0,1,2,1,2,
2,1,2,2,2,0,0,0,0,0,0,1,0,3,1,0,3,0,1,0,0,0,0,1,1,0,1,2,1,2,
2,1,0,1,0,0,0,0,0,0,0,1,0,3,1,0,3,0,1,0,0,0,0,1,1,0,1,2,1,2,
0,0,2,0,1,4,0,2,0,3,0,3,0,0,3,1,0,0,0,1,2,0,0,0,1,2,0,2,1,2,
0,0,0,1,3,1,0,0,3,0,0,0,3,0,3,0,1,3,3,2,0,2,0,0,0,0,0,1,0,1,
0,0,0,0,1,0,0,0,0,0,3,0,1,0,0,1,0,0,2,0,4,0,2,0,0,0,1,1,0,0,
0,0,0,0,1,0,0,0,0,0,3,0,1,0,0,1,0,0,2,0,0,0,2,0,0,0,1,1,0,0,
0,0,0,0,1,0,0,0,0,0,3,0,1,1,1,1,0,0,2,0,0,0,2,0,0,0,1,1,0,0,
0,0,0,0,0,0,0,0,0,3,1,1,3,3,1,3,3,0,0,2,0,2,0,1,0,0,0,0,1,0,
0,0,2,0,1,0,0,2,0,0,1,3,3,1,1,3,0,0,0,0,2,0,0,0,0,1,0,0,1,0,
0,0,2,0,2,2,0,0,0,3,0,1,3,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,2,0,2,2,0,0,0,3,0,0,1,0,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,0,2,0,2,2,0,0,0,3,0,0,0,0,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,
0,2,0,2,2,2,2,0,0,0,0,3,3,3,1,4,1,3,3,0,0,1,1,1,0,0,4,0,0,1,
2,0,0,2,4,2,2,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,2,1,0,0,0,0,1,1,
0,0,2,1,0,0,2,0,0,0,0,0,0,3,0,3,0,0,0,1,1,2,1,0,0,0,0,1,1,1,
2,0,0,2,0,1,0,1,0,0,1,0,0,0,0,3,3,0,1,1,2,1,0,0,0,0,1,1,1,2,
0,1,0,0,0,0,0,0,0,1,2,1,0,3,0,3,0,0,1,2,1,0,0,0,0,1,1,1,1,2,
0,1,1,0,0,0,0,0,1,0,1,0,3,0,1,1,1,1,2,2,1,0,0,0,1,1,1,2,2,2,
0,4,0,0,1,0,2,0,3,3,3,0,0,0,1,2,1,2,1,1,1,1,0,1,1,1,1,2,2,2,
0,3,0,0,1,0,2,0,3,3,3,0,0,0,1,2,1,2,1,1,1,1,0,1,1,1,2,2,2,2,
0,2,0,0,1,0,2,3,3,3,3,0,0,0,1,2,2,2,1,1,1,1,0,1,1,2,2,2,2,2,
0,0,0,2,0,1,0,0,3,3,3,0,0,0,1,2,1,0,0,0,1,1,1,1,2,2,2,2,2,2,
0,0,0,0,0,1,0,0,1,3,0,0,1,1,2,1,1,0,4,0,1,2,1,2,2,2,2,2,2,2,
1,0,0,0,0,0,0,3,0,1,0,0,0,1,1,2,1,0,0,0,1,1,2,2,2,2,2,2,2,2,
2,1,0,0,1,0,1,0,1,0,0,1,0,1,0,1,2,1,1,1,2,2,2,2,2,2,2,2,2,2,
2,1,0,1,1,1,1,0,1,0,0,1,1,0,0,1,2,1,1,1,2,2,2,2,2,2,2,2,2,2,
2,1,1,1,4,1,1,0,1,0,0,1,0,1,0,1,2,1,1,2,2,2,2,2,2,2,2,2,2,2,
2,2,1,1,1,1,0,1,0,1,0,1,0,0,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2,5
]
PLAIN    = 0    # 平地
FOLEST   = 1    # 森
MOUNTAIN = 2    # 山
WATER    = 3    # 水
TOWN     = 4    # 街
CASTLE   = 5    # 魔王城

 行数は多いですが、内容は少ないです。マップの横幅w、高さh、マスのデータdata、そして各土地を表す定数です。この定数は、land.pngの画像の位置に対応しています。

land.pngの位置
land.pngの位置

 dataを画像で表示すると、次のようになります。

dataの画像化
dataの画像化

enemy.py

 敵のデータをまとめたenemy.pyモジュールです。

enemy.py
import map

# 敵
name = ""   # 名前
rate = 0    # 出現頻度
land = 0    # 土地
iref = 0    # 画像参照
hp = 0      # HP
hp_max = 0  # HP最大値
at = 0      # 攻撃力
df = 0      # 防御力
exp = 0     # 経験値
boss = False    # ボス

ENEMIES = (
    # 名前   出現頻度   土地   画像参照 HP 最大 攻撃 防御 EXP ボス
    ("ゴブリン", 15, map.PLAIN,    2,   40,  40, 20,  5, 30, False),
    ("エルフ",   10, map.FOLEST,   4,   60,  60, 30, 10, 50, False),
    ("ロック",   5,  map.MOUNTAIN, 6,  200, 200, 40, 30, 80, False),
    ("魔王",     1,  map.CASTLE,   8,  999, 999, 99, 99, 99, True)
)

# 敵の設定
def set(i):
    global name, rate, land, iref, hp, hp_max, at, df, exp, boss    # 代入可能に
    name, rate, land, iref, hp, hp_max, at, df, exp, boss = ENEMIES[i]

set(0)  # 仮設定

 序盤は敵の能力値、中盤は各土地に登場する敵のデータ、終盤は戦闘相手を設定する処理です。土地の値を利用するためにmapをインポートしています。

 敵の能力値は主人公よりも少ないです。irefの画像参照位置は、次のようになっています。

各敵のirefの位置
各敵のirefの位置

 出現頻度rateと土地landは特殊な値なので解説します。敵は、土地landの場所に生息しており、1/rateの確率で出現します。また、ボスbossTrueの場合はラスボスです。この敵を倒すとエンディングが始まります。

生息と出現頻度
生息地 rateの値 計算式 出現率
ゴブリン 平地 15 1/15 6.6%
エルフ 10 1/10 10%
ロック 5 1/5 20%
魔王 魔王城 1 1/1 100%

 敵のデータはENEMIESというタプルでまとめています。set()関数を使い、set(0)のように指定すると、ENEMIESの要素0の値が、敵のデータとして設定されます。

 ここでは、関数外の変数に値を代入するためにglobal文を用いています。また、ENEMIES[i]で得たタプルを分割することで、各変数に値を代入しています。

diaglog.py

 マップ画面では、敵と遭遇したときや街に着いたときにダイアログを出して知らせます。そのためのダイアログ表示処理をdiaglog.pyモジュールでおこないます。

 スクリーンショットとプログラムを示します。

スクリーンショット
スクリーンショット
dialog.py
import pygame, data, img
from data import U, W, H, COL_W, COL_G

def show(text):
    # 土台描画
    rect = (U, U, W - U * 2, H - U * 2)
    pygame.draw.rect(data.screen, COL_G, rect)  # 四角形塗りつぶし

    # 文字描画
    fsz = img.fsz * 2    # フォント サイズ
    texts = text.splitlines()   # 改行でリストに分割
    y = (H - fsz * len(texts)) // 2 # 行開始位置
    for line in texts:
        rect = img.font.get_rect(line, size=fsz)    # 描画四角形取得
        x = (W - rect.w) // 2       # 中央揃え
        img.font.render_to(data.screen, (x, y), line, COL_W, size=fsz)  # 文字描画
        y += fsz    # 行位置更新

    # 画面の反映と待機
    pygame.display.flip()   # 画面フリップ
    pygame.time.wait(1200)  # 待機
    pygame.event.get()      # イベントの消費

 まず、インポート部分では、pygame, data, imgを読み込みます。また、dataモジュールから、描画単位U、横幅W、高さH、白色COL_W、灰色COL_Gを読み込みます。

 描画をおこなうshow()関数はテキストを引数に取ります。この関数は、土台の描画、文字の描画、画面の反映と待機の3つの部分に分かれます。

 土台の描画では、画面より描画単位Uだけ内側を灰色で塗りつぶします。

 文字の描画では、フォント サイズの2倍を変数fszに代入して、文字の描画サイズにします。そして、テキストをtext.splitlines()で改行単位で分割してリストtextsにします。

 そのあとはfor文で、textsから1行分の文字列lineを得て、1行ずつY位置をずらして描画していきます。

 文字の描画では、中央位置に描画するために描画サイズを事前に得ます。img.font.get_rect(line, size=fsz)の部分です。get_rect()関数は、第1引数のテキストを、引数sizeの文字サイズで描画した場合のサイズをpygame.Rectオブジェクトで返します。戻り値を代入したrectの、rect.wは横幅、rect.hは高さになります。

 ここでは(W - rect.w) // 2を計算して、ウィンドウの横幅の中央に、文字が描画されるようにします。

中央描画の計算
中央描画の計算

 最後の画面の反映と待機の部分は、Pygameの挙動の理解が必要です。

 まずpygame.display.flip()で描画したダイアログを表示します。次にpygame.time.wait(1200)で1200ミリ秒(1.2秒)待機します。最後のpygame.event.get()で、待機していたあいだの押しっぱなしのキーの記録を空費します。

 Pygameでは、pygame.event.get()pygame.event.get()のあいだで押されたキーは、現在押されているキーとして記録されます。ここで1回pygame.event.get()を実行することで、待機中の操作が、元のループのキーに影響しないようにします。

次のページ
マップ画面

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

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

もっと読む

この記事の著者

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

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

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

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

この記事をシェア

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

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング