こんにちは。
「Pythonでつくる ゲーム開発 入門講座」の
Chapter11,12(本格RPGを作ろう!全編・後編)を基にRPGを作っていきます。
前回は補助系の呪文のラリホー・ラリホーマを追加しました。
ただ、やり残しがあるので今回はそれを追加します。
もう一つ、今まで戦闘時のモンスターの行動は攻撃のみでしたが、他の行動もできるようにします。
目次
モンスターの行動(1)
前回、ラリホー・ラリホーマで
相手を眠り状態にしました。
ただ、眠り状態でも攻撃できて
しまうので、
戦闘中に眠っている場合は
攻撃しないようにします。
攻撃しないようにしますが、
今まで戦闘中のモンスターの行動は
攻撃のみでした。
def get_battle_turn(player):
(省略)
if turn_key is player:
return turn_key, player.act
else:
return turn_key, 13
if turn_key is player:
return turn_key, player.act
プレイヤーの行動は
「攻撃」や「呪文」などによって
actに入る数字が変わります。
※攻撃:12、呪文:26
else:
return turn_key, 13
ただ、モンスターの場合は
全て13にしていました。
※モンスターの攻撃:13
なので、モンスターにも
変数actを追加します。
charaの変更
プレイヤーと同様にモンスターも
変数actを利用することになるので、
Charaクラスに変数actを追加して、
Braveクラスから変数actを削除します。
class Chara():
def __init__(self):
self.act = 0 # 戦闘のコマンド(行動)12:プレイヤー攻撃、13:モンスター攻撃、18:防御
(省略)
class Brave(Chara):
def __init__(self):
(省略)
# self.act = 0 # 削除
ラリホー・ラリホーマが効いた場合
actを28にします。
※後程、眠り状態の場合の
行動をidx28で設定します。
class Chara():
(省略)
def rariho(self, target): # ラリホー
if random.randint(0, 99) < spell_up_dict["ラリホー"]: # 呪文の上限値が成功率
target.act = 28 # 眠っている行動
(省略)
def rarihoma(self, target): # ラリホーマ
if random.randint(0, 99) < spell_up_dict["ラリホーマ"]: # 呪文の上限値が成功率
target.act = 28 # 眠っている行動
(省略)
target.act = 28
を追加しました。
次に、モンスターの行動を決める
set_monster_comメソッドを
追加します。
class Monster(Chara):
(省略)
def attack(self, obj):
(省略)
def set_monster_com(self): # モンスターの戦闘コマンド
if self.flag_sleep:
self.act = 28 # 眠っている(無くても良い)
else:
self.act = 13 # 攻撃
if self.flag_sleep:
self.act = 28 # 眠っている(無くても良い)
眠り状態の場合は
actを28にします。
※眠り状態になった時に
actを28にしているので
ここで改めて28にしなくても
問題ないはず、たぶん(^_^;)
else:
self.act = 13 # 攻撃
眠り状態でない(起きている)場合は
actを13にして、通常攻撃します。
chara.pyの変更は以上です。
battleの変更
battle.pyを変更します。
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 23: # ターンセット
set_battle_turn(btl_start, player)
for mon in monster: # 追加
mon.set_monster_com() # 追加
btl_start = 0 # 戦闘は通常に戻す
idx = 24
for mon in monster:
mon.set_monster_com()
の2行を追加しました。
生存モンスターの行動を
set_monster_comメソッドで
確認します。
※変数actを指定します。
そして、get_battle_turn関数の
戻り値を変更します。
変更前
def get_battle_turn(player):
(省略)
if turn_key is player:
return turn_key, player.act
else:
return turn_key, 13
(省略)
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 24: # ターン確認
tmr = 0
turn_obj, idx = get_battle_turn(player)
変更後
def get_battle_turn():
(省略)
return turn_key, turn_key.act
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 24: # ターン確認
tmr = 0
turn_obj, idx = get_battle_turn()
return turn_key, turn_key.act
今までモンスターの行動は
全て13(攻撃)でしたが
プレイヤーもモンスターも
変数actを返します。
※引数のplayerも不要になります。
次に、眠り状態の時の
行動を設定します。
actが28の時、眠り状態なので
idx28に設定します。
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 28: # 眠り状態
draw_battle(screen, fontS, turn_obj, player)
if tmr == 1:
if turn_obj.flag_sleep: # 眠っている
set_message(turn_obj.name + "は ねむっている")
else: # ラリホーは効いたけど、攻撃などで起きた
idx = 24 # 何も表示させない
if tmr == 6:
init_message()
idx = 24 # ターン確認へ
if turn_obj.flag_sleep: # 眠っている
set_message(turn_obj.name + "は ねむっている")
眠り状態の場合、
何もせずメッセージを表示します。
else: # ラリホーは効いたけど、攻撃などで起きた
idx = 24 # 何も表示させない
前回、設定しましたが、
攻撃で目を覚ますことがあります。
目を覚ました時に眠り状態は
invalid_sleepメソッドで解除しますが、
行動(act)は28のままなので、
ここで設定します。
※何もせず、次のキャラの行動に移ります。
これで眠り状態のモンスターは
攻撃しなくなりました。
眠り状態の残りの設定
眠り状態の時の設定を
もう少し追加しておきます。
(現状では)
モンスターがラリホーなどで
プレイヤーを眠り状態に
することはありませんが、
プレイヤーが
眠り状態になった時のことを
追加しておきます。
まず、プレイヤーが眠り状態の時に
攻撃されたら目を覚ますことが
あるようにしておきます。
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 13: # 敵のターン、敵の攻撃
(省略)
if tmr == 15:
player.hp = player.hp - dmg
if player.hp <= 0:
player.hp = 0
idx = 15 # 敗北
tmr = 0
else:
if dmg != 0 and player.flag_sleep: # 攻撃を受けた かつ 生きている かつ 眠っている
if random.randint(0, 99) < 55:
player.invalid_sleep()
set_message(player.name + " はめをさました!")
(省略)
前回のモンスターの時とほぼ同じです。
あと、眠り状態の時は
コマンド選択できないようにします。
変更前
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 27: # 呪文効果の確認
(省略)
init_message()
idx = 11
変更後
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 27: # 呪文効果の確認
(省略)
init_message()
if player.flag_sleep:
idx = 23 # ターンセット(眠っている時はコマンド選択できない)
else:
idx = 11
眠り状態の場合は
idx11のコマンド選択には
行かないようにします。
もう一つ、眠り状態の時に
相手の攻撃をかわさないようにします。
chara.pyです。
class Brave(Chara):
(省略)
def attack(self, obj):
(初略)
if not obj.flag_sleep: # 眠っていないなら(眠っていたらかわさない)
if obj.quick <= 400:
avoid = 1/64 + obj.quick//80 * (1/192)
elif obj.quick <= 500:
avoid = 1/64 + obj.quick//80 * (1/192) + (obj.quick-400)//25 * (1/32)
else:
avoid = 1/6
if random.random() < avoid:
dmg = 0
if random.randint(0, 1) == 0:
message.append(obj.name + "は ひらりと みをかわした!")
else:
message.append(obj.name + "は すばやく みをかわした!")
return dmg, message
(省略)
class Monster(Chara):
(省略)
def attack(self, obj):
(省略)
if not obj.flag_sleep: # 眠っていないなら(眠っていたらかわさない)
if obj.quick <= 400:
(省略)
if not obj.flag_sleep: # 眠っていないなら(眠っていたらかわさない)
attackメソッドに1行追加しただけです。
眠り状態については以上です。
モンスターの行動(2)
戦闘時のモンスターの行動について
今回で眠り状態を追加しましたが、
今までは基本的に攻撃のみでした。
が、攻撃以外の行動もできるようにします。
最初に「モンスター」シートの
M列に特技を追加します。

