CODE Python RPG

独学でPythonでRPGを作成する(第31回)マップごとに出現モンスターを変える

更新日:

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

前回はマップの1マスの大きさを変えました。

今回はマップ(エリア)ごとにモンスターエリアという値を追加して
出現するモンスターを変えます。

目次

  1. モンスターエリア
  2. モンスターエリアの追加
  3. 結果
  4. 最後に

モンスターエリア

マップごとに出現するモンスターを変えます。

例えば、以下の図の四角で囲ったエリアで
モンスターに遭遇した時に
色が異なるエリアでは出現するモンスターを変えます。
※同じ色のエリアは同じモンスターが出現です。

 

まず、スプレッドシートのモンスターシートに
エリアの列を追加します。(L列)

 

次にマップにも同じくエリアを追加します。
今までは以下のように、
mapsにはマップの画像の数字のみ指定していました。
※maps.py です。

 

これを以下のような値に変更します。

maps = [["0000", "0101",......]]

"0000"の前の2つ(00)をエリア、
後ろの(00)をマップの画像の数字にします。

マップのエリアとモンスターシートのエリアが
一致するモンスターが出現するようにします。

 

ちなみにpythonのリストは
以下のようにスライス(切り取り)します。

list[start:end]
※start と end はインデックス。

startを省略すると最初から、
endを省略すると最後まで
スライスします。
※詳細はこちら

ex_list = ["1234"]
の場合、
ex_list[:2] = "12"
ex_list[2:] = "34"
です。

 

なので、mapsは
[:2]とするとモンスターエリア(最初の2文字)
[2:]とするとマップの画像の数字(最後の2文字)
となります。

 

 

モンスターエリアの追加

コードにモンスターエリアを追加して行きます。
今回は珍しく全てのファイルを変えます。

 

chara.py

最初に chara.py です。

(省略)
mon_name = ws_mon.col_values(2) # B列 名前
mon_maxhp = ws_mon.col_values(4) # D列 最大HP
mon_quick = ws_mon.col_values(8) # H列 素早さ
mon_atk = ws_mon.col_values(9) # I列 攻撃力
mon_dfs = ws_mon.col_values(10) # J列 防御力
mon_exp = ws_mon.col_values(11) # K列 経験値
mon_area = ws_mon.col_values(12) # L列 エリア ⇦追加

(省略)
def get_mon_area_list():
    return mon_area

mon_area 追加しています。

また、このmon_areaを返す関数get_mon_area_listを
最後に追加しました。
後述しますが、battle.pyで使います。

 

maps.py

次に maps.py ですが、
これはファイルをアップしました。

※mapsは手打ちで作っているので
 エライ苦労しています(^_^;)
 画像データを読み取って
 楽に作ることも出来そうな・・
 私には出来ませんが。

 

one_hour_dungeon.py

続いて one_hour_dungeon.py です。
最初に使わなくなった変数が
残っていたので消します(^_^;)

(省略)
potion = 0 # ポーションを使える回数(使うと全快する) # 削除
blazegem = 0 # blazeを使える回数 # 削除

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

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

そして変更です。
変更前

(省略)
def main(): # メイン処理
(省略)

        elif idx == 1:
            idx, tmr = move.main(screen, clock, font, fontS, n_map, player)
        elif idx == 2: # マップの設定
            n_map = maps.get_map(0, player)
            idx = 1
        elif idx == 10: # 戦闘開始
            idx, tmr = battle.main(screen, clock, font, fontS, player)

 

変更後

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

    n_map = [[]] # 0:床 1:宝箱 2:繭 3:階段 9:壁
    mon_area = 99 # 追加
(省略)

        elif idx == 1:
            tmr = 0
            idx, mon_area = move.main(screen, clock, font, fontS, n_map, player)
(省略)

        elif idx == 10: # 戦闘開始
            tmr = 0
            idx = battle.main(screen, clock, font, fontS, player, mon_area)

 

