CODE Python RPG

独学でPythonでRPGを作成する(第52回)仲間を追加(戦闘)

投稿日:

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

前回は仲間を追加しました。

前回の変更はマップを移動中のみであり、
戦闘になるとエラーになってしまうので、
今回は戦闘の変更をします。

目次

  1. 名前
  2. battleの変更
  3. 最後に

名前

前回、名前をスプレッドシートから
取得しているところは変更しなかったので
全員同じ名前になっていました。

これは分かりにくいので
1人1人名前を変えます。

 

one_hour_dungeon.py

(変更前)
player = []
player.append(chara.Brave())
player.append(chara.Brave())
player.append(chara.Brave())
player.append(chara.Brave())

(変更後)
player = []
player.append(chara.Brave("ハロ1"))
player.append(chara.Brave("ハロ2"))
player.append(chara.Brave("ハロ3"))
player.append(chara.Brave("ハロ4"))

chara.py

(変更前)
class Brave(Chara):
    def __init__(self):
        super(Brave, self).__init__()
        self.name = brave_name

(変更後)
class Brave(Chara):
    def __init__(self, name): # 引数にnameを追加
        super(Brave, self).__init__()
        self.name = name

 

オブジェクトを作る時に
名前を指定するようにしました。

それに伴いBraveクラスの
コンストラクタでnameの
引数を追加しました。

print(player[0].name)
# ハロ1
print(player[1].name)
# ハロ2

のようになります。

 

 

battleの変更

今回のメインになるbattle.pyを変更します。

前回と同じように
「player」から「player[0]」への
変更が大量にあります。
※元は変数の「player」が
 リストに変わった為です。

今回もこの変更は最後に回します。

 

 

ステータス表示

最初にステータス表示を変更します。

戦闘画面

 

残りHPによって
色を変えます。

変更前

def draw_battle(bg, fnt, obj, player):
(省略)

    if party == 1: # 一人の時の全体の表示幅
        x_w *= 1
    col = WHITE
    if player.hp < player.maxhp/4:
        col = DANGER
    elif player.hp < player.maxhp/2:
        col = WARNING

変更後

WHITE = (255, 255, 255)
WARNING = (255, 191, 0)
DANGER = (255, 101, 101)
(省略)

def draw_battle(bg, fnt, obj, player):
(省略)

#    if party == 1: # 不要なので削除 len(player)で取得
#        x_w *= 1 # 不要なので削除 len(player)で取得

    col = WHITE
    for p in player:
        if p.hp < p.maxhp/4:
            col = DANGER
            break
        elif p.hp < p.maxhp/2:
            col = WARNING

前回の移動画面と同様です。

MAXHPの1/4未満が1人でもいる:DANGER
MAXHPの1/2未満が1人でもいる:WARNING

 

ステータスの枠です。

変更前

def draw_battle(bg, fnt, obj, player):
(省略)

    # プレイヤーのパラメータ表示
    x_i = int(bg.get_width()/4) # x座標の開始位置
    x_w = int(bg.get_width()/8) # 幅
    y_i = 20 # y座標の開始位置
    y_h = 120 # 高さ
    x_i_t = x_i+x_w/2-50 # テキストのx座標の開始位置
    pygame.draw.rect(bg, col, [x_i, y_i, x_w, y_h], 2, 5)
    pygame.draw.line(bg, col, [x_i, y_i+33], [x_i+x_w-1, y_i+33], 2) # -1がないと少し出る

変更後

def draw_battle(bg, fnt, obj, player):
(省略)

    # プレイヤーのパラメータ表示
    x_i = int(bg.get_width()/4) # x座標の開始位置
    x_b = int(bg.get_width()/8) # 1人分の幅
    # x座標の開始位置 + 横幅 = 0.95にする(右端を0.05空ける)
    if len(player) == 1:
        x_w = x_b # 横幅
    elif len(player) == 2:
        x_w = x_b * 2 # 横幅
    elif len(player) == 3:
        x_w = x_b * 3 # 横幅
    elif len(player) == 4:
        x_w = x_b * 4 # 横幅
    y_i = 20 # y座標の開始位置
    y_h = 120 # 高さ
    x_i_t = 0 # テキストのx座標の開始位置
    y_i_t = y_i + 6 # テキストのy座標の開始位置

    pygame.draw.rect(bg, col, [x_i, y_i, x_w, y_h], 2, 5)
    pygame.draw.line(bg, col, [x_i, y_i+33], [x_i+x_w-1, y_i+33], 2) # -1がないと少し出る

人数によって枠の横幅を
変えています。

    if len(player) == 1:
        x_w = x_b # 横幅
    elif len(player) == 2:
        x_w = x_b * 2 # 横幅
    elif len(player) == 3:
        x_w = x_b * 3 # 横幅
    elif len(player) == 4:
        x_w = x_b * 4 # 横幅

 

枠に続いて、
ステータスの表示です。

変更前

def draw_battle(bg, fnt, obj, player):
(省略)

    # 名前の表示(回復呪文の対象を選択中の時は点滅)
    if idx == 25 and chara.get_spell_target(player.mas_spell[sel_spell_i]) == 0: # 呪文の対象の選択中,呪文の対象が味方なら
        draw_text(bg, "{}".format(player.name), x_i_t, y_i+6, fnt, BLINK[tmr%6])
    else: # 呪文の対象の選択中でなければ
        draw_text(bg, "{}".format(player.name), x_i_t, y_i+6, fnt, col)
    if len(str(player.hp)) == 3:
        draw_text(bg, mojimoji.han_to_zen("H{}".format(player.hp)), x_i_t, y_i+36, fnt, col)
    elif len(str(player.hp)) == 2:
        draw_text(bg, mojimoji.han_to_zen("H {}".format(player.hp)), x_i_t, y_i+36, fnt, col)
    elif len(str(player.hp)) == 1:
        draw_text(bg, mojimoji.han_to_zen("H  {}".format(player.hp)), x_i_t, y_i+36, fnt, col)

    if len(str(player.mp)) == 3:
        draw_text(bg, mojimoji.han_to_zen("M{}".format(player.mp)), x_i_t, y_i+66, fnt, col)
    elif len(str(player.mp)) == 2:
        draw_text(bg, mojimoji.han_to_zen("M {}".format(player.mp)), x_i_t, y_i+66, fnt, col)
    elif len(str(player.mp)) == 1:
        draw_text(bg, mojimoji.han_to_zen("M  {}".format(player.mp)), x_i_t, y_i+66, fnt, col)
    
    if len(str(player.lv)) == 2:
        draw_text(bg, mojimoji.han_to_zen("L:{}".format(player.lv)), x_i_t, y_i+96, fnt, col)
    elif len(str(player.lv)) == 1:
        draw_text(bg, mojimoji.han_to_zen("L: {}".format(player.lv)), x_i_t, y_i+96, fnt, col)

変更後

def draw_battle(bg, fnt, obj, player):
(省略)

    for i, p in enumerate(player):
        x_i_t = x_i + x_b*i + 10 # テキストのx座標の開始位置
        # 名前の表示(回復呪文の対象を選択中の時は点滅)
        if idx == 25 and chara.get_spell_target(p.mas_spell[sel_spell_i]) == 0: # 呪文の対象の選択中,呪文の対象が味方なら
            draw_text(bg, "{}".format(p.name), x_i_t, y_i_t, fnt, BLINK[tmr%6])
        else: # 呪文の対象の選択中でなければ
            draw_text(bg, "{}".format(p.name), x_i_t, y_i_t, fnt, col)

        if len(str(p.hp)) == 3:
            draw_text(bg, mojimoji.han_to_zen("H{}".format(p.hp)), x_i_t, y_i_t+30, fnt, col)
        elif len(str(p.hp)) == 2:
            draw_text(bg, mojimoji.han_to_zen("H {}".format(p.hp)), x_i_t, y_i_t+30, fnt, col)
        elif len(str(p.hp)) == 1:
            draw_text(bg, mojimoji.han_to_zen("H  {}".format(p.hp)), x_i_t, y_i_t+30, fnt, col)

        if len(str(p.mp)) == 3:
            draw_text(bg, mojimoji.han_to_zen("M{}".format(p.mp)), x_i_t, y_i_t+60, fnt, col)
        elif len(str(p.mp)) == 2:
            draw_text(bg, mojimoji.han_to_zen("M {}".format(p.mp)), x_i_t, y_i_t+60, fnt, col)
        elif len(str(p.mp)) == 1:
            draw_text(bg, mojimoji.han_to_zen("M  {}".format(p.mp)), x_i_t, y_i_t+60, fnt, col)
        
        if len(str(p.lv)) == 2:
            draw_text(bg, mojimoji.han_to_zen("L:{}".format(p.lv)), x_i_t, y_i_t+90, fnt, col)
        elif len(str(p.lv)) == 1:
            draw_text(bg, mojimoji.han_to_zen("L: {}".format(p.lv)), x_i_t, y_i_t+90, fnt, col)
for i, p in enumerate(player):
    x_i_t = x_i + x_b*i + 10 # テキストのx座標の開始位置

テキストの開始位置を
1人1人変えます。
※縦の位置は変わりません。

また、オブジェクトは「p」に
入っているので
名前:p.name
HP:p.hp
MP:p.mp
のようになります。

 

 

