SHOEISHA iD

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

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

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

【作って学ぶPython】バトル画面を実装させ、レトロ風RPGを完成させよう!

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

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

s_battle/draw.py

 描画をおこなうs_battle/draw.pyモジュールです。主人公と敵のステータス、主人公と敵のキャラクター画像、メニュー、枠の描画をおこないます。プログラム自体は長いですが、愚直に描画をおこなうだけです。かなりボリュームがあるので、全体を掲載したあと、部分を掲載しながら説明します。

s_battle/draw.py
import pygame, data, hero, enemy, img
from data import U, W, H, COL_W, COL_G

RECT_HERO  = pygame.Rect(U,      U, W // 2 - U, H // 2 + U)     # 領域自
RECT_ENEMY = pygame.Rect(W // 2, U, W // 2 - U, H // 2 + U)     # 領域敵
RECT_MENU  = pygame.Rect(U, H // 2 + U * 2, W - U * 2, H // 2 - U * 3)  # 領域メニュー

# 描画
def render():
    t_hero = f"HP:{hero.hp}/{hero.hp_max} MP:{hero.mp}/{hero.mp_max}"   # ステータス自
    t_enemy = f"{enemy.name} HP:{enemy.hp}/{enemy.hp_max}"             # ステータス敵
    render_stat(RECT_HERO,  t_hero)     # ステータスの描画
    render_stat(RECT_ENEMY, t_enemy)    # ステータスの描画
    render_char(RECT_HERO,  hero.iref,  data.ef_hero)   # キャラクターの描画
    render_char(RECT_ENEMY, enemy.iref, data.ef_enemy)  # キャラクターの描画
    render_menu()   # メニューの描画
    render_frame()  # 枠の描画

# ステータスの描画
def render_stat(rect, text):
    # 文字
    size = img.font.get_rect(text)  # 描画領域四角形
    x = rect.x + rect.w / 2 - size.w / 2    # 中央になるX位置
    y = rect.y + U * 0.5    # Y位置
    img.font.render_to(data.screen, (x, y), text, COL_W)    # 文字描画

# キャラクターの描画
def render_char(rect, iref, is_ef):
    # 拡大画像準備
    time = pygame.time.get_ticks()  # ゲーム開始からの経過時間
    ref = iref + time // 500 % 2    # 画像参照位置
    image = pygame.transform.scale(img.chara[ref], (U * 6, U * 6))  # 拡大画像

    # キャラ画像の描画
    x = rect.x + U * 1.5    # キャラX位置
    y = rect.y + U * 2      # キャラY位置
    if is_ef:   # 演出の有無
        pygame.draw.rect(data.screen, (255, 0, 0), rect)    # 背景塗りつぶし
        data.screen.blit(image, (x, y))     # キャラ画像描画

        # 50ミリ秒ごとにガタガタと揺らす
        x += - U // 2 + int(U * (time // 50 * 11 % 13 / 13))    # X位置修正
        y += - U // 2 + int(U * (time // 50 * 11 % 17 / 17))    # Y位置修正
    data.screen.blit(image, (x, y))     # キャラ画像描画

# メニューの描画
def render_menu():
    line_h = img.fsz * 1.25     # 行の高さ
    for i, text in enumerate(data.menu_texts):
        size = img.font.get_rect(text)  # 描画領域四角形
        x = RECT_MENU.x + RECT_MENU.w / 2 - size.w / 2  # 中央になるX位置
        y = RECT_MENU.y + U * 0.75 + line_h * i     # Y位置
        if i == data.menu_sel:
            # 選択メッセージ背景
            rect = (RECT_MENU.x, y - img.fsz * 0.25, RECT_MENU.w, line_h) # 背景の四角形
            pygame.draw.rect(data.screen, COL_G, rect)      # 四角形の塗りつぶし
        img.font.render_to(data.screen, (x, y), text, COL_W)    # 文字描画

# 枠の描画
def render_frame():
    line_w = U // 8     # 線の太さ
    pygame.draw.rect(data.screen, COL_W, RECT_HERO, line_w)     # 自
    pygame.draw.rect(data.screen, COL_W, RECT_ENEMY, line_w)    # 敵
    pygame.draw.rect(data.screen, COL_W, RECT_MENU, line_w)     # メニュー

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

3つの領域

 続く定数のRECT_HERORECT_ENEMYRECT_MENUは、バトル画面の左上、右上、下の領域の四角形です。

3つの領域
RECT_HERO  = pygame.Rect(U,      U, W // 2 - U, H // 2 + U)     # 領域自
RECT_ENEMY = pygame.Rect(W // 2, U, W // 2 - U, H // 2 + U)     # 領域敵
RECT_MENU  = pygame.Rect(U, H // 2 + U * 2, W - U * 2, H // 2 - U * 3)  # 領域メニュー
バトル画面の3つの領域
バトル画面の3つの領域

まとめて描画

 最初のrender()関数は、それぞれの描画処理を呼び出します。

まとめて描画
# 描画
def render():
    t_hero = f"HP:{hero.hp}/{hero.hp_max} MP:{hero.mp}/{hero.mp_max}"   # ステータス自
    t_enemy = f"{enemy.name} HP:{enemy.hp}/{enemy.hp_max}"             # ステータス敵
    render_stat(RECT_HERO,  t_hero)     # ステータスの描画
    render_stat(RECT_ENEMY, t_enemy)    # ステータスの描画
    render_char(RECT_HERO,  hero.iref,  data.ef_hero)   # キャラクターの描画
    render_char(RECT_ENEMY, enemy.iref, data.ef_enemy)  # キャラクターの描画
    render_menu()   # メニューの描画
    render_frame()  # 枠の描画

 主人公側のステータスt_heroはHPとMP、敵側のステータスt_enemyは名前とHPにしています。領域の幅が限られているので、全ての能力値は表示しません。

ステータスの描画

 次はステータスの描画をおこなうrender_stat()関数です。

ステータスの描画
# ステータスの描画
def render_stat(rect, text):
    # 文字
    size = img.font.get_rect(text)  # 描画領域四角形
    x = rect.x + rect.w / 2 - size.w / 2    # 中央になるX位置
    y = rect.y + U * 0.5    # Y位置
    img.font.render_to(data.screen, (x, y), text, COL_W)    # 文字描画

 img.fontget_rect()関数で、textを描画した場合の横幅と高さのpygame.Rectオブジェクトを得ます。

 このRectの横幅size.wを利用して、描画X位置が領域の中央になるようにします。

キャラクターの描画

 次はキャラクターの描画をおこなうrender_char()関数です。

キャラクターの描画
# キャラクターの描画
def render_char(rect, iref, is_ef):
    # 拡大画像準備
    time = pygame.time.get_ticks()  # ゲーム開始からの経過時間
    ref = iref + time // 500 % 2    # 画像参照位置
    image = pygame.transform.scale(img.chara[ref], (U * 6, U * 6))  # 拡大画像

    # キャラ画像の描画
    x = rect.x + U * 1.5    # キャラX位置
    y = rect.y + U * 2      # キャラY位置
    if is_ef:   # 演出の有無
        pygame.draw.rect(data.screen, (255, 0, 0), rect)    # 背景塗りつぶし
        data.screen.blit(image, (x, y))     # キャラ画像描画

        # 50ミリ秒ごとにガタガタと揺らす
        x += - U // 2 + int(U * (time // 50 * 11 % 13 / 13))    # X位置修正
        y += - U // 2 + int(U * (time // 50 * 11 % 17 / 17))    # Y位置修正
    data.screen.blit(image, (x, y))     # キャラ画像描画

 画像参照位置irefに、500ミリ秒ごとに0、1、0、1、……と切り替わるtime // 500 % 2を足してアニメーションさせます。

 画像はpygame.transform.scale()関数で拡大してから描画します。

 is_efの値(data.ef_herodata.ef_enemy)がTrueのときは、背景を赤(255, 0, 0)で塗りつぶして、50ミリ秒ごとにガタガタと揺らします。

ガタガタと揺らす処理
ガタガタと揺らす処理

メニューの描画

 次はメニューの描画です。

メニューの描画
# メニューの描画
def render_menu():
    line_h = img.fsz * 1.25     # 行の高さ
    for i, text in enumerate(data.menu_texts):
        size = img.font.get_rect(text)  # 描画領域四角形
        x = RECT_MENU.x + RECT_MENU.w / 2 - size.w / 2  # 中央になるX位置
        y = RECT_MENU.y + U * 0.75 + line_h * i     # Y位置
        if i == data.menu_sel:
            # 選択メッセージ背景
            rect = (RECT_MENU.x, y - img.fsz * 0.25, RECT_MENU.w, line_h) # 背景の四角形
            pygame.draw.rect(data.screen, COL_G, rect)      # 四角形の塗りつぶし
        img.font.render_to(data.screen, (x, y), text, COL_W)    # 文字描画

 リストdata.menu_texts内のテキストを1行ずつ描画します。

 ステータスと同様に、img.fontget_rect()関数で、テキストtextを描画した場合の横幅と高さのpygame.Rectオブジェクトを得ます。このRectの横幅size.wを利用して、描画X位置が領域の中央になるようにします。

 また、要素のインデックスidata.menu_selと等しいときに、灰色で四角形を塗ります。こうすることで、選択項目であることを示します。

枠の描画

 最後は枠の描画をおこなうrender_frame()関数です。

枠の描画
# 枠の描画
def render_frame():
    line_w = U // 8     # 線の太さ
    pygame.draw.rect(data.screen, COL_W, RECT_HERO, line_w)     # 自
    pygame.draw.rect(data.screen, COL_W, RECT_ENEMY, line_w)    # 敵
    pygame.draw.rect(data.screen, COL_W, RECT_MENU, line_w)     # メニュー

 各領域の四角形を白線で描画します。

まとめと次回

 これで、短いながらも、タイトル、マップ、バトル画面を行き来するレトロ風RPGが完成しました。アイテムも会話もないシンプルなRPGですが、処理を追加すればより複雑なゲームにすることができます。

 さて、最初の数回で学んだPythonの知識だけでプログラムを書いてきましたが、Pythonにはまださまざまな仕様があります。

 次回はクラスを取り上げます。クラスはデータと処理をひとまとめにあつかえるデータ構造です。また、クラスを利用してPygameのスプライトもあつかいます。

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

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

もっと読む

この記事の著者

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

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

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

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

この記事をシェア

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

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング