s_battle/proc_hero.py
主人公側の進行を管理するs_battle/proc_hero.pyモジュールです。かなりボリュームがあるので、全体を掲載したあと、部分を分割しながら説明します。
import pygame, data, hero, enemy, dialog, audio
from random import randrange
# 開始
def start():
action = data.menu_texts[data.menu_sel] # 選択行動取得
# 逃走
if action == "ESCAPE": # 選択行動が ESCAPE の場合
if randrange(2) == 0: # 1/2の確率
dialog.show("逃げるのに\n成功しました") # ダイアログ表示
data.scene_next = "map" # シーンをマップに
else:
dialog.show("逃げるのに\n失敗しました") # ダイアログ表示
data.proc = "enemy_start" # 進行を敵開始に
return # 処理を打ち切る
# 通常攻撃
at = randrange(hero.at // 2, hero.at) # 半分以上、1倍未満
# 魔法
if action == "MAGIC": # 選択行動が MAGIC の場合
if hero.mp == 0: # MPが0
dialog.show("MPが足りません") # ダイアログ表示
return # 処理を打ち切る
at = randrange(hero.at * 2, hero.at * 4) # 2倍以上、4倍未満
hero.mp -= 1
# ダメージ適用
df = randrange(enemy.df) # 0以上、1倍未満
damage = max(1, at - df) # ダメージ(最小1)
enemy.hp = max(0, enemy.hp - damage) # HP減少(HPの最低値は0)
# 演出と進行
audio.damage.play() # ダメージSE再生
data.ef_enemy = True # 敵演出ありに
data.ef_time_start = pygame.time.get_ticks() # 演出開始時間記録
data.proc = "hero_draw" # 進行を自描画に
# 描画
def draw():
time = pygame.time.get_ticks() # ゲーム開始からの経過時間
if time >= data.ef_time_start + 500: # 500ミリ秒以上経過したか
data.ef_enemy = False # 敵演出なしに
end() # 終了
# 終了
def end():
data.proc = "enemy_start" # 進行を敵開始に
if enemy.hp == 0: # HPが0か確認
if enemy.boss: # ラスボスか確認
# ラスボス時はエンディング
audio.play(audio.ENDING) # エンディングのBGM再生
dialog.show(f"{enemy.name}を\n倒しました") # ダイアログ表示
dialog.show("あなたは王国を\n救いました") # ダイアログ表示
dialog.show("Congratulations!") # ダイアログ表示
data.scene_next = "title" # シーンをタイトルに
else:
audio.play(audio.WIN) # 勝利のBGM再生
dialog.show(f"{enemy.name}を\n倒しました") # ダイアログ表示
mes = f"経験値 {enemy.exp} 獲得" # 経験値獲得メッセージ
if hero.add_exp(enemy.exp): # 経験値追加(レベルアップ判定)
mes += f"\nレベル {hero.level} に上昇" # レベルアップ時メッセージ
dialog.show(mes) # ダイアログ表示
data.scene_next = "map" # シーンをマップに
インポート部分ではpygame, data, hero, enemy, dialog, audioを読み込みます。また、randomからrandrange関数を読み込みます。randomをインポートしてrandom.randrange()と書いてもよいのですが、プログラムのボリュームが大きいので、少しでも記述量を減らすために関数のみを読み込んでいます。
開始の冒頭
次は、開始をおこなうstart()関数の冒頭部分です。
# 開始
def start():
action = data.menu_texts[data.menu_sel] # 選択行動取得
# 逃走
if action == "ESCAPE": # 選択行動が ESCAPE の場合
if randrange(2) == 0: # 1/2の確率
dialog.show("逃げるのに\n成功しました") # ダイアログ表示
data.scene_next = "map" # シーンをマップに
else:
dialog.show("逃げるのに\n失敗しました") # ダイアログ表示
data.proc = "enemy_start" # 進行を敵開始に
return # 処理を打ち切る
選択したメニュー位置data.menu_selに当たるdata.menu_textsのテキストを読み込み、変数actionに代入します。このactionが選択した行動です。
actionの中身は"SWORD"、"MAGIC"、"ESCAPE"のいずれかになります。
actionが"ESCAPE"(逃走)のときは、1/2の確率で逃走に成功します。成功した場合はダイアログを表示して、マップ画面に移動します。失敗した場合はダイアログを表示して、進行を敵手番に移します。
攻撃とダメージの適用
次は攻撃とダメージの適用です。
# 通常攻撃
at = randrange(hero.at // 2, hero.at) # 半分以上、1倍未満
# 魔法
if action == "MAGIC": # 選択行動が MAGIC の場合
if hero.mp == 0: # MPが0
dialog.show("MPが足りません") # ダイアログ表示
return # 処理を打ち切る
at = randrange(hero.at * 2, hero.at * 4) # 2倍以上、4倍未満
hero.mp -= 1
# ダメージ適用
df = randrange(enemy.df) # 0以上、1倍未満
damage = max(1, at - df) # ダメージ(最小1)
enemy.hp = max(0, enemy.hp - damage) # HP減少(HPの最低値は0)
まず、通常攻撃("SWORD")の攻撃力atを求めます。ランダムな数値で、主人公の攻撃力の半分以上、1倍未満です。
次にactionが"MAGIC"だった場合は、MPが足りるか確認します。足りないときはダイアログを表示して処理を打ち切ります。
足りる場合は、攻撃力atを魔法の攻撃力に変更して、mpを1減らします。魔法の攻撃力は、ランダムな数値で、主人公の攻撃力の2倍以上、4倍未満です。
敵の防御力dfを求めます。0以上、敵の防御力未満です。
求めた攻撃力から防御力を引いて、ダメージを計算します。その際、1未満にならないように、引数のうち最大の値を返すmax()関数を利用します。
最後に敵のhpを減らします。こちらもmax()関数を利用して、0未満にならないようにします。
演出と進行、描画
次は演出と進行、描画です。
# 演出と進行
audio.damage.play() # ダメージSE再生
data.ef_enemy = True # 敵演出ありに
data.ef_time_start = pygame.time.get_ticks() # 演出開始時間記録
data.proc = "hero_draw" # 進行を自描画に
# 描画
def draw():
time = pygame.time.get_ticks() # ゲーム開始からの経過時間
if time >= data.ef_time_start + 500: # 500ミリ秒以上経過したか
data.ef_enemy = False # 敵演出なしに
end() # 終了
ダメージのSEを再生して、data.ef_enemyをTrueにして敵の演出を有効にします。また、data.ef_time_startにゲーム開始からの経過時間を代入します。
最後に、進行data.procを、主人公の描画の"hero_draw"にします。
描画を管理するdraw()関数では、500ミリ秒経過したら描画を終了します。その際、data.ef_enemyをFalseにして敵の演出を止めます。そしてend()関数を実行します。
終了処理
最後は終了をおこなうend()関数です。
# 終了
def end():
data.proc = "enemy_start" # 進行を敵開始に
if enemy.hp == 0: # HPが0か確認
if enemy.boss: # ラスボスか確認
# ラスボス時はエンディング
audio.play(audio.ENDING) # エンディングのBGM再生
dialog.show(f"{enemy.name}を\n倒しました") # ダイアログ表示
dialog.show("あなたは王国を\n救いました") # ダイアログ表示
dialog.show("Congratulations!") # ダイアログ表示
data.scene_next = "title" # シーンをタイトルに
else:
audio.play(audio.WIN) # 勝利のBGM再生
dialog.show(f"{enemy.name}を\n倒しました") # ダイアログ表示
mes = f"経験値 {enemy.exp} 獲得" # 経験値獲得メッセージ
if hero.add_exp(enemy.exp): # 経験値追加(レベルアップ判定)
mes += f"\nレベル {hero.level} に上昇" # レベルアップ時メッセージ
dialog.show(mes) # ダイアログ表示
data.scene_next = "map" # シーンをマップに
進行data.procを敵開始"enemy_start"にしたあと、敵のhpが0なら勝利の処理をおこないます。
勝利の処理は、ラスボスか通常戦闘かで異なります。まずは、それぞれBGMを再生してダイアログを表示します。
ラスボスのときは、ダイアログを表示したあとタイトル画面に移ります。通常戦闘のときは、経験値を獲得してマップ画面に移ります。

s_battle/proc_enemy.py
敵側の進行を管理するs_battle/proc_enemy.pyモジュールです。主人公側と関数の構成は同じですが、主人公側に比べてボリュームは少ないです。
import pygame, data, hero, enemy, dialog, audio
from random import randrange
# 開始
def start():
# 攻撃
at = randrange(enemy.at // 2, enemy.at) # 半分以上、1倍未満
df = randrange(hero.df) # 0以上、1倍未満
damage = max(1, at - df) # ダメージ(最小1)
hero.hp = max(0, hero.hp - damage) # HP減少(HPの最低値は0)
# 演出と進行
audio.damage.play() # ダメージSE再生
data.ef_hero = True # 自演出ありに
data.ef_time_start = pygame.time.get_ticks() # 演出開始時間記録
data.proc = "enemy_draw" # 進行を敵描画に
# 描画
def draw():
time = pygame.time.get_ticks() # ゲーム開始からの経過時間
if time >= data.ef_time_start + 500: # 500ミリ秒以上経過したか
data.ef_hero = False # 自演出なしに
end() # 終了
# 終了
def end():
data.proc = "menu" # 進行をメニューに
if hero.hp == 0: # HPが0か確認
audio.play(audio.LOSE) # 敗北のBGM再生
dialog.show("あなたは死にました\nそして復活しました") # ダイアログ表示
mes = "経験値 20 獲得" # 経験値獲得メッセージ
if hero.add_exp(20): # 経験値追加(レベルアップ判定)
mes += f"\nレベル {hero.level} に上昇" # レベルアップ時メッセージ
dialog.show(mes) # ダイアログ表示
# スタート位置に移動して回復
hero.x = hero.next_x = hero.start_x # X位置、次回X位置を開始X位置に
hero.y = hero.next_y = hero.start_y # Y位置、次回Y位置を開始Y位置に
hero.hp = hero.hp_max # HP回復
hero.mp = hero.mp_max # MP回復
data.scene_next = "map" # シーンをマップに
インポート部分、描画を管理するdraw()関数は主人公側と同じです。開始のstart()関数は、逃走と魔法がないので短くなっています。説明が必要なのは、終了のend()関数だけです。
end()関数では、進行data.procをメニュー"menu"にしたあと、主人公のhpが0なら敗北の処理をおこないます。
敗北時は、経験値が20固定で入り、ダイアログを表示します。そして、開始位置に移動して、hpとmpを最大値まで回復します。
最後に、マップ画面に移動してバトルを抜けます。