ステータス表示は以上です。

 

コマンド選択

次にコマンドの選択について
変更します。

選択するのは1人のみでしたが
複数人が選択することになるので
1つグローバル変数を追加します。

今、誰がコマンドを
選択しているのか、です。
※リストplayerの添字になります。

btl_player = 0 # コマンド選択しているキャラ(playerの添字)

 

この変数を使って
変更していきます。

def draw_battle(bg, fnt, obj, player):
(省略)

    if idx == 11 or idx == 19: # コマンド選択 or モンスター選択(攻撃)
        # コマンド表示
        x_i = 50 # x座標の開始位置
        x_w = bg.get_width()*0.4
        y_i = bg.get_height()*0.6 + 40
        y_h = bg.get_height()*0.4 - 40 # 高さ
        if len(player[btl_player].name) <= 2: # 変更前:if len(player.name) <= 2:
            x_i_t = x_i+x_w/2-25 # テキストのx座標の開始位置
        else:
            x_i_t = x_i+x_w/2-50 # テキストのx座標の開始位置
        y_i_t = y_i + 6
        pygame.draw.rect(bg, col, [x_i, y_i, x_w, y_h], 2, 5)
        pygame.draw.line(bg, col, [x_i, y_i+33], [x_i+x_w-1, y_i+33], 2) # -1がないと少し出る
        draw_text(bg, "{}".format(player[btl_player].name), x_i_t, y_i_t, fnt, col) # 変更前:player[btl_player].name

コマンド選択時の名前の位置です。
名前の文字数によって変えています。

コマンドを選択しているキャラの
オブジェクトは「player[btl_player]」に
なります。

 

コマンド選択です。(idx11)

def main(screen, clock, font, fontS, player, area):
    global btl_cmd_x, btl_cmd_y, btl_enemy, btl_player # btl_playerを追加
(省略)

        elif idx == 11: # プレイヤーのターン(入力待ち)
            btl_enemy = 0 # モンスターの選択位置を初期化
            
            player[btl_player].use_defense = False # ぼうぎょを元に戻す(ぼうぎょ効果を消す)
            player[btl_player].check_dfs()

            if not player[btl_player].flag_sleep: # 眠っていないならコマンド選択 眠っているとactは28にセットされている

                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 = 21 # 呪文の選択
                        tmr = 0
                    if COMMAND[btl_cmd_y][btl_cmd_x] == "ぼうぎょ":
                        player[btl_player].act = 18 # get_battle_turnの戻り値idxを18
                        player[btl_player].use_defense = True
                        player[btl_player].check_dfs() # 選択した時点で防御力2倍(ターンが回ってきてからではない)
                        idx = 23
                        tmr = 0
                    if COMMAND[btl_cmd_y][btl_cmd_x] == "にげる":
                        idx = 14
                        tmr = 0

変更点です。

player[btl_player].use_defense = False # ぼうぎょを元に戻す(ぼうぎょ効果を消す)
player[btl_player].check_dfs()

ぼうぎょについて
コマンド選択する
オブジェクト(player[btl_player])を
確認しています。

player[btl_player].act = 18 # get_battle_turnの戻り値idxを18
player[btl_player].use_defense = True
player[btl_player].check_dfs() # 選択した時点で防御力2倍(ターンが回ってきてからではない)

防御を選択した時の
オブジェクトを「player[btl_player]」
にします。

 

あと、眠っている時の
確認をidx11に持ってきました。

1人1人確認するには
ここのほうが都合が良かった為です。

if not player[btl_player].flag_sleep: # 眠っていないならコマンド選択 眠っているとactは28にセットされている

眠っている場合は
コマンド選択を飛ばします。

今まではidx27の
最後に確認していましたので
これを削除します。

        elif idx == 27: # 呪文効果の確認
            check_message = check_spell_effect(player) # 関数がないと全ての呪文効果ごとに↓を記載する必要がある
            for mes in check_message:
                set_message(mes)
                draw_battle(screen, fontS, turn_obj, player)
                pygame.display.update() # if turn_obj is None: にしてidxを11にするとここに来ない
                pygame.time.delay(delay_speed)
            init_message()

            # if player.flag_sleep: # 削除
            #     idx = 23 # ターンセット(眠っている時はコマンド選択できない) # 削除
            # else: # 削除
            #     idx = 11 # 削除

 

攻撃対象の選択です。

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

ここも「player[btl_player]」に
変更しています。

 

        elif idx == 23: # ターンセット
            if btl_player == len(player)-1: # 全員のコマンド選択完了
                set_battle_turn(btl_start, player)
                for mon in monster:
                    mon.set_monster_com()
                btl_start = 0 # 戦闘は通常に戻す
                idx = 24
            else: # 選択していなプレイヤーがいる(眠っているときはactが28がセットされている)
                idx = 11
                btl_player += 1

最後のキャラのコマンド選択が
終わったら、攻撃開始となります。

まだ、コマンド選択が
終わっていないキャラがいる時は
次のキャラのコマンド選択となります。

 

選択済みのキャラの
コマンドキャンセルです。

def battle_command(bg, key): # コマンドの入力と表示
    global btl_cmd_x, btl_cmd_y, btl_player # btl_player を追加 

(省略)
    elif key[K_b]: # キャンセル
        if btl_player > 0:
            btl_player -= 1

コマンド選択時に
キャンセル(「b」キーを押下)すると
前のキャラに戻します。

 

「btl_player」関連の変更は以上です。

 

その他

いくつか細かいところの変更です。

 

素早さについて、
全員分を確認をします。
※素早さの高い順に行動します。

def set_battle_turn(num, player): # 0:通常、1:先制攻撃、2:不意打ち
(省略)

    # 素早さの順に並べる(戦闘順)
    if num == 0 or num == 1:
        for p in player:
            r = random.randint(66, 100)
            tmp_order[p] = int(p.quick * r/100)

 

モンスターと遭遇した時に
不意打ちの場合の
メッセージを人数によって
少し変えます。

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

elif idx == 10: # 戦闘開始
(省略)

                    else:
                        set_message(mon_typ + "は")
                        if len(player) == 1:
                            set_message(player[0].name + "が みがまえるまえに")
                        else:
                            set_message(player[0].name + "たちが みがまえるまえに")
                        set_message("おそいかかってきた!")

 

キャラの攻撃です。

        elif idx == 12: # プレイヤーの攻撃
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                set_message(turn_obj.name + " のこうげき!") # playerから変更

「player」から「turn_obj」に
変更しました。

ここはもともと「turn_obj」が
正でした。。
※turn_obj:行動中のオブジェクト

 

にげる、全滅時のメッセージです。

        elif idx == 14: # 逃げられる?
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                if len(player) == 1:
                    set_message(player[0].name + "は にげだした!")
                else:
                    set_message(player[0].name + "たちは にげだした!")

        elif idx == 15: # 敗北
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                pygame.mixer.music.stop()
                if len(player) == 1:
                    set_message(player[0].name + " は気絶した...")
                else:
                    set_message(player[0].name + "たち は全滅した...")

戦闘からにげる時や
全滅した時のメッセージも
人数によって変更しました。

 

戦闘が終わった時の呪文効果について
全員リセットに変更します。

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

 

このセクションの最後になります。
戦闘に勝利した時の経験値です。

import math
(省略)

        elif idx == 16: # 勝利
            draw_battle(screen, fontS, turn_obj, player)
            if tmr == 1:
                if len(dead_monster) != 1: # 1匹の時はいらない
                    set_message(mon_typ + " をやっつけた!")
                btl_exp = math.ceil(btl_exp/len(player)) # 切り上げ
                for p in player:
                    p.exp += btl_exp
            if tmr == 5:
                if len(player) == 1:
                    set_message(str(btl_exp) + "ポイントの けいけんちを かくとく!")
                else:
                    set_message("それぞれ" + str(btl_exp) + "ポイントの けいけんちを かくとく!")
btl_exp = math.ceil(btl_exp/len(player))

獲得した経験値は仲間同士で
等分します。

ゲーム内で少数は扱わないので
整数にしますが、ceilで切り上げました。
※ceilの詳細はこちら
※mathのimportが必要です。

intでも整数になりますが、
intは切り捨てになります。

4人の時に獲得経験値が4未満だと
0になってしまうなどの理由で
切り上げにしました。

import math
btl_exp1 = int(3/4)
btl_exp2 = math.ceil(3/4)
print(f"btl_exp1:{btl_exp1}, btl_exp2:{btl_exp2}")

# btl_exp1:0, btl_exp2:1

上記のように
int(3/4):0
math.ceil(3/4):1
になります。

 

「その他」については
以上です。

 

playerをリストに

今回も前回同様、最後に
変数だった「player」を
「player[0]」に変更します。

ただ、今回の変更はとりあえずです。。
「player」もおかしいですが、
「player[0]」でもない箇所が
いくつもあります。

ので、動作としてはおかしかったり、
止まってしまったりします。

というわけで、ご参考程度に・・
battle.pyを添付します。

 

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

 

 

最後に

今回は仲間追加に伴い
battle.pyを変更しました。

ただ、残した修正点が多いです。
※気付いているバグだけでもたくさんあります。
 気付いていないのも含めると・・。

とうわけで、次回も戦闘(battle.py)です。

まとめサイトへ

 

 

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

 

 

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

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