CODE Python RPG

独学でPythonでRPGを作成する(第16回)素早さを追加

更新日:

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

前回は出現したモンスターが1匹につき1回ずつ攻撃するように変更しました。

前回の行動順は攻撃力の高い順にしたのですが、今回は素早さのパラメータを追加して、
行動順を素早さの高い順に変更します。
また、モンスターが攻撃する時、全員が一緒に動作してしまうので
攻撃したモンスターだけが動作するように変更します。

 

目次

  1. 素早さを追加
  2. 攻撃の行動順
  3. 先制攻撃・不意打ち
  4. モンスターの攻撃
  5. 最後に

素早さを追加

プレイヤーのパラメータに素早さを追加します。
あわせてスプレッドシートの形式も変えました。

変更前

変更後

3行目からレベルごとのパラメータも追加しましたが
この形式で使うかは未定です・・(∀`*ゞ)
とりあえず、今回はレベル1の値だけ使います。

また、HP(F列)、MP(H列)は使いません。
なんとなく入れているだけです。

続いて、モンスターのパラメータにも素早さを追加します。

変更前

変更後

こちらもHP(E列)、MP(G列)は使いません。
最大MP(F列)も使いませんが、そのうち使うと思います。
たぶん・・

 

スプレッドシートを変えたので、chara.pyも変えます。

変更前

chara1_name = ws_chara.acell('B2').value
chara1_lv = ws_chara.acell('B3').value
chara1_maxhp = ws_chara.acell('B4').value
chara1_hp = ws_chara.acell('B5').value
chara1_maxmp = ws_chara.acell('B6').value
chara1_mp = ws_chara.acell('B7').value
chara1_atk = ws_chara.acell('B8').value
chara1_dfs = ws_chara.acell('B9').value

mon_name = ws_mon.col_values(2) # B列
mon_maxhp = ws_mon.col_values(4) # D列
mon_atk = ws_mon.col_values(8) # H列
mon_dfs = ws_mon.col_values(9) # H列

変更後

# 2行目はレベル1の値
chara1_name = ws_chara.acell('C2').value
chara1_lv = ws_chara.acell('D2').value
chara1_maxhp = ws_chara.acell('E2').value
chara1_hp = ws_chara.acell('F2').value
chara1_maxmp = ws_chara.acell('G2').value
chara1_mp = ws_chara.acell('H2').value
chara1_quick = ws_chara.acell('I2').value # 追加(素早さ)
chara1_atk = ws_chara.acell('J2').value
chara1_dfs = ws_chara.acell('K2').value

mon_name = ws_mon.col_values(2) # B列
mon_maxhp = ws_mon.col_values(4) # D列
mon_quick = ws_mon.col_values(8) # H列 # 追加(素早さ)
mon_atk = ws_mon.col_values(9) # I列
mon_dfs = ws_mon.col_values(10) # J列

class Brave():
    def __init__(self):
(省略)
        self.quick = int(chara1_quick) # 追加(素早さ)
(省略)

class Monster():
    def __init__(self, num):
(省略)
        self.quick = int(mon_quick[num]) # 追加(素早さ)
(省略)

これでプレイヤー・モンスター共に
素早さのパラメータが追加されました。

 

 

攻撃の行動順

前回は攻撃の順番は攻撃力の高い順にしましたが
素早さの高い順に変更します。

変更前

def set_battle_turn():
    global battle_order
    tmp_order = {}
    tmp_order[player] = player.atk # 変更前は攻撃力を利用
    for mon in monster:
        tmp_order[mon] = mon.atk # 変更前は攻撃力を利用
    battle_order = sorted(tmp_order.items(), key=lambda x:x[1], reverse=True)

変更後

def set_battle_turn():
    global battle_order
    tmp_order = {}
    r = random.randint(66, 100)
    tmp_order[player] = int(player.quick * r/100) # 変更後は素早さを利用
    for mon in monster:
        r = random.randint(66, 100)
        tmp_order[mon] = int(mon.quick * r/100) # 変更後は素早さを利用
    # [(obj, quick), (ojb, quick), ...]のリストになる
    battle_order = sorted(tmp_order.items(), key=lambda x:x[1], reverse=True) # 素早さでソート

変更後は素早さを降順でソートして、
素早さが高い順に攻撃するようにしました。

ただ、単純に素早さを比べると毎回行動順が同じになってしまいます。
それでも良いですけど、ターンの度に行動順を変えたいので、
素早さに66≦r≦100のランダムな数字をかけました。

以下、例です。
プレイヤーも含め全員素早さが10の時の行動順です。

数字は素早さです。
素早さに6割〜10割(66≦r≦100)をかけているので
素早さが10だと6〜10になります。

1ターン目
[(<chara.Brave object at xxx>, 9),
(<chara.Monster object at xxx>, 9),
(<chara.Monster object at xxx>, 8),
(<chara.Monster object at xxx>, 8),
(<chara.Monster object at xxx>, 6)]

2ターン目
[(<chara.Monster object at xxx>, 9),
(<chara.Monster object at xxx>, 8),
(<chara.Brave object at xxx>, 7),
(<chara.Monster object at xxx>, 7),
(<chara.Monster object at xxx>, 7)]

3ターン目
[(<chara.Monster object at xxx>, 10),
(<chara.Brave object at xxx>, 9),
(<chara.Monster object at xxx>, 8),
(<chara.Monster object at xxx>, 8),
(<chara.Monster object at xxx>, 7)]

chara.Braveの行動順がターンごとに変わりました!

 

 

先制攻撃・不意打ち

ついでにset_battle_turn関数をもう少し変えます。
通常の攻撃順、先制攻撃の攻撃順、不意打ちの攻撃順
を追加します。

引数を受け取って、その数字によって変えます。
通常:battle_orderにプレイヤー・モンスターを加える
先制:battle_orderにプレイヤーのみ加える
不意打ち:battle_orderにモンスターのみ加える

def set_battle_turn(num): # 引数numを追加 0:通常、1:先制攻撃、2:不意打ち
    global battle_order
    tmp_order = {}
    if num == 0 or num == 1: # 通常と先制はプレイヤーを追加
        r = random.randint(66, 100)
        tmp_order[player] = int(player.quick * r/100)
    if num == 0 or num == 2: # 通常と不意打ちはモンスターを追加
        for mon in monster:
            r = random.randint(66, 100)
            tmp_order[mon] = int(mon.quick * r/100)
    # [(obj, quick), (ojb, quick), ...]のリストになる
    battle_order = sorted(tmp_order.items(), key=lambda x:x[1], reverse=True)
    print(battle_order)

 

引数が必要になったので、呼び出す側も変えます。

変更前

def main():
(省略)
        elif idx == 19: # 敵の選択
            draw_battle(screen, fontS)
            if battle_select(screen, key) == True:
                idx = 23
                tmr = 0
        elif idx == 23: # ターンセット
            set_battle_turn()
            idx = 24

変更後

def main():
(省略)
        elif idx == 19: # 敵の選択
            draw_battle(screen, fontS, turn_obj)
            if battle_select(screen, key) == True:
                set_battle_turn(0)
                idx = 24
                tmr = 0

idx19は通常の攻撃順なのでnum=0です。
idx19の時にset_battle_turn(0)を呼び出します。

idx23はあまり意味がなかったので消して、
idx19で呼び出すようにしました。

 

他にコマンド「にげる」を選択した後、
逃げられなかった時に関数set_battle_turnを追加しました。

def main():
(省略)
        elif idx == 14: # 逃げられる?
            draw_battle(screen, fontS, turn_obj)
            if tmr == 1: set_message(player.name + " は逃げ出した!")
            if tmr == 5:
                if random.randint(0, 99) < 60:
                    idx = 22 # 戦闘終了
                else:
                    set_message("しかし、まわりこまれてしまった!")
            if tmr == 10:
                init_message()
                set_battle_turn(2) # 追加
                idx = 24 # ターンの確認
                tmr = 0

set_battle_turn(2)を追加しました。
num=2は不意打ちです。
(不意打ちとは違いますが)逃げられなかった時は1ターンの間、
モンスターに一方的に攻撃されるようにしました。

戦闘が始まった時に一定の確率で先制攻撃やモンスターの不意打ちが
発生するようにしようと思っていますが、それはまた今度にします。

 

 

モンスターの攻撃

もう1箇所変えます。
今はモンスターが攻撃する時は全員一緒に動作してしまうので
行動が回ってきたモンスターのみ動作するように変えます。

攻撃時のモンスターの動作を表示しているのはdraw_battle関数です。
今は生存しているかどうかしか判定していないので、
生存しているモンスターだったら全員動作します。

def draw_battle(bg, fnt): # 戦闘画面の描画
(省略)
        if display_flg: # 生存しているなら
            bg.blit(mon.img, [monster[i].x, monster[i].y+emy_step])

 

なので、行動が回ってきたかどうかも判定に加えます。
その為に、引数に行動が回ってきたオブジェクトを追加します。

def draw_battle(bg, fnt, obj): # 行動が回ってきたobjを追加
(省略)
        if display_flg:
            if monster[i] is obj: # 行動が回ってきたモンスターなら動作する
                bg.blit(mon.img, [monster[i].x, monster[i].y+emy_step])
            else: # 順番ではないモンスターなら動作しない
                bg.blit(mon.img, [monster[i].x, monster[i].y])

前回もありましたが、オブジェクトの比較です。
今回も値ではなく、オブジェクトが一致しているかを
判定したい為、「==」ではなく「is」を利用しました。
「==」はオブジェクトの値を比較
「is」はオブジェクトを比較
(詳細はこちら

そして引数が変わったので、呼び出す側も変更します。
行動が回ってきたオブジェクトは
前回追加したturn_objです。

def main(): # メイン処理
(省略)
            elif tmr == 5:
                set_message(monster[0].name + " が現れた!")
                draw_battle(screen, fontS, turn_obj) # turn_objを追加
            elif tmr == 6:
                if len(monster) >= 2:
                    set_message(monster[1].name + " が現れた!")
                    draw_battle(screen, fontS, turn_obj) # turn_objを追加
                    time.sleep(0.3)
            elif tmr == 7:
                if len(monster) >= 3:
                    set_message(monster[2].name + " が現れた!")
                    draw_battle(screen, fontS, turn_obj) # turn_objを追加
                    time.sleep(0.3)
            elif tmr == 8:
                if len(monster) == 4:
                    set_message(monster[3].name + " が現れた!")
                    draw_battle(screen, fontS, turn_obj) # turn_objを追加
                    time.sleep(0.3)

追加箇所は他にもたくさんありますが、省かせて頂きます・・(∀`*ゞ)
※ちなみに、エディタにVSCodeを使っている場合、
 たくさんある箇所に同じ変更を加える時は
 こちらのショートカットが便利です。

これで行動が回ってきたモンスターのみ攻撃の動作をするようになりました。
変更前と変更後の動画をご参考までにこちらのページに載せました。

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

 

 

最後に

今回はパラメータに素早さを追加して、
行動順を素早さの高い順に変更しました。

あと、攻撃したモンスターだけが動作するようにも変更しました。

次回は、え〜・・う〜ん・・未定です。。。

まとめサイトへ

 

 

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

 

 

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

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