モンスターエリアを入れる変数mon_areaを追加しました。
初期値(99)は適当です。

move.pyのmainの戻り値と
battle.pyのmainの引数に
mon_areaを追加しました。

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

 

move.pyでプレイヤーが移動中にモンスターと遭遇した時に
モンスターエリアを取得して、それをbattle.pyに渡します。

あと、変更前はtmrをなぜか戻り値として
受け取っていましたが、0固定で返していたので
tmr = 0
にしています。
※one_hour_dungeon.py では
 tmrの意味が薄れてきている気がしますが一応(^_^;)

 

move.py

更に続いて move.py です。

まず、型変換をします。
これはmapsが数値から文字列へ変更した為です。

変更前は数値。
maps = [[16, 16, ...]]

変更後は文字列。
maps = [["0000", "0101",......]]

 

このmapsは move.py で
変数n_map として使っています。

それをimgmap(マップの画像)が
imgmap[n_map[dy][dx]]
のようにインデックスにしています。
※imgmap = [pygame.image.load("map/0.png"), ...]

なので、n_mapの値は数値(int型)で
ある必要があります。

 

と、いうわけで型変換です。

(省略)
def draw_map(bg, fnt, player):
(省略)

            if 0 <= dx and dx < map_w and 0 <= dy and dy < map_h:
                bg.blit(imgmap[int(n_map[dy][dx][2:])], [X, Y])
(省略)

def move_player(key, player): # 主人公の移動
(省略)

    if key[K_UP] == 1:
        player.d = 0
        if int(n_map[player.y-1][player.x][2:]) != 9:
            player.y = player.y - 1
    if key[K_DOWN] == 1:
        player.d = 1
        if int(n_map[player.y+1][player.x][2:]) != 9:
            player.y = player.y + 1
    if key[K_LEFT] == 1:
        player.d = 2
        if int(n_map[player.y][player.x-1][2:]) != 9:
            player.x = player.x - 1
    if key[K_RIGHT] == 1:
        player.d = 3
        if int(n_map[player.y][player.x+1][2:]) != 9:
            player.x = player.x + 1
(省略)

def main(screen, clock, font, fontS, use_map, player):
(省略)

        elif idx == 10: # 戦闘 モンスターエリアを返す
            return 10, int(n_map[player.y][player.x][:2])

以下のように変更しています。

bg.blit(imgmap[n_map[dy][dx]], [X, Y])	 # 変更前
bg.blit(imgmap[int(n_map[dy][dx][2:])], [X, Y]) # 変更後

 

先に述べたようにn_mapの後ろの2文字が
マップの画像になるので、[2:]でスライスしています。

他にもいくつか同様の箇所がありますので
同じく変更します。
※同じ変更をする時、VSCodeでは
 こちらのショートカットが便利です。

 

あと、以下のようにn_mapを
使っている箇所(移動の制限)が
ありますので、ここも変更します。

if n_map[player.y-1][player.x] != 9: # 変更前
if int(n_map[player.y-1][player.x][2:]) != 9: # 変更後

 

この移動の制限は画像も変わったことで
おかしくなっているので
別回で修正したいと思います。

 

変更箇所としてはもう一つ、
戻り値をモンスターエリアにしています。
変更前はtmrの0を返していました。(変更前:return 10, 0)

elif idx == 10:
    return 10, int(n_map[player.y][player.x][:2])

※モンスターエリアなので
 n_mapの最初の2文字([:2])です。

one_hour_dungeon.py 経由で
battle.py へモンスターエリアを渡します。

  

battle.py

最後に battle.py です。

まず、モンスターエリアを受け取り
変数mon_areaに格納します。

(省略)
def main(screen, clock, font, fontS, player, area): # areaを追加
(省略)

    mon_typ = "" # モンスターの種類が複数:"まもののむれ"、1種類:モンスターの名前
    mon_area = area # プレイヤーのいる場所のモンスターエリア