特技 = 戦闘中の行動とします。
複数の行動がある場合は
「,」で区切ります。
Green slimeの行動は「攻撃」と「その他」、
Red slimeの行動は「攻撃」のみ
です。
※「その他」は今回用に適当に作ったもので、
今後変えると思います。
charaの変更
作った特技をchara.pyで読み込みます。
(省略)
mon_area = ws_mon.col_values(12) # L列 エリア
mon_skill = ws_mon.col_values(13) # M列 特技 ←追加
(省略)
class Monster(Chara):
def __init__(self, num):
(省略)
self.skill = mon_skill[num].split(",")
M列をリストmon_skillに入れて
それをsplitを使って「,」で分割します。
※splitの詳細はこちら。
Green slimeは['攻撃', 'その他']、
Red slimeは['攻撃']
となります。
これを今回で追加した
set_monster_comメソッドで
使います。
class Monster(Chara):
(省略)
def set_monster_com(self): # モンスターの戦闘コマンド
if self.flag_sleep:
self.act = 28 # 眠っている(無くても良い)
else:
skill_no = random.randint(0, len(self.skill)-1)
if self.skill[skill_no] == "攻撃":
self.act = 13 # 攻撃
else:
self.act = 29 # 特技
skill_no = random.randint(0, len(self.skill)-1)
分割したskillをランダムで選択します。
if self.skill[skill_no] == "攻撃":
self.act = 13 # 攻撃
else:
self.act = 29 # 特技
skillが「攻撃」であれば13、
skillが「その他」であれば29
にします。
13はモンスターの攻撃(idx13)
29はこの後、新しく追加します。
charaの変更は以上です。
battleの変更
特技の行動をするidx29を追加します。
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 29:
draw_battle(screen, fontS, turn_obj, player)
if tmr == 1:
dmg_eff = 0
set_message(turn_obj.name + "は どうしていいのか わからない!")
if tmr == 6:
init_message()
idx = 24
今回、追加した特技で「攻撃」の他は
「その他」のみです。
「その他」の行動は
何もしないことにしています。
※こちらを参考にさせて頂きました。
変数dmg_effはモンスターの攻撃時に
画面を揺らす為に使います。
今回は画面を揺らさないので
0にしています。
とりあえず、これでモンスターも
「攻撃」以外の行動ができるようになりました。
こんな感じです。

