CODE Python RPG

独学でPythonでRPGを作成する(第21回)モジュール化

更新日:

こんにちは。
「Pythonでつくる ゲーム開発 入門講座」のChapter11,12(本格RPGを作ろう!全編・後編)を基にRPGを作っていきます。

前回はバグ修正をしてプログラム(ソースコード)を公開しました。

今回は長くなってきたプログラムを分割(モジュール化)します。
また、分割したプログラムを公開します。

目次

  1. モジュール
  2. マップデータを保持
  3. プレイヤーがマップ上を動く
  4. 戦闘シーン
  5. 最後に

モジュール

one_hour_dungeon.pyのファイルを分割してモジュール化します。
モジュールはクラスや関数を集めたもの、です。

使い方は以下の通りです。
import モジュール名
モジュール名.クラス名
モジュール名.関数名

これはchara.pyで既に使っています。

one_hour_dungeon.py

(省略)
import chara # インポート
(省略)
player = chara.Brave()

chara.pyにはBraveクラスがあるので
chara.Brave()でオブジェクトを生成しています。

生成したplayer(オブジェクト)で
player.変数やplayer.関数といった使い方をしています。

def main(): # メイン処理
(省略)
        elif idx == 17: # レベルアップ
            draw_battle(screen, fontS, turn_obj)
            if tmr == 1:
                set_message(player.name + " はレベルが上がった!")
                player.lv_up()

player.「name」、player.「lv_up()」などは
chara.pyにある変数や関数です。
※モジュールの詳しい説明はこちら

こんな感じで「one_hour_dungeon.py」を以下の機能に分割します。

・キャラデータを保持 → chara.py
・マップデータを保持 → maps.py
・プレイヤーがマップ上を動く → move.py
・戦闘シーン → battle.py

これらを「one_hour_dungeon.py」にインポートします。

 

 

マップデータを保持