(省略)

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

        elif idx == 10: # 戦闘開始
            if tmr == 1:
                mon_typ = init_battle(screen, mon_area) # mon_areaを追加
(省略)

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

 

また、ゲームオーバー(idx9)や
戦闘終了時(idx22)に
戻り値を1つにしています。

one_hour_dungeon.py で

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

から

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

と変更した為です。

 

次に関数init_battleにmon_areaを渡して
出現するモンスターを指定します。

変更前

def init_battle(bg): # 戦闘に入る準備をする
    global monster
    num_enemy = random.randint(1, 4) # 出現モンスターの数
    sum_x = 0
    list_typ = [] # 出現モンスターのNoを格納(A列)
    for _ in range(num_enemy):
        if floor >= 10:
            typ = random.randint(1, 10)
        else:
            typ = random.randint(1, floor+1)
        list_typ.append(typ)

変更前はフロアごとに出現モンスターが
変わっていました。

変更後

def init_battle(bg, mon_area): # 戦闘に入る準備をする
    global monster
    num_enemy = random.randint(1, 4)
    sum_x = 0
    list_typ = [] # 出現モンスターのNoを格納(A列)
    list_mon_area = chara.get_mon_area_list() # モンスター全体のエリアをリストで取得
    # 該当するエリアのモンスターの要素(=モンスターのNo)を格納,mon_areaはプレイヤーのいる場所のエリア
    list_land = [i for i, tmp in enumerate(list_mon_area) if tmp == str(mon_area)]
    for _ in range(num_enemy):
        typ = random.choice(list_land)
        list_typ.append(typ)

 

chara.py で新しく作成した
関数get_mon_area_list()で
モンスターエリアのリストを取得して
リストlist_mon_areaに格納します。
※モンスターシートのL列です。

また、該当するモンスターエリアのモンスターを
リストlist_landに格納します。

この時、内包表記で格納しています。
※内包表記の詳細はこちら

list_mon_areaの中から該当する
モンスターエリアと同じモンスターを
list_landに入れてきます。

 

具体的に・・
モンスターエリア(mon_area)が0の時です。

リストlist_mon_areaは
モンスターシートのL列なので
['エリア', '0', '0', '1', '1', '2', '2', '3', '3', '4', '4']
となります。

このうち、モンスターエリア0と等しいのは
list_mon_area[1]
list_mon_area[2]
の2つなので、リストlist_landは
[1, 2]
となります。

上記の通り、
list_landにはlist_mon_areaのインデックスが
格納されますが、このインデックスの値は
モンスターのNoと同じになります。
※以下の赤枠

 

ちなみにリストlist_mon_areaは
['エリア', '0', '0', '1', '1', '2', '2', '3', '3', '4', '4']
と文字列になっているので
if tmp == str(mon_area)
のようにmon_areaを文字列に型変換しています。
※mon_areaはmove.py から one_hour_dungeon.py へ
 int型で渡しているので、数値です。
 return 10, int(n_map[player.y][player.x][:2])

 

あとはlist_landの中からランダムで選択して、
出現するモンスターのNoを決定します。
※モンスターエリアが0の場合、
 モンスターのNoは1もしくは2の
 どちらかが選択されます。

typ = random.choice(list_land)

※choiceの詳細はこちら

 

これでマップ(エリア)ごとに出現するモンスターを
変えることが出来ました。

 

 

結果

maps.py では
モンスターエリアは以下のようにしています。

なので、それぞれのエリアで出現するのは
以下の同じ色で囲ったモンスターになります。

 

というところで、今回は以上です!(`・ω・´)

 

 

最後に

今回はマップ(エリア)ごとに
出現するモンスターが変わるようにしました。

次回はマップ上で歩けるところ、歩けないところの
制限を修正したいと思います。

まとめサイトへ

 

 

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

 

 

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

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