ちなみにモンスターも攻撃以外の
行動をとれるようにしたいことは
19回目で記載していました。
長かったです。。(^_^;)
battleの変更も以上です。
一定時間停止
ここから話が変わって
時間停止についてです。
今まで戦闘メッセージで
少しだけ時間を停止する時は
time.sleepを使っていました。
これを
pygame.time.delay
に変更します。
※詳細はこちら。
特に理由は無く、何となくです(^_^;)
(省略)
delay_speed = 500 # 止める時間(ms)
escape_rate = 100 # 逃げる確率(100は100%逃げる)
delay_speedに止める時間を
入れておきます。
※大文字にすべきかもしれません。
あとは、time.sleep を pygame.time.delay に
変更します。
time.sleepは使っている場所によって
止める時間を変えていましたが
変更後は全てdelay_speedを使います。
変更前
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 10: # 戦闘開始
(省略)
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)
変更後
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 10: # 戦闘開始
(省略)
elif tmr == 6:
if len(monster) >= 2:
set_message(monster[1].name + " が現れた!")
draw_battle(screen, fontS, turn_obj, player)
pygame.time.delay(delay_speed)
elif tmr == 7:
if len(monster) >= 3:
set_message(monster[2].name + " が現れた!")
draw_battle(screen, fontS, turn_obj, player)
pygame.time.delay(delay_speed)
elif tmr == 8:
if len(monster) == 4:
set_message(monster[3].name + " が現れた!")
draw_battle(screen, fontS, turn_obj, player)
pygame.time.delay(delay_speed)
その他の time.sleepも
すべて変更しました。
バグ修正
最後にバグ修正をします。
グループ攻撃の呪文で
攻撃対象のモンスターを選択します。
この時、以下のように赤枠を
選択(点滅)しているところから
さらに右に行くとエラーになってしまいます。

これは右に行く判定で
使う変数を誤っていました。
def spell_target_select(bg, key, player): # 呪文の対象を選択
(選択中)
elif chara.get_spell_target(player.mas_spell[sel_spell_i]) == 3: # 呪文の対象がモンスターグループ
dic_typ = collections.Counter(monster)
if key[K_LEFT] and spell_target_i > 0: # ←キー
spell_target_i -= 1
elif key[K_RIGHT] and spell_target_i < len(dic_typ)-1: # →キー
spell_target_i += 1
elif key[K_RIGHT] and spell_target_i < len(dic_typ)-1: # →キー
右に行く判定でlen(dic_typ)を
使っていますが、上記画像では
値が4になります。
※モンスターのオブジェクトは
1つずつ違うので重複とはならない為
でも、攻撃対象がグループの呪文の場合
モンスターグループを選択します。
なのに、上記画像ではグループは2ですが
3以上を選択できてしまうので
エラーになってしまいます。
というわけで、以下に変更します。
def spell_target_select(bg, key, player): # 呪文の対象を選択
(省略)
elif chara.get_spell_target(player.mas_spell[sel_spell_i]) == 3: # 呪文の対象がモンスターグループ
list_typ = mon_overlap()
if key[K_LEFT] and spell_target_i > 0: # ←キー
spell_target_i -= 1
elif key[K_RIGHT] and spell_target_i < len(list_typ)-1: # →キー
spell_target_i += 1
mon_overlap関数です。
def mon_overlap():
# 呪文の対象のモンスターNoを取得(list_typに格納する)
mon_no = [] # モンスターNoを格納
for mon in monster:
mon_no.append(mon.num)
dic_typ = collections.Counter(mon_no) # 重複を抽出 例{9:1、1:1}
list_typ = list(dic_typ.keys()) # キーをリストで取得(キーがモンスターNo)list_typ[0]=9,list_typ[1]=1
return list_typ
これでlen(list_typ)の値は
2になるので、
先程の画像で右を押しても、
これ以上は右に行けないので
エラーになりません。
という感じで、今回は以上です!(`・ω・´)
最後に
今回は眠り状態で前回残していた設定を追加しました。
また、戦闘時のモンスターが攻撃以外の行動をとることができるようにしました。
次回はモンスターの行動をもう少し追加しようと思います。
【ご注意】
プログラムやデータなどは著作権法により保護されています。
著作者の許諾を得ずに、プログラムおよびデータそのものまたは改変したものを
配布したり販売したりすることはできません。
また、これらを利用して発生した損害などに関して、著作者は一切責任を負いません。