こんにちは。
「Pythonでつくる ゲーム開発 入門講座」の
Chapter11,12(本格RPGを作ろう!全編・後編)を基にRPGを作っていきます。
前回は補助系の呪文のヘナトスを追加しました。
今回も同じく補助系の呪文マヌーサを追加します。
あと会心の一撃や痛恨の一撃も追加します。
目次
呪文シート
「マヌーサ」は
相手を幻でつつみこみます。
※攻撃の命中率を下げます。

以下のページを参考にさせて頂きました。
呪文の効果はこちら。
呪文の説明はこちら。
説明のページでは
「敵全体」になっていますが、
効果のページの説明にあるように
「敵1グループ」(3)にしました。
また、マヌーサは相手に使う為
成功率を設けます。
成功率を75%(上限値を75)にします。
※下限は使いません。
※補助系の場合、上限・下限は
成功率に使います。
シートは以上です。
charaの変更
最初に変数を追加します。
有効ターン数を格納する変数です。
回数を格納する変数は用意しません。
※何回、幻につつまれても
命中率は変わりません。
class Chara():
def __init__(self):
self.x = 0 # プレイヤーのx座標, モンスター画像の表示位置
self.y = 0 # プレイヤーのy座標, モンスター画像の表示位置
self.use_defense = False # 防御を使っているか
self.turn_sukara = 0 # スカラの有効ターン
self.num_sukara = 0 # スカラを使った回数
self.turn_rukani = 0 # ルカニの有効ターン
self.num_rukani = 0 # ルカニが効いた回数
self.turn_baikiruto = 0 # バイキルトの有効ターン
self.turn_henatos = 0 # ヘナトスの有効ターン
self.turn_manusa = 0 # マヌーサの有効ターン
self.flag_manusa = False # マヌーサにかかっているか
マヌーサにかかっているかどうかは、
flag_manusa
を用意しなくても
turn_manusa
が0かどうかで判断できそうですが
何となく用意しました。
マヌーサ
マヌーサのメソッドを追加します。
class Chara():
(省略)
def manusa(self, target): # マヌーサ
if target.flag_manusa:
target.turn_manusa = random.randint(4, 5) # 効果の残りターン
return 1 # 既にマヌーサにかかっている
else:
if random.randint(0, 99) < spell_up_dict["マヌーサ"]: # 呪文の上限値が成功率
target.flag_manusa = True
target.turn_manusa = random.randint(4, 5) # 効果の残りターン
return 2
else: # 効かなかった
return -1
def invalid_manusa(self):
self.turn_manusa = 0
self.flag_manusa = False
既にマヌーサにかかっている時
有効ターンのみ再度確認します。
※繰り返しになりますが
何回、幻につつまれても
命中率は変わりません
if target.flag_manusa:
target.turn_manusa = random.randint(4, 5) # 効果の残りターン
return 1 # 既にマヌーサにかかっている
マヌーサにかかっていない時
成功率を下回っていると成功にしています。
※呪文シートの上限が75のため、
75%成功する、25%失敗する。
else:
if random.randint(0, 99) < spell_up_dict["マヌーサ"]: # 呪文の上限値が成功率
target.flag_manusa = True
target.turn_manusa = random.randint(4, 5) # 効果の残りターン
return 2
else: # 効かなかった
return -1
最後のinvalid_manusaメソッドで
マヌーサの効果を無効にします。
spell_resetメソッドで呼び出します。
def spell_reset(self):
(省略)
self.invalid_manusa() # マヌーサの効果を消す
あと、前回のヘトナスについて
今回のマヌーサと同じように
失敗した時に-1を返すようにします。
あわせて、失敗した時を
elseにしました。
※特に理由はなく、何となくです。
変更前
def henatos(self, target): # ヘナトス
if random.randint(0, 99) > spell_up_dict["ヘナトス"]: # 呪文の上限値が成功率
return 1 # 効かなかった
tmp_atk = target.atk # 現在の攻撃力
if target.atk >= target.get_atk(): # 元の攻撃力以上なら
target.atk -= int(0.5 * target.get_atk()) # 元の攻撃力の半分をひく
# 既に元の攻撃力より小さいなら変えない(どんなに小さくても半分まで)
tmp_atk = target.atk - tmp_atk
target.turn_henatos = random.randint(4, 5) # 効果の残りターン
return tmp_atk # 変更後から変更前を引いた値
変更後
def henatos(self, target): # ヘナトス
if random.randint(0, 99) < spell_up_dict["ヘナトス"]: # 呪文の上限値が成功率
tmp_atk = target.atk # 現在の攻撃力
if target.atk >= target.get_atk(): # 元の攻撃力以上なら下げる、既に元の攻撃力より小さいなら変えない(どんなに小さくても半分まで)
target.atk -= int(0.5 * target.get_atk()) # 元の攻撃力の半分をひく
tmp_atk = tmp_atk - target.atk # 少なくなるので、元の値からひく
target.turn_henatos = random.randint(4, 5) # 効果の残りターン
return tmp_atk
else: # 効かなかった
return -1
呪文を使う
呪文を使う時のメソッドuse_spellを
変更します。
class Chara():
(省略)
def use_spell(self, str_spell, target, spell_target_no):
(省略)
elif str_spell == "ヘナトス":
tmp_atk = self.henatos(target)
if tmp_atk == -1:
message = target.name + " には きかなかった!"
elif tmp_atk > 0:
message = target.name + " の こうげきりょくを " + str(tmp_atk) + "さげた!"
elif tmp_atk == 0: # 効果はなくても残りターンは増える
message = target.name + " には こうかが なかった!"
# 増えることは無いので、elseは省略
return -1, message
elif str_spell == "マヌーサ":
tmp = self.manusa(target)
if tmp == -1:
message = target.name + " には きかなかった!"
elif tmp == 1:
message = target.name + "は まぼろしに つつまれている!"
elif tmp == 2:
message = target.name + "は まぼろしに つつまれた!"
return -1, message
point = random.randint(spell_lo_dict[str_spell], spell_up_dict[str_spell])
if spell_target_no == 0 or spell_target_no == 1: # ターゲット(0:味方単体,1:味方全員,2:敵単体,3:敵グループ,4:敵全体)
message = target.name + " の キズが かいふくした!"
else:
message = target.name + "に " + str(point) + "ポイントのダメージを与えた!"
return point, message
manusaメソッドを呼び出した後
戻り値によって、戦闘メッセージを
変えています。
また、先程ヘトナスの戻り値を
変更したので
一緒に変えました。
攻撃力の計算
攻撃力を計算する
attackメソッドを変更します。
前回まで戻り値は
ダメージのみでしたが
戦闘メッセージも
返すようにします。
def attack(self, obj):
message = []
if self.flag_manusa: # 攻撃者がマヌーサにかかっている
if random.random() > 3/8: # 命中率が37.5%
message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
return 0, message
base_dmg = self.atk/2 - obj.dfs/4
if base_dmg <= 0:
base_dmg = 0
width_dmg = base_dmg/16 + 1
min_dmg = int(base_dmg - width_dmg) if base_dmg != 0 else 0
max_dmg = int(base_dmg + width_dmg)
dmg = random.randint(min_dmg, max_dmg)
if dmg > 0:
message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
else:
message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
return dmg, message
マヌーサがかかっている時の
動作も追加しています。
if self.flag_manusa: # 攻撃者がマヌーサにかかっている
if random.random() > 3/8: # 命中率が37.5%
message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
return 0, message
マヌーサにかかっている時の
命中率は37.5%にしています。
※62.5%は攻撃が当たらない
会心の一撃と痛恨の一撃
会心の一撃と痛恨の一撃の機能を
attackメソッドに追加します。
効果などは
それぞれ以下のページを
参考にさせて頂きました。
会心の一撃
痛恨の一撃
相手の守備力に関係なく
攻撃力とほぼ同じダメージを与えます。
※ダメージ幅を±5%にしています。
def attack(self, obj):
message = []
if random.random() < 1/32:
base_dmg = self.get_atk()
min_dmg = int(base_dmg - 0.05*base_dmg)
max_dmg = int(base_dmg + 0.05*base_dmg)
dmg = random.randint(min_dmg, max_dmg)
message.append("かいしんの いちげき!")
message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
return dmg, message
if self.flag_manusa: # 攻撃者がマヌーサにかかっている
if random.random() > 3/8: # 命中率が37.5%
message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
return 0, message
(省略)
会心の一撃はマヌーサとか
関係なく命中させる為
一番最初に持ってきました。
1/32の確率で発生します。
if random.random() < 1/32:
base_dmg = self.get_atk()
min_dmg = int(base_dmg - 0.05*base_dmg)
max_dmg = int(base_dmg + 0.05*base_dmg)
dmg = random.randint(min_dmg, max_dmg)
message.append("かいしんの いちげき!")
message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
return dmg, message
会心の一撃はプレイヤー、
痛恨の一撃はモンスター
の攻撃とします。
どちらも機能は同じですが
戦闘メッセージが異なります。
ので、BraveクラスとMonsterクラスに
それぞれattackメソッドを追加しました。
class Chara():
(省略)
# def attack(self, obj): # 削除
(省略)
class Brave(Chara):
(省略)
def attack(self, obj):
message = []
if random.random() < 1/32:
base_dmg = self.get_atk()
min_dmg = int(base_dmg - 0.05*base_dmg)
max_dmg = int(base_dmg + 0.05*base_dmg)
dmg = random.randint(min_dmg, max_dmg)
message.append("かいしんの いちげき!")
message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
return dmg, message
if self.flag_manusa: # 攻撃者がマヌーサにかかっている
if random.random() > 3/8: # 命中率が37.5%
message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
return 0, messag
base_dmg = self.atk/2 - obj.dfs/4
if base_dmg <= 0:
base_dmg = 0
width_dmg = base_dmg/16 + 1
min_dmg = int(base_dmg - width_dmg) if base_dmg != 0 else 0
max_dmg = int(base_dmg + width_dmg)
dmg = random.randint(min_dmg, max_dmg)
if dmg > 0:
message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
else:
message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
return dmg, message
(省略)
class Monster(Chara):
(省略)
def attack(self, obj):
message = []
if random.random() < 1/32:
base_dmg = self.atk
min_dmg = int(self.atk - 0.05*self.atk)
max_dmg = int(self.atk + 0.05*self.atk)
dmg = random.randint(min_dmg, max_dmg)
message.append("つうこんの いちげき!")
message.append(obj.name + "は " + str(dmg) + "ポイントの ダメージを うけた!")
return dmg, message
if self.flag_manusa: # 攻撃者がマヌーサにかかっている
print("マヌーサ")
if random.random() > 3/8: # 命中率が37.5%
message.append("ミス!" + obj.name + " は ダメージを うけない!")
return 0, message
base_dmg = self.atk/2 - obj.dfs/4
if base_dmg <= 0:
base_dmg = 0
width_dmg = base_dmg/16 + 1
min_dmg = int(base_dmg - width_dmg) if base_dmg != 0 else 0
max_dmg = int(base_dmg + width_dmg)
dmg = random.randint(min_dmg, max_dmg)
if dmg > 0:
message.append(obj.name + "は " + str(dmg) + "ポイントの ダメージを うけた!")
else:
message.append("ミス!" + obj.name + " は ダメージを うけない!")
return dmg, message
※attackメソッドは
Charaクラスのままで
引数を一つ増やして、
プレイヤーかモンスターの
どちらの攻撃かを
判断しても良いと思います
攻撃の回避
攻撃についてもう一つ
回避率を追加します。
前回まで、攻撃は確実に当たっていましたが
一定の確率で攻撃をかわすようにします。
回避について以下のページを
参考にさせて頂きました。
回避率
(省略)
def attack(self, obj):
message = []
if random.random() < 1/32:
base_dmg = self.get_atk()
min_dmg = int(base_dmg - 0.05*base_dmg)
max_dmg = int(base_dmg + 0.05*base_dmg)
dmg = random.randint(min_dmg, max_dmg)
message.append("かいしんの いちげき!")
message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
return dmg, message
if self.flag_manusa: # 攻撃者がマヌーサにかかっている
if random.random() > 3/8: # 命中率が37.5%
message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
return 0, message
if obj.quick <= 400:
avoid = 1/64 + obj.quick//80 * (1/192)
elif obj.quick <= 500:
avoid = 1/64 + 5 * (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
base_dmg = self.atk/2 - obj.dfs/4
if base_dmg <= 0:
base_dmg = 0
width_dmg = base_dmg/16 + 1
min_dmg = int(base_dmg - width_dmg) if base_dmg != 0 else 0
max_dmg = int(base_dmg + width_dmg)
dmg = random.randint(min_dmg, max_dmg)
if dmg > 0:
message.append(obj.name + "に " + str(dmg) + "ポイントの ダメージ!!")
else:
message.append("ミス!" + obj.name + "に ダメージを あたえられない!")
return dmg, message
参考ページの以下を使っています。
「素早さ400未満では素早さが
80上がるごとに回避率が1/192上がり、
400以上では素早さが
25上がるごとに回避率が1/32上がる。」
if obj.quick <= 400:
# 80上がるごとに回避率が1/192上がり
avoid = 1/64 + obj.quick//80 * (1/192)
elif obj.quick <= 500:
# 25上がるごとに回避率が1/32上がる
avoid = 1/64 + 5 * (1/192) + (obj.quick-400)//25 * (1/32)
else: # 500より上は1/6
avoid = 1/6
※Braveクラス、Monsterクラスとも
同じ機能を追加しています。
商の整数部分は「//」を使います。
詳細はこちら。
素早さが50
→ obj.quick//80は0
素早さが83
→ obj.quick//80は1
素早さが350
→ obj.quick//80は4
ランダムで数値を取得して
その数値が回避率(avoid)より
小さい値となったら
攻撃を回避します。
if random.random() < avoid:
dmg = 0
回避した時のメッセージは
2つにしました。
どちらのメッセージになるかは
ランダムです。
if random.randint(0, 1) == 0:
message.append(obj.name + "は ひらりと みをかわした!")
else:
message.append(obj.name + "は すばやく みをかわした!")
chara.pyは以上です。
battleの変更
battle.pyを変更します。
補助呪文の残りターンを確認する
check_spell_effect関数に
マヌーサを追加します。
def check_spell_effect(player):
(省略)
if obj.turn_manusa != 0:
obj.turn_manusa -= 1
if obj.turn_manusa == 0:
check_message.append(obj.name + " のマヌーサ効果が消えた!")
obj.invalid_manusa() # マヌーサ効果を消す
return check_message
attckメソッドで
戦闘メッセージを返すことにしたので
攻撃の時の機能を少し変えます。
def main(screen, clock, font, fontS, player, area):
(省略)
elif idx == 12: # プレイヤーの攻撃
draw_battle(screen, fontS, turn_obj, player)
if tmr == 1:
set_message(turn_obj.name + " の攻撃!")
dmg, get_message = 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
for mes in get_message:
set_message(mes)
if tmr == 11:
btl_exp += do_attack(btl_enemy, dmg, turn_obj)
check_monster()
if tmr == 16:
init_message()
idx = 24 # ターンの確認
attackメソッドから受けた
戦闘メッセージを
tmrが5の時に表示します。
if tmr == 5:
emy_blink = 5
for mes in get_message:
set_message(mes)
モンスターの攻撃の時も
同様です。
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, get_message = turn_obj.attack(player)
for mes in get_message:
set_message(mes)
if dmg > 0:
dmg_eff = 5
else:
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 # ターンの確認
attackメソッドから受けた
戦闘メッセージを
tmrが9の時に表示します。
if tmr == 9:
dmg, get_message = turn_obj.attack(player)
for mes in get_message:
set_message(mes)
攻撃に関する変更は以上です。
あと、少しだけ
勝利した時に
獲得した経験値を表示します。
※戦闘メッセージの
1行を追加しました。
elif idx == 16: # 勝利
(省略)
if tmr == 5:
set_message(player.name + " は " + str(btl_exp) + "ポイントの けいけんちを かくとく!") # 追加
if player.exp >= player.lv_exp:
idx = 17
tmr = 0
あともう一つ
逃げた時にbtl_exp(経験値)を
0にしていましたが
戦闘が終わった時に
0にしているので
削除しました。
elif idx == 14: # 逃げられる?
draw_battle(screen, fontS, turn_obj, player)
if tmr == 1:
set_message(player.name + " は逃げ出した!")
if tmr == 5:
if btl_start == 1 or random.randint(0, 99) < escape_rate:
# btl_exp = 0 # 逃げたら0 ←戦闘が終わった時に0にしているので削除
idx = 22 # 戦闘終了
elif idx == 22: # 戦闘終了
idx = 1
monster.clear() # モンスターオブジェクトを削除
dead_monster.clear()
btl_exp = 0 # ←0にしている
player.spell_reset()
return 1
idxが14の時の
btl_exp = 0
を消しました。
という感じで、今回は以上です!(`・ω・´)
最後に
今回は相手に使う補助系の呪文マヌーサを追加しました。
また、会心の一撃や痛恨の一撃の機能、
攻撃の回避の機能を追加しました。
次回も・・何かします(^_^;)
【ご注意】
プログラムやデータなどは著作権法により保護されています。
著作者の許諾を得ずに、プログラムおよびデータそのものまたは改変したものを
配布したり販売したりすることはできません。
また、これらを利用して発生した損害などに関して、著作者は一切責任を負いません。