それではまずマップデータを保持するmaps.pyです。
※chara.pyは既にあるので省きます。(chara.pyはこちら

今まではmake_dungeon関数でマップを生成していましたが、
これを消します。

代わりにmaps.pyにマップデータを保持させて、
ここから取得するようにします。

maps.py

def get_map(num, player):
    if num == 0:
        maps = [
            [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9],
            [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
        ]
    player.d = 1 # プレイヤーの向き
    player.a = 2 # プライヤーの画像
    player.x = 3 # マップの最初のx座標
    player.y = 3 # マップの最初のy座標
    return maps

 

「one_hour_dungeon.py」から以下のように呼び出します。
numによって、取得するマップデータを変更します。

n_map = maps.get_map(0, player)

 

そして変数「maps」にマップデータを入れて、
「one_hour_dungeon.py」に返します。
0は床、1は宝箱、2は繭、3は階段、9は壁です。

画像にある数字はmapsが持っているデータです。

また、このマップに来たときに
最初にプレイヤーがいる場所と向きを指定しています。

player.d = 1 # プレイヤーの向き
player.a = 2 # プライヤーの画像
player.x = 3 # マップの最初のx座標
player.y = 3 # マップの最初のy座標

 

今は1つのマップのみですが(numが0のみ)、
1つのゲームでマップは複数になることがほとんどだと思います。

全てのマップ情報をこのmaps.pyで保持して、プレイヤーがマップを
移動した時はこのモジュールを呼び出して、マップ情報を取得します。
※マップが複数になったら、numも1, 2, ...と増えていきます。

 

 

プレイヤーがマップ上を動く

次にプレイヤーがマップ上を動くモジュールです。

これは元の「one_hour_dungeon.py」から以下の関数を保持します。

・draw_map
・move_player
・draw_para
・draw_text
・mainの必要な機能

プレイヤーがマップ上を動く時に必要な関数です。

先程も記載しましたが、マップ情報はmaps.pyから取得するので、
make_dungeon関数は消します。
draw_mapはmake_dungeon関数の代わりです。

move.py

import pygame
import sys
import random
from pygame.locals import *

# 色の定義
WHITE = (255, 255, 255)
WARNING = (255, 191, 0)
DANGER = (255, 101, 101)
BLACK = (0, 0, 0)
RED   = (255, 0, 0) # プレイヤーの体力・食料が僅かの時、ゲームオーバーの時
CYAN  = (0, 255, 255)
BLINK = [(224,255,255), (192,240,255), (128,224,255), (64,192,255), (128,224,255), (192,240,255)] # 選択中の戦闘コマンドなどを点滅させる

treasure = 0 # TRE_NAMEの添字 1,2,3:宝箱、4,5:繭

n_map = [[]]
map_w = 0
map_h = 0
food = 10
floor = 1
tmr = 0

imgWall = pygame.image.load("image/wall.png")
imgWall2 = pygame.image.load("image/wall2.png")
imgDark = pygame.image.load("image/dark.png")
imgPara = pygame.image.load("image/parameter.png")
imgItem = [
    pygame.image.load("image/potion.png"),
    pygame.image.load("image/blaze_gem.png"),
    pygame.image.load("image/spoiled.png"),
    pygame.image.load("image/apple.png"),
    pygame.image.load("image/meat.png")
]
imgFloor = [
    pygame.image.load("image/floor.png"),
    pygame.image.load("image/tbox.png"),
    pygame.image.load("image/cocoon.png"),
    pygame.image.load("image/stairs.png")
]

TRE_NAME = ["Potion", "Blaze gem", "Food spoiled.", "Food +20", "Food +100"]


def draw_map(bg, fnt, player): # マップを描画する
    bg.fill(BLACK)
    for y in range(-4, 6):
        for x in range(-5, 6):
            X = (x+5)*80
            Y = (y+4)*80
            dx = player.x + x
            dy = player.y + y
            if 0 <= dx and dx < map_w and 0 <= dy and dy < map_h:
                if n_map[dy][dx] <= 3:
                    bg.blit(imgFloor[n_map[dy][dx]], [X, Y])
                if n_map[dy][dx] == 9:
                    bg.blit(imgWall, [X, Y-40])
                    if dy >= 1 and n_map[dy-1][dx] == 9:
                        bg.blit(imgWall2, [X, Y-80])
            if x == 0 and y == 0: # 主人公キャラの表示
                bg.blit(player.img[player.a], [X, Y-40])
    bg.blit(imgDark, [0, 0]) # 四隅が暗闇の画像を重ねる
    draw_para(bg, fnt, player) # 主人公の能力を表示

def move_player(key, player): # 主人公の移動
    global tmr, food, potion, blazegem, treasure

    if n_map[player.y][player.x] == 0: # 何もない床
        # プレイヤーがいる場所(リストimgFloor 0:床 1:宝箱 2:繭 3:階段 -にしたらリストの逆からになるので-4にしたら床になる)
        # これがないと連続して敵が出る。下の方で、移動後にいた場所を0にする
        n_map[player.y][player.x] = -4
        r = random.randint(0, 99)
        if r < 10: # 敵出現
            tmr = 0
            return 10
    elif n_map[player.y][player.x] == 1: # 宝箱に載った
        n_map[player.y][player.x] = 0
        treasure = random.choice([0,0,0,1,1,1,1,1,1,2])
        if treasure == 0:
            potion = potion + 1
        if treasure == 1:
            blazegem = blazegem + 1
        if treasure == 2: # ハズレ
            food = int(food/2)
        tmr = 0
        return 3
    elif n_map[player.y][player.x] == 2: # 繭に載った
        n_map[player.y][player.x] = 0
        r = random.randint(0, 99)
        if r < 40: # 食料
            treasure = random.choice([3,3,3,4])
            if treasure == 3:
                food = food + 20
            if treasure == 4:
                food = food + 100
            tmr = 0
            return 3
        return 1
    elif n_map[player.y][player.x] == 3: # 階段に載った
        tmr = 0
        return 2
    # 方向キーで上下左右に移動
    x = player.x # 移動したかを確認する為に今の位置を保存
    y = player.y
    if key[K_UP] == 1:
        player.d = 0
        if n_map[player.y-1][player.x] != 9:
            player.y = player.y - 1
    if key[K_DOWN] == 1:
        player.d = 1
        if n_map[player.y+1][player.x] != 9:
            player.y = player.y + 1
    if key[K_LEFT] == 1:
        player.d = 2
        if n_map[player.y][player.x-1] != 9:
            player.x = player.x - 1
    if key[K_RIGHT] == 1:
        player.d = 3
        if n_map[player.y][player.x+1] != 9:
            player.x = player.x + 1
    player.a = player.d*2 # 0:上 1:下 2:左 3:右
    if player.x != x or player.y != y: # 移動したら食料の量と体力を計算
        player.a = player.a + tmr%2 # 移動したら足踏みのアニメーション
        n_map[y][x] = 0 # いた場所を0にする
        if food > 0:
            food = food - 1
            if player.hp < player.maxhp:
                player.hp = player.hp + 1
        else:
            player.hp = player.hp - 5
            if player.hp <= 0:
                player.hp = 0
                pygame.mixer.music.stop()
                tmr = 0
                return 9
    return 1

def draw_para(bg, fnt, player): # 主人公の能力を表示
    X = 30
    Y = 600
    bg.blit(imgPara, [X, Y])
    col = WHITE
    if player.hp < 10 and tmr%2 == 0:
        col = RED
    draw_text(bg, "{}/{}".format(player.hp, player.maxhp), X+128, Y+6, fnt, col)
    draw_text(bg, str(player.atk), X+128, Y+33, fnt, WHITE)
    col = WHITE
    if food == 0 and tmr%2 == 0:
        col = RED
    draw_text(bg, str(food), X+128, Y+60, fnt, col)
    draw_text(bg, str(potion), X+266, Y+6, fnt, WHITE)
    draw_text(bg, str(blazegem), X+266, Y+33, fnt, WHITE)

def draw_text(bg, txt, x, y, fnt, col): # 影付き文字の表示
    sur = fnt.render(txt, True, BLACK)
    bg.blit(sur, [x+1, y+2])
    sur = fnt.render(txt, True, col)
    bg.blit(sur, [x, y])

def main(screen, clock, font, fontS, use_map, player):
    global n_map, map_w, map_h
    global floor, tmr
    n_map = use_map
    map_w = len(n_map[0])
    map_h = len(n_map)
    idx = 1 # 歩く

    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        tmr += 1
        key = pygame.key.get_pressed()

        if idx == 1:
            idx = move_player(key, player)
            draw_map(screen, fontS, player)
            draw_text(screen, "{}階 ({},{})".format(floor, player.x, player.y), 60, 40, fontS, WHITE)
        elif idx == 2: # 画面切り替え(階段)
            draw_map(screen, fontS, player)
            if 1 <= tmr and tmr <= 5:
                h = 80*tmr
                pygame.draw.rect(screen, BLACK, [0, 0, 880, h]) # 上側を閉じていく
                pygame.draw.rect(screen, BLACK, [0, 720-h, 880, h]) # 下側を閉じていく
            if tmr == 5:
                floor = floor + 1
                player.x = 3
                player.y = 3
            if 6 <= tmr and tmr <= 9:
                h = 80*(10-tmr)
                pygame.draw.rect(screen, BLACK, [0, 0, 880, h]) # 上側を開いていく
                pygame.draw.rect(screen, BLACK, [0, 720-h, 880, h]) # 下側を開いていく
            if tmr == 10:
                idx = 1

        elif idx == 3: # アイテム入手もしくはトラップ(宝箱・繭)
            draw_map(screen, fontS, player)
            screen.blit(imgItem[treasure], [320, 220]) # アイテム画像
            draw_text(screen, TRE_NAME[treasure], 380, 240, font, WHITE) # アイテムテキスト
            if tmr == 10:
                idx = 1
        elif idx == 9:
            if tmr <= 30:
                PL_TURN = [2, 4, 0, 6]
                player.a = PL_TURN[tmr%4] # プレイヤーを回転
                if tmr == 30:
                    player.a = 8 # 倒れた絵
                draw_map(screen, fontS, player)
            elif tmr == 31:
                draw_text(screen, "You died.", 360, 240, font, RED)
                draw_text(screen, "Game over.", 360, 380, font, RED)
            elif tmr == 80:
                return 0
        elif idx == 10: # 戦闘
            return 10
        pygame.display.update()
        clock.tick(10)

 
「one_hour_dungeon.py」から以下のように呼び出します。

idx = move.main(screen, clock, font, fontS, n_map, player)

マップデータである、n_mapを渡してmove.pyで使います。
その他は元々「one_hour_dungeon.py」で使っていた変数です。

戻り値として、idxを返します。
プレイヤーのhpが0になった時 → 0を返す
モンスターと遭遇した時 → 10を返す

 

他にマップ情報がdungeonからn_mapに変わった為
変数を以下のように変更しています。

変更前
DUNGEON_W = MAZE_W*3
DUNGEON_H = MAZE_H*3
dungeon = [] # 0:床 1:宝箱 2:繭 3:階段 9:壁

変更後
map_w = len(n_map[0])
map_h = len(n_map)
n_map # 0:床 1:宝箱 2:繭 3:階段 9:壁

 

その他は変更前の「one_hour_dungeon.py」と同じです。

 

 

戦闘シーン

最後に戦闘シーンのモジュールです。

これは元の「one_hour_dungeon.py」から以下の関数を保持します。

・draw_text
・init_battle
・draw_battle
・battle_command
・battle_select
・set_battle_turn
・get_battle_turn
・del_battle_turn
・init_message
・set_message
・mainの必要な機能

戦闘時に必要な関数です。
main以外の関数はほぼ変わっていないので、今回は省略します。
変更前のプログラムはこちら

変更は引数にplayerを追加したのみです。
playerがグローバル変数ではなくなる為です。

・draw_battle(bg, fnt, obj, player)
・set_battle_turn(num, player):
・get_battle_turn(player)

battle.py

import pygame
import sys
import random
from pygame.locals import *

import chara

import mojimoji # 半角⇄全角変換
import time
import collections

# 色の定義
WHITE = (255, 255, 255)
WARNING = (255, 191, 0)
DANGER = (255, 101, 101)
BLACK = (0, 0, 0)
RED   = (255, 0, 0) # プレイヤーの体力・食料が僅かの時、ゲームオーバーの時
CYAN  = (0, 255, 255)
BLINK = [(224,255,255), (192,240,255), (128,224,255), (64,192,255), (128,224,255), (192,240,255)] # 選択中の戦闘コマンドなどを点滅させる

# 画像の読み込み
imgBtlBG = pygame.image.load("image/btlbg.png")
imgEffect = [
    pygame.image.load("image/effect_a.png"), # 攻撃
]

# 変数の宣言
# 0: タイトル画面 1: プレイヤーの移動 2: 画面切り替え(階段) 3: アイテム入手もしくはトラップ(宝箱・繭) 9: ゲームオーバー 10: 戦闘開始 11: プレイヤーのターン(入力待ち)
# 12: プレイヤーの攻撃 13: 敵のターン、敵の攻撃 14: 逃げられる? 15: 敗北 16: 勝利 17: レベルアップ 20: Potion 21: Blaze gem 22: 戦闘終了
idx = 0
tmr = 0
floor = 0 # 階層 出現する敵や敵のレベルに影響(階層が上がるほど出現する敵の種類が増え、レベルの上限が上がる)出現する敵とレベルはランダム
fl_max = 1 # 最高到達階層 タイトル画面に表示

monster = [] # 敵のオブジェクト
dead_monster = [] # 倒した敵のオブジェクト
emy_step = 0 # 敵が攻撃する時の動きの大きさ(前に出るステップの大きさ)
emy_blink = 0 # 攻撃した時に敵を点滅させる(奇数:表示させない、偶数:表示させる)

dmg_eff = 0
btl_cmd_x = 0 # コマンド選択の時の"▶︎"のx位置
btl_cmd_y = 0 # コマンド選択の時の"▶︎"のy位置
btl_enemy = 0 # 敵選択の時の"▶︎"の位置
battle_order = {} # 戦闘の順番

COMMAND = [["こうげき", "どうぐ"], ["じゅもん", "そうび"], ["ぼうぎょ", "にげる"]]
TRE_NAME = ["Potion", "Blaze gem", "Food spoiled.", "Food +20", "Food +100"]

(省略)

def main(screen, clock, font, fontS, player):
    global idx, tmr, floor
    global emy_step, emy_blink, dmg_eff
    global btl_cmd_x, btl_cmd_y, btl_enemy

    idx = 10
    tmr = 0
    dmg = 0 # プレイヤーが与えるダメージ、受けるダメージ

    turn_obj = "" # 戦闘で行動するオブジェクト
    btl_exp = 0 # 戦闘で獲得した経験値(逃げたら0)
    btl_start = 0 # 0:通常、1:先制攻撃、2:不意打ち
    mon_typ = "" # モンスターの種類が複数:"まもののむれ"、1種類:モンスターの名前

    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        tmr = tmr + 1
        key = pygame.key.get_pressed()

        if idx == 9: # ゲームオーバー
            if tmr == 10:
                monster.clear() # モンスターオブジェクトを削除
                dead_monster.clear()
                return 0

        elif idx == 10: # 戦闘開始
            if tmr == 1:
                mon_typ = init_battle(screen)
                init_message()
            elif tmr <= 4:
                bx = (4-tmr)*220
                by = 0
                screen.blit(imgBtlBG, [bx, by]) # バトル画面を右から左へ挿入していく
                draw_text(screen, "Encounter!", 350, 200, font, WHITE)
            elif tmr == 5:
                set_message(monster[0].name + " が現れた!")
                draw_battle(screen, fontS, turn_obj, player)
            elif tmr == 6:
                if len(monster) >= 2:
                    set_message(monster[1].name + " が現れた!")
                    draw_battle(screen, fontS, turn_obj, player)
                    time.sleep(0.3)
            elif tmr == 7:
                if len(monster) >= 3:
                    set_message(monster[2].name + " が現れた!")
                    draw_battle(screen, fontS, turn_obj, player)
                    time.sleep(0.3)
            elif tmr == 8:
                if len(monster) == 4:
                    set_message(monster[3].name + " が現れた!")
                    draw_battle(screen, fontS, turn_obj, player)
                    time.sleep(0.3)
            elif tmr == 9:
                btl_start = random.randint(1, 32)
                if btl_start == 1: # 先制攻撃
                    init_message()
                    if random.randint(0, 1) == 0:
                        set_message("しかし " + mon_typ + "は")
                        set_message("まだ こちらに きづいていない!")
                    else:
                        set_message("しかし " + mon_typ + "は")
                        set_message("おどろき とまどっている!")
                    draw_battle(screen, fontS, turn_obj, player)
                    time.sleep(0.5)
                elif btl_start == 2: # 不意打ち
                    init_message()
                    if random.randint(0, 1) == 0:
                        set_message(mon_typ + "は")
                        set_message("いきなり おそいかかってきた!")
                    else:
                        set_message(mon_typ + "は")
                        set_message(player.name + "が みがまえるまえに")
                        set_message("おそいかかってきた!")
                    draw_battle(screen, fontS, turn_obj, player)
                    time.sleep(0.5)
                else: # 通常攻撃
                    btl_start = 0
                    
            elif tmr <= 16:
                draw_battle(screen, fontS, turn_obj, player)
                time.sleep(1)
                # draw_text(screen, monster.name+" appear!", 300, 150, font, WHITE)
                init_message()
                if btl_start == 2: # 表示時間の問題でここで改めて判定
                    idx = 23
                else:
                    idx = 11
                tmr = 0

        elif idx == 11: # プレイヤーのターン(入力待ち)
            btl_enemy = 0
            player.re_defense() # ぼうぎょを元に戻す(ぼうぎょ効果を消す)(呪文効果が残っているか確認)
            draw_battle(screen, fontS, turn_obj, player)
            if battle_command(screen, key) == True:
                if COMMAND[btl_cmd_y][btl_cmd_x] == "こうげき":
                    idx = 19
                    tmr = 0
                if COMMAND[btl_cmd_y][btl_cmd_x] == "じゅもん":
                    idx = 18
                    tmr = 0
                if COMMAND[btl_cmd_y][btl_cmd_x] == "ぼうぎょ":
                    player.act = 18 # get_battle_turnの戻り値idxを18
                    player.defense() # 選択した時点で防御力2倍(ターンが回ってきてからではない)
                    idx = 23
                    tmr = 0
                if COMMAND[btl_cmd_y][btl_cmd_x] == "にげる":
                    idx = 14
                    tmr = 0

                # コマンドの位置のリセット
                btl_cmd_x = 0
                btl_cmd_y = 0

        elif idx == 12: # プレイヤーの攻撃
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                set_message(turn_obj.name + " の攻撃!")
                dmg = player.attack(monster[btl_enemy])
            if 2 <= tmr and tmr <= 4:
                screen.blit(imgEffect[0], [monster[btl_enemy].x+monster[btl_enemy].img.get_width()-tmr*80, tmr*80])
            if tmr == 5:
                emy_blink = 5
                if dmg > 0:
                    set_message(monster[btl_enemy].name + "に " + str(dmg)+"ポイントのダメージを与えた!")
                else:
                    set_message("ミス!" + monster[btl_enemy].name + "にダメージを与えられない!")

            if tmr == 11:
                monster[btl_enemy].hp = monster[btl_enemy].hp - dmg
                if monster[btl_enemy].hp <= 0:
                    monster[btl_enemy].hp = 0
                    set_message(turn_obj.name + " は " + monster[btl_enemy].name + " をやっつけた!")
                    btl_exp += monster[btl_enemy].exp
                    del_battle_turn(monster[btl_enemy]) # ターンオブジェクトから消す(倒したモンスターは攻撃しない)
                    dead_monster.append(monster[btl_enemy]) # 先に加える
                    monster.pop(btl_enemy)

                if not monster: # 空だとFalseを返すので not monsterがTrueだと空
                    idx = 16 # 勝利
                    tmr = 0
            if tmr == 16:
                init_message()
                idx = 24 # ターンの確認
                tmr = 0

        elif idx == 13: # 敵のターン、敵の攻撃
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 5:
                set_message(turn_obj.name + " の攻撃!")
                emy_step = 30
            if tmr == 9:
                dmg = turn_obj.attack(player)
                if dmg > 0:
                    set_message(player.name + "は " + str(dmg) + "ポイントのダメージを受けた!")
                    dmg_eff = 5
                else:
                    set_message("ミス!" + player.name + " はダメージを受けない!")
                    dmg_eff = 0
                emy_step = 0
            if tmr == 15:
                player.hp = player.hp - dmg
                if player.hp <= 0:
                    player.hp = 0
                    idx = 15 # 敗北
                    tmr = 0
            if tmr == 20:
                init_message()
                idx = 24 # ターンの確認
                tmr = 0

        elif idx == 14: # 逃げられる?
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1: set_message(player.name + " は逃げ出した!")
            if tmr == 5:
                if random.randint(0, 99) < 60:
                    btl_exp = 0 # 逃げたら0
                    idx = 22 # 戦闘終了
                else:
                    set_message("しかし、まわりこまれてしまった!")
            if tmr == 10:
                init_message()
                btl_start = 2 # 不意打ちと同じ状態にする
                idx = 23 # ターンセット
                tmr = 0
             
        elif idx == 15: # 敗北
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                set_message(player.name + " は気絶した...")
            if tmr == 11:
                idx = 9 # ゲームオーバー
                tmr = 0

        elif idx == 16: # 勝利
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                if len(dead_monster) != 1: # 1匹の時はいらない
                    set_message(player.name + " は " + mon_typ + " をやっつけた!")
                player.exp += btl_exp
                if player.exp >= player.lv_exp:
                    idx = 17
                    tmr = 0
            if tmr == 28:
                idx = 22 # 戦闘終了

        elif idx == 17: # レベルアップ
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                set_message(player.name + " はレベルが上がった!")
                # se[4].play()
                # lif_p = random.randint(10, 20)
                # str_p = random.randint(5, 10)
                player.lv_up()
            if tmr == 21:
                # set_message("Max life + "+str(lif_p))
                # player.maxhp = player.maxhp + lif_p
                init_message()
                set_message("最大HP:"+str(player.maxhp))
            if tmr == 26:
                set_message("素早さ:"+str(player.quick))
            if tmr == 30:
                set_message("攻撃力:"+str(player.atk))
            if tmr == 34:
                set_message("防御力:"+str(player.dfs))
            if tmr == 40:
                if player.exp >= player.lv_exp:
                    idx = 17
                    tmr = 0
            if tmr == 50:
                idx = 22 # 戦闘終了
        
        elif idx == 18: # ぼうぎょ
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                player.defense()
                set_message(player.name + "は みをまもっている!")
            if tmr == 5:
                init_message()
                idx = 24 # ターンの確認
                tmr = 0

        elif idx == 19: # 敵の選択
            draw_battle(screen, fontS, turn_obj, player)
            if battle_select(screen, key) == True:
                player.act = 12 # get_battle_turnの戻り値idxを12
                idx = 23
                tmr = 0

        elif idx == 22: # 戦闘終了
            idx = 1
            monster.clear() # モンスターオブジェクトを削除
            dead_monster.clear()
            btl_exp = 0
            return 1

        elif idx == 23: # ターンセット
            set_battle_turn(btl_start, player)
            btl_start = 0 # 戦闘は通常に戻す
            idx = 24
            tmr = 0

        elif idx == 24: # ターン確認
            tmr = 0 # 0にしないと idx=12では battle_calに行かない()
            turn_obj, idx = get_battle_turn(player)

        pygame.display.update()
        clock.tick(10)

 
「one_hour_dungeon.py」から以下のように呼び出します。

idx = battle.main(screen, clock, font, fontS, player)

戻り値として、idxを返します。
戦闘に敗北した時 → 0を返す
戦闘に勝利した時 → 1を返す

また、モンスター生成にchara.pyを使う為
インポートしています。

 

 

main

最後に「one_hour_dungeon.py」です。
全てのモジュールをインポートします。

・キャラデータを保持 → chara.py
・マップデータを保持 → maps.py
・プレイヤーがマップ上を動く → move.py
・戦闘シーン → battle.py

import pygame
import sys
from pygame.locals import *

import chara
import maps, move, battle

# 色の定義
WHITE = (255, 255, 255)
WARNING = (255, 191, 0)
DANGER = (255, 101, 101)
BLACK = (0, 0, 0)
RED   = (255, 0, 0) # プレイヤーの体力・食料が僅かの時、ゲームオーバーの時
CYAN  = (0, 255, 255)
BLINK = [(224,255,255), (192,240,255), (128,224,255), (64,192,255), (128,224,255), (192,240,255)] # 選択中の戦闘コマンドなどを点滅させる

# 画像の読み込み
imgTitle = pygame.image.load("image/title.png")
# 変数の宣言
speed = 1 # 速度(1-3) 大きいほど速い sキーで変化
# 0: タイトル画面 1: プレイヤーの移動 2: 画面切り替え(階段) 3: アイテム入手もしくはトラップ(宝箱・繭) 9: ゲームオーバー 10: 戦闘開始 11: プレイヤーのターン(入力待ち)
# 12: プレイヤーの攻撃 13: 敵のターン、敵の攻撃 14: 逃げられる? 15: 敗北 16: 勝利 17: レベルアップ 20: Potion 21: Blaze gem 22: 戦闘終了
idx = 0
tmr = 0
floor = 0 # 階層 出現する敵や敵のレベルに影響(階層が上がるほど出現する敵の種類が増え、レベルの上限が上がる)出現する敵とレベルはランダム
fl_max = 1 # 最高到達階層 タイトル画面に表示

player = chara.Brave()

n_map = [[]]

def draw_text(bg, txt, x, y, fnt, col): # 影付き文字の表示
    sur = fnt.render(txt, True, BLACK)
    bg.blit(sur, [x+1, y+2])
    sur = fnt.render(txt, True, col)
    bg.blit(sur, [x, y])

def main(): # メイン処理
    global speed, idx, tmr, floor, fl_max
    global n_map
    global food

    pygame.init()
    pygame.display.set_caption("One hour Dungeon") # タイトル
    screen = pygame.display.set_mode((880, 720))
    clock = pygame.time.Clock()
    font = pygame.font.Font(None, 40) # fontS以外の画面表示フォント
    fontS = pygame.font.Font("font/JKG-L_3.ttf", 20, bold=True) # プレイヤーのパラメータ・位置情報・スピードなどの表示フォント
    fontS.set_bold(True) # ↑でなぜかTrueになっていない → print(fontS.get_bold())

    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN:
                if event.key == K_s:
                    speed = speed + 1
                    if speed == 4:
                        speed = 1

        tmr = tmr + 1
        key = pygame.key.get_pressed()

        if idx == 0: # タイトル画面
            screen.fill(BLACK)
            screen.blit(imgTitle, [40, 60])
            if fl_max >= 2:
                draw_text(screen, "You reached floor {}.".format(fl_max), 300, 460, font, CYAN)
            draw_text(screen, "Press space key", 320, 560, font, BLINK[tmr%6])
            if key[K_SPACE] == 1:
                player.reset()
                floor = 1
                food = 300
                idx = 2 # マップの設定
        elif idx == 1:
            idx = move.main(screen, clock, font, fontS, n_map, player)
            tmr = 0
        elif idx == 2: # マップの設定
            n_map = maps.get_map(0, player)
            idx = 1
        elif idx == 10: # 戦闘開始
            idx = battle.main(screen, clock, font, fontS, player)
            tmr = 0
        draw_text(screen, "[S]peed "+str(speed), 740, 40, fontS, WHITE)

        pygame.display.update()
        clock.tick(4+2*speed)

if __name__ == '__main__':
    main()

 

モジュールmoveとbattleは以下のように呼び出しています。

idx = move.main(screen, clock, font, fontS, n_map, player)
idx = battle.main(screen, clock, font, fontS, player)

move.mainでモンスターと遭遇したら、idxが10になるので、
battle.mainへ移動します。

battle.mainで勝利したら、idxが1になるので、
move.mainへ移動します。

 

マップ生成の変更はありますが、
これで前回までと同じ動きになりました。

という感じで、今回は以上です。!(`・ω・´)

 

 

最後に

今回は長くなってきたプログラムを分割(モジュール化)しました。
なんとなくスッキリしました。(^^♪

次回から戦闘シーンを変える時はbattle.pyを
マップ上を動くところを変える時はmove.pyを
主に変えていきます。

次回は呪文の機能を追加していこうと思います。

まとめサイトへ

 

 

【ご注意】
プログラムやデータなどは著作権法により保護されています。
著作者の許諾を得ずに、プログラムおよびデータそのものまたは改変したものを
配布したり販売したりすることはできません。
また、これらを利用して発生した損害などに関して、著作者は一切責任を負いません。

 

 

-CODE, Python, RPG
-, , , , , , , ,

Copyright© kerublog , 2021 All Rights Reserved Powered by STINGER.