概要
本記事では、pythonを使って、ターミナルで動くターン制コマンドバトル(〇ケモンみたいな)を作成していきます。記事通りに手を動かしながらアプリを作ることで、コーディングの基礎を学んでいきましょう。
第一章 アイデアをアルゴリズムに変換する
1.1 アルゴリズムを考える
まずは、どのようなアルゴリズムでゲームを進行させるかを考えます。基本的な流れは以下のようにしました。
- プレイヤーと敵のHPを設定する
- ターン制で攻撃を行う
- 攻撃の結果を表示する
- HPが0になったら勝敗を決定する
処理フローを言語化できたところで、次は実際にコードを書いていきます。
1.2 コードを書く
まず関数の名前を決めましょう。今後どんどん更新していくため、わかりやすい名前をつけることが大切です。ここでは、battle()という関数名にします。
def battle():
関数名が決まったので、次は関数に肉付けをしていきます。戦闘なのでまずはHPを設定しましょう。とりあえず適当にプレイヤーのHPを20、敵のHPを10に設定します。
def battle():
player_hp = 20
enemy_hp = 10
print(player_hp)
print(enemy_hp)
battle() # 関数を実行
ここできちんとHPが表示されることを確認しましょう。ターミナルで以下のように入力して実行します。
$ python "自分のファイル名".py
20
10
次に、ターン制で攻撃を行うためのループを作成します。ここでは、プレイヤーと敵が交互に攻撃する形にします。
def battle():
player_hp = 20
enemy_hp = 10
while player_hp > 0 and enemy_hp > 0: # ループの条件
print("あなたのターンです。攻撃します!") # プレイヤーの攻撃
enemy_hp -= 5 # 敵にダメージを与える
print(f"敵に5のダメージ! 敵のHP: {max(enemy_hp, 0)}") # 敵のHPを表示
if enemy_hp > 0: # 敵がまだ生きている場合
print("敵のターンです。攻撃します!") # 敵の攻撃
player_hp -= 3 # プレイヤーにダメージを与える
print(f"あなたは3のダメージを受けた! あなたのHP: {max(player_hp, 0)}") # プレイヤーのHPを表示
if player_hp > 0:
print("\nあなたの勝ち!") # プレイヤーが勝った場合
else:
print("\nあなたは負けた…") # プレイヤーが負けた場合
battle()
ではコードの解説をしていきます。このコードを書くときに注意したいのは、HPが0未満にならないようにすることです。max()関数を使って、HPが0未満にならないようにしています。
また、プレイヤーと敵のターンを交互に行うために、whileループを使用しています。ループの条件は、両者のHPが0より大きいことです。
max() 関数は、与えられた複数の値の中から「最大値」を返す関数です。
たとえば、HPが0未満にならないようにする場合、max(現在のHP, 0) のように使います。
こうすることで、HPがマイナスになっても0が返され、表示や処理で負の値にならないようにできます。max(player_hp, 0) # プレイヤーのHPが0未満にならないようにする max(enemy_hp, 0) # 敵のHPが0未満にならないようにするこのように、max() を使うことで「最低でも0」という制約を簡単に実現できます。
ゲームのHP管理や、値が負になってはいけない場面でよく使われます。
実際にこのコードを実行してみると、プレイヤーと敵が交互に攻撃し、最終的に勝敗が決まることが確認できます。
$ python "自分のファイル名".py
あなたのターンです。攻撃します!
敵に5のダメージ! 敵のHP: 5
敵のターンです。攻撃します!
あなたは3のダメージを受けた! あなたのHP: 17
あなたのターンです。攻撃します!
敵に5のダメージ! 敵のHP: 0
あなたの勝ち!
これで初めに考えたアルゴリズムに沿った基本的なターン制コマンドバトルをターミナルに表示できました。
しかしこのままだとプログラムに指定されたように表示しているだけです。次は、ユーザーからの入力を受け付けて、よりインタラクティブなゲームにしていきましょう。
1.3 ユーザー入力を受け付ける
現在のコードでは、プレイヤーの攻撃が自動で行われています。これをユーザーがコマンドを入力して攻撃できるように変更します。プレイヤーは攻撃しかしていないので、攻撃コマンドを入力するだけで良いようにします。
def battle():
player_hp = 20
enemy_hp = 10
while player_hp > 0 and enemy_hp > 0:
command = input("コマンドを入力してください (攻撃): ") # プレイヤーからのコマンド入力を受け付ける
if command == "攻撃": # プレイヤーが攻撃コマンドを入力した場合
print("あなたのターンです。攻撃します!")
enemy_hp -= 5
print(f"敵に5のダメージ! 敵のHP: {max(enemy_hp, 0)}")
if enemy_hp > 0:
print("敵のターンです。攻撃します!")
player_hp -= 3
print(f"あなたは3のダメージを受けた! あなたのHP: {max(player_hp, 0)}")
else:
print("無効なコマンドです。もう一度入力してください。") #無効なコマンドが入力された場合の処理
if player_hp > 0:
print("\nあなたの勝ち!")
else:
print("\nあなたは負けた…")
battle()
このコードでは、input()関数を使ってユーザーからのコマンド入力を受け付けています。ユーザーが「攻撃」と入力した場合にのみ攻撃が行われます。それ以外の入力があった場合は「無効なコマンドです」と表示され、再度入力を促します。
これで、ユーザーがコマンドを入力して攻撃できるようになりました。実行してみると、以下のように表示されます。
$ python "自分のファイル名".py
コマンドを入力してください (攻撃): 攻撃
あなたのターンです。攻撃します!
敵に5のダメージ! 敵のHP: 5
敵のターンです。攻撃します!
あなたは3のダメージを受けた! あなたのHP: 17
コマンドを入力してください (攻撃): 防御
無効なコマンドです。もう一度入力してください。
コマンドを入力してください (攻撃): 攻撃
あなたのターンです。攻撃します!
敵に5のダメージ! 敵のHP: 0
あなたの勝ち!
さてこれで基本的なターン制コマンドバトルが完成しました。次の章では、さらにゲームを面白くするために、様々な変更を施していきましょう
第二章 ゲームを面白くするための改良
2.1 ユーザーの選択肢を増やそう
現在のゲームでは、プレイヤーは攻撃しかできません。これを改善して、プレイヤーが選択肢を選べるようにします。例えば、攻撃と魔法、防御の3つの選択肢を用意します。魔法は攻撃力を高くする代わりにMPを消費するものとします。防御は次の敵の攻撃を無効化するものとします。
def battle():
player_hp = 20
enemy_hp = 10
player_mp = 5 # プレイヤーのMP
print(f"あなたのHP: {player_hp}, MP: {player_mp}")
while player_hp > 0 and enemy_hp > 0:
command = input("コマンドを入力してください (攻撃, 魔法, 防御): ") # プレイヤーからのコマンド入力を受け付ける
if command == "攻撃": # プレイヤーが攻撃コマンドを入力した場合
print("あなたのターンです。攻撃します!")
enemy_hp -= 5
print(f"敵に5のダメージ! 敵のHP: {max(enemy_hp, 0)}")
elif command == "魔法": # プレイヤーが魔法コマンドを入力した場合
if player_mp >= 2:
print("あなたのターンです。魔法を使います!")
enemy_hp -= 8
player_mp -= 2
print(f"敵に8のダメージ! 敵のHP: {max(enemy_hp, 0)}")
print(f"あなたのMP: {player_mp}")
else:
print("MPが足りません!") # MPが足りない場合の処理
elif command == "防御": # プレイヤーが防御コマンドを入力した場合
print("あなたは防御を選択しました。次の敵の攻撃を無効化します。")
continue
else:
print("無効なコマンドです。もう一度入力してください。")
continue
if enemy_hp > 0:
if command != "防御": # 防御を選んでいない場合のみ敵が攻撃
print("敵のターンです。攻撃します!")
damage = 3 if command != "防御" else 0
player_hp -= damage
print(f"あなたは{damage}のダメージを受けた! あなたのHP: {max(player_hp, 0)}")
if player_hp > 0:
print("\nあなたの勝ち!")
else:
print("\nあなたは負けた…")
battle()
今回の変更ではMP(マジックポイント)を導入し、プレイヤーが魔法を使うことで強力な攻撃ができるようにしました。また、防御コマンドを追加し、次の敵の攻撃を無効化することができます。技術的には前章で書いたもののみを使っています。簡単なコードですが、ゲームの戦略性が増しました。実行してみると、以下のように表示されます。
$ python "自分のファイル名".py
あなたのHP: 20, MP: 5
コマンドを入力してください (攻撃, 魔法, 防御): 魔法
あなたのターンです。魔法を使います!
敵に8のダメージ! 敵のHP: 2
敵のターンです。攻撃します!
あなたは3のダメージを受けた! あなたのHP: 17
コマンドを入力してください (攻撃, 魔法, 防御): 防御
あなたは防御を選択しました。次の敵の攻撃を無効化します。
コマンドを入力してください (攻撃, 魔法, 防御): 攻撃
あなたのターンです。攻撃します!
敵に5のダメージ! 敵のHP: 0
あなたの勝ち!
次に、HPなどのステータスや、攻撃時のダメージなどをランダムに変化させて、よりリアルなバトル感を出していきましょう。
2.2 ランダム要素を追加する
今は、HPなどのステータスや攻撃や魔法のダメージが固定値になっています。これをランダムに変化させることで、よりリアルなバトル感を出します。Pythonのrandomモジュールを使って、それらの値をランダムに設定します。
import random # randomモジュールをインポート
def battle():
player_hp = random.randint(18, 25) # プレイヤーHPをランダムに設定
enemy_hp = random.randint(8, 15) # 敵HPをランダムに設定
player_mp = 5
print(f"あなたのHP: {player_hp}, MP: {player_mp}")
print(f"敵のHP: {enemy_hp}")
while player_hp > 0 and enemy_hp > 0:
command = input("コマンドを入力してください (攻撃, 魔法, 防御): ")
if command == "攻撃":
damage = random.randint(4, 7) # 攻撃ダメージをランダムに
print("あなたのターンです。攻撃します!")
enemy_hp -= damage
print(f"敵に{damage}のダメージ! 敵のHP: {max(enemy_hp, 0)}")
elif command == "魔法":
if player_mp >= 2:
damage = random.randint(7, 10) # 魔法ダメージをランダムに
print("あなたのターンです。魔法を使います!")
enemy_hp -= damage
player_mp -= 2
print(f"敵に{damage}のダメージ! 敵のHP: {max(enemy_hp, 0)}")
print(f"あなたのMP: {player_mp}")
else:
print("MPが足りません!")
elif command == "防御":
print("あなたは防御を選択しました。次の敵の攻撃を無効化します。")
enemy_attack = False
continue
else:
print("無効なコマンドです。もう一度入力してください。")
continue
if enemy_hp > 0:
if command != "防御":
damage = random.randint(2, 5) # 敵の攻撃ダメージもランダムに
print("敵のターンです。攻撃します!")
player_hp -= damage
print(f"あなたは{damage}のダメージを受けた! あなたのHP: {max(player_hp, 0)}")
if player_hp > 0:
print("\nあなたの勝ち!")
else:
print("\nあなたは負けた…")
battle()
このコードでは、プレイヤーと敵のHP、攻撃や魔法のダメージをランダムに設定しています。random.randint(a, b)を使うことで、aからbまでの整数の中からランダムな値を取得できます。これにより、毎回異なるバトルが楽しめるようになります。
random.randint(a, b) は、a から b までの整数の中からランダムな値を返す関数です。
たとえば、random.randint(1, 10) とすると、1 から 10 の間の整数がランダムに選ばれます。
この関数は、ゲームやシミュレーションでランダムな要素を導入する際によく使われます。
実行してみると、以下のように表示されます。
$ python "自分のファイル名".py
あなたのHP: 21, MP: 5
敵のHP: 8
コマンドを入力してください (攻撃, 魔法, 防御): 魔法
あなたのターンです。魔法を使います!
敵に8のダメージ! 敵のHP: 0
あなたのMP: 3
あなたの勝ち!
これで、毎回異なるバトルが楽しめるようになりました。
次に、リストを使って、敵やプレイヤーのステータスを管理してみましょう
2.3 リストを使ってステータスを管理する
現在のコードでは、プレイヤーや敵のステータスを個別の変数で管理しています。これをリストを使って管理することで、より効率的にコードを整理できます。また複数の敵データを作成しておくことでゲームの攻略性も向上させてみましょう。以下のように変更します。
import random
def battle():
player = { # プレイヤーのステータスを辞書で管理
"hp": random.randint(18, 25),
"mp": random.randint(2, 6),
"name": "プレイヤー"
}
enemies = [ # 敵の情報をリストの辞書で管理
{"name": "スライム", "hp": random.randint(8, 15)},
{"name": "ゴブリン", "hp": random.randint(10, 20)},
{"name": "ドラゴン", "hp": random.randint(15, 30)}
]
print(f"{player['name']}のHP: {player['hp']}, MP: {player['mp']}")
for enemy in enemies: # 敵の情報をループで処理
print(f"敵の名前: {enemy['name']}, HP: {enemy['hp']}")
while player["hp"] > 0 and enemy["hp"] > 0:
command = input("コマンドを入力してください (攻撃, 魔法, 防御): ")
if command == "攻撃":
damage = random.randint(4, 7)
print(f"{player['name']}のターンです。{enemy['name']}に攻撃します!")
enemy["hp"] -= damage
print(f"{enemy['name']}に{damage}のダメージ! 敵のHP: {max(enemy['hp'], 0)}")
elif command == "魔法":
if player["mp"] >= 2:
damage = random.randint(7, 10)
print(f"{player['name']}のターンです。{enemy['name']}に魔法を使います!")
enemy["hp"] -= damage
player["mp"] -= 2
print(f"{enemy['name']}に{damage}のダメージ! 敵のHP: {max(enemy['hp'], 0)}")
print(f"{player['name']}のMP: {player['mp']}")
else:
print("MPが足りません!")
elif command == "防御":
print(f"{player['name']}は防御を選択しました。次の敵の攻撃を無効化します。")
enemy_attack = False
continue
else:
print("無効なコマンドです。もう一度入力してください。")
continue
if enemy["hp"] > 0:
if command != "防御":
damage = random.randint(2, 5)
print(f"{enemy['name']}のターンです。攻撃します!")
player["hp"] -= damage
print(f"{player['name']}は{damage}のダメージを受けた! あなたのHP: {max(player['hp'], 0)}")
if player["hp"] <= 0:
print("\nあなたは負けた…")
break
else:
print(f"\n{player['name']}は{enemy['name']}を倒した!")
if player["hp"] > 0:
print("\nあなたの勝ち!")
battle()
このコードでは、プレイヤーのステータスを辞書で管理し、敵の情報をリストの辞書で管理しています。これにより、複数の敵を簡単に追加できるようになり、コードも整理されました。
実行してみると、以下のように表示されます。
$ python "自分のファイル名".py
プレイヤーのHP: 20, MP: 4
敵の名前: スライム, HP: 14
コマンドを入力してください (攻撃, 魔法, 防御): 魔法
プレイヤーのターンです。スライムに魔法を使います!
スライムに7のダメージ! 敵のHP: 7
プレイヤーのMP: 2
スライムのターンです。攻撃します!
プレイヤーは3のダメージを受けた! あなたのHP: 17
コマンドを入力してください (攻撃, 魔法, 防御): 魔法
プレイヤーのターンです。スライムに魔法を使います!
スライムに7のダメージ! 敵のHP: 0
プレイヤーのMP: 0
プレイヤーはスライムを倒した!
敵の名前: ゴブリン, HP: 16
コマンドを入力してください (攻撃, 魔法, 防御): 攻撃
プレイヤーのターンです。ゴブリンに攻撃します!
ゴブリンに6のダメージ! 敵のHP: 10
ゴブリンのターンです。攻撃します!
プレイヤーは4のダメージを受けた! あなたのHP: 13
コマンドを入力してください (攻撃, 魔法, 防御): 攻撃
プレイヤーのターンです。ゴブリンに攻撃します!
ゴブリンに5のダメージ! 敵のHP: 5
ゴブリンのターンです。攻撃します!
プレイヤーは5のダメージを受けた! あなたのHP: 8
コマンドを入力してください (攻撃, 魔法, 防御): 攻撃
プレイヤーのターンです。ゴブリンに攻撃します!
ゴブリンに5のダメージ! 敵のHP: 0
プレイヤーはゴブリンを倒した!
敵の名前: ドラゴン, HP: 24
コマンドを入力してください (攻撃, 魔法, 防御): 攻撃
プレイヤーのターンです。ドラゴンに攻撃します!
ドラゴンに7のダメージ! 敵のHP: 17
ドラゴンのターンです。攻撃します!
プレイヤーは2のダメージを受けた! あなたのHP: 6
コマンドを入力してください (攻撃, 魔法, 防御): 魔法
MPが足りません!
ドラゴンのターンです。攻撃します!
プレイヤーは4のダメージを受けた! あなたのHP: 2
コマンドを入力してください (攻撃, 魔法, 防御): 魔法
MPが足りません!
ドラゴンのターンです。攻撃します!
プレイヤーは2のダメージを受けた! あなたのHP: 0
あなたは負けた…
このように、リストを使うことで複数の敵を簡単に管理できるようになりました。また、プレイヤーのステータスも辞書で管理することで、コードがより読みやすくなりました。
第三章 さらなる改良と拡張
本章ではゲームを面白くするだけでなく、ユーザーのUX向上や、コードの可読性を高めるための改良を行います。具体的には、ゲームの開始時にプレイヤー名を入力できるようにしたり、入力内容を簡素化したりするなどの改善を行います。
3.1 UXの向上
まず、ゲーム開始時にプレイヤーの名前を入力できるようにします。やっぱり自分の名前でプレイしたいですよね。加えて現状コマンド入力をわざわざ入力するのは面倒なので、攻撃や魔法、防御のコマンドを数字で入力できるようにします。以下のように変更します。
import random
def battle():
player_name = input("プレイヤーの名前を入力してください: ") # プレイヤーの名前を入力
player = {
"hp": random.randint(18, 25),
"mp": random.randint(2, 6),
"name": player_name # プレイヤーの名前を辞書に追加
}
enemies = [
{"name": "スライム", "hp": random.randint(8, 15)},
{"name": "ゴブリン", "hp": random.randint(10, 20)},
{"name": "ドラゴン", "hp": random.randint(15, 30)}
]
print(f"{player['name']}のHP: {player['hp']}, MP: {player['mp']}")
for enemy in enemies:
print(f"敵の名前: {enemy['name']}, HP: {enemy['hp']}")
while player["hp"] > 0 and enemy["hp"] > 0:
command = input("コマンドを入力してください (1: 攻撃, 2: 魔法, 3: 防御): ") # コマンドを数字で入力
if command == "1":
# 攻撃
damage = random.randint(4, 7)
print(f"{player['name']}のターンです。{enemy['name']}に攻撃します!")
enemy["hp"] -= damage
print(f"{enemy['name']}に{damage}のダメージ! 敵のHP: {max(enemy['hp'], 0)}")
elif command == "2":
# 魔法
if player["mp"] >= 2:
damage = random.randint(7, 10)
print(f"{player['name']}のターンです。{enemy['name']}に魔法を使います!")
enemy["hp"] -= damage
player["mp"] -= 2
print(f"{enemy['name']}に{damage}のダメージ! 敵のHP: {max(enemy['hp'], 0)}")
print(f"{player['name']}のMP: {player['mp']}")
else:
print("MPが足りません!")
elif command == "3":
# 防御
print(f"{player['name']}は防御を選択しました。次の敵の攻撃を無効化します。")
enemy_attack = False
else:
print("無効なコマンドです。もう一度入力してください。")
continue
if enemy["hp"] > 0:
if command == "3":
damage = 0
else:
damage = random.randint(2, 5)
print(f"{enemy['name']}のターンです。攻撃します!")
player["hp"] -= damage
print(f"{player['name']}は{damage}のダメージを受けた! あなたのHP: {max(player['hp'], 0)}")
if player["hp"] <= 0:
print("\nあなたは負けた…")
break
else:
print(f"\n{player['name']}は{enemy['name']}を倒した!")
if player["hp"] > 0:
print("\nあなたの勝ち!")
battle()
このコードでは、ゲーム開始時にプレイヤーの名前を入力できるようになりました。また、コマンド入力を数字で行えるように変更しました。これにより、ユーザーはより直感的に操作できるようになります。
実行結果は省略します。
3.2 コードの可読性を高める
コードの可読性を高めるために、関数を分割して整理します。これにより、各処理が明確になり、コードの理解が容易になります。
import random
def initialize_player():
"""プレイヤーの初期化"""
"""プレイヤーの名前とステータスを設定"""
player_name = input("プレイヤーの名前を入力してください: ")
return {
"name": player_name,
"hp": random.randint(18, 25),
"mp": random.randint(2, 6)
}
def initialize_enemies():
"""敵の初期化"""
"""敵の名前とステータスを設定"""
return [
{"name": "スライム", "hp": random.randint(8, 15)},
{"name": "ゴブリン", "hp": random.randint(10, 20)},
{"name": "ドラゴン", "hp": random.randint(15, 30)}
]
def attack(player, enemy):
"""プレイヤーの攻撃処理"""
damage = random.randint(4, 7)
print(f"{player['name']}のターンです。{enemy['name']}に攻撃します!")
enemy["hp"] -= damage
print(f"{enemy['name']}に{damage}のダメージ! 敵のHP: {max(enemy['hp'], 0)}")
def magic(player, enemy):
"""プレイヤーの魔法処理"""
if player["mp"] >= 2:
damage = random.randint(7, 10)
print(f"{player['name']}のターンです。{enemy['name']}に魔法を使います!")
enemy["hp"] -= damage
player["mp"] -= 2
print(f"{enemy['name']}に{damage}のダメージ! 敵のHP: {max(enemy['hp'], 0)}")
print(f"{player['name']}のMP: {player['mp']}")
else:
print("MPが足りません!")
def defend(player):
"""プレイヤーの防御処理"""
print(f"{player['name']}は防御を選択しました。次の敵の攻撃を無効化します。")
enemy_attack = False
return True
def enemy_attack(player, enemy):
"""敵の攻撃処理"""
damage = random.randint(2, 5)
print(f"{enemy['name']}のターンです。攻撃します!")
player["hp"] -= damage
print(f"{player['name']}は{damage}のダメージを受けた! あなたのHP: {max(player['hp'], 0)}")
def battle():
"""バトルのメイン処理"""
player = initialize_player()
enemies = initialize_enemies()
print(f"{player['name']}のHP: {player['hp']}, MP: {player['mp']}")
for enemy in enemies:
print(f"敵の名前: {enemy['name']}, HP: {enemy['hp']}")
while player["hp"] > 0 and enemy["hp"] > 0:
command = input("コマンドを入力してください (1: 攻撃, 2: 魔法, 3: 防御): ")
if command == "1":
attack(player, enemy)
elif command == "2":
magic(player, enemy)
elif command == "3":
defend(player)
continue
else:
print("無効なコマンドです。もう一度入力してください。")
continue
if enemy["hp"] > 0:
if command != "3":
enemy_attack(player, enemy)
if player["hp"] <= 0:
print("\nあなたは負けた…")
break
else:
print(f"\n{player['name']}は{enemy['name']}を倒した!")
if player["hp"] > 0:
print("\nあなたの勝ち!")
battle()
関数分割の基準について
本記事では「1つの関数は1つの役割(機能)だけを持つ」という原則に従い、以下のように分割しています。
- initialize_player:プレイヤーの初期化(名前やHP/MPの設定)という「初期化処理」の機能
- initialize_enemies:敵キャラクターの初期化(複数の敵の生成)という「初期化処理」の機能
- attack:プレイヤーの攻撃処理という「攻撃アクション」の機能
- magic:プレイヤーの魔法攻撃処理という「魔法アクション」の機能
- defend:プレイヤーの防御処理という「防御アクション」の機能
- enemy_attack:敵の攻撃処理という「敵アクション」の機能
- battle:ゲーム全体の進行管理という「バトル全体の制御」の機能
このように、「何をする処理か(=機能)」ごとに関数を分けることで、
・各関数の役割が明確になる
・再利用や修正がしやすくなる
・バグの発見やテストがしやすくなる
といったメリットがあります。
今回は1つのファイルにまとめていますが、チーム開発などでは始めにどのような関数を作るかを決めておくと、初めから複数人で開発を進めやすくなります。
クラスを使ってコードを整理する
さらに可読性や拡張性を高める方法として、「クラス」を使ってプレイヤーや敵、バトルの処理をまとめる方法もあります。クラスを使うことで、関連するデータと処理をひとまとまりにでき、オブジェクト指向の考え方も学べます。
以下は、プレイヤーと敵をCharacterクラスで表現し、バトル処理をBattleクラスにまとめた例です。
import random
class Character:
"""キャラクターの基本クラス"""
"""プレイヤーと敵の共通の属性とメソッドを定義"""
def __init__(self, name, hp, mp=0):
self.name = name
self.hp = hp
self.mp = mp
def is_alive(self):
return self.hp > 0
def attack(self, target):
damage = random.randint(4, 7)
target.hp -= damage
print(f"{self.name}の攻撃!{target.name}に{damage}のダメージ! 残りHP: {max(target.hp, 0)}")
def magic(self, target):
if self.mp >= 2:
damage = random.randint(7, 10)
target.hp -= damage
self.mp -= 2
print(f"{self.name}の魔法!{target.name}に{damage}のダメージ! 残りHP: {max(target.hp, 0)} MP: {self.mp}")
else:
print("MPが足りません!")
def defend(self):
print(f"{self.name}は防御した!")
return True
class Battle:
"""バトルの処理をまとめたクラス"""
"""プレイヤーと敵のバトルを管理"""
def __init__(self, player, enemies):
self.player = player
self.enemies = enemies
def start(self):
for enemy in self.enemies:
print(f"\n敵が現れた!{enemy.name} (HP: {enemy.hp})")
while self.player.is_alive() and enemy.is_alive():
command = input("コマンドを入力してください (1: 攻撃, 2: 魔法, 3: 防御): ")
if command == "1":
self.player.attack(enemy)
elif command == "2":
self.player.magic(enemy)
elif command == "3":
self.player.defend()
continue
else:
print("無効なコマンドです。")
continue
if enemy.is_alive() and command != "3":
enemy.attack(self.player)
if not self.player.is_alive():
print("\nあなたは負けた…")
return
else:
print(f"{enemy.name}を倒した!")
print("\nあなたの勝ち!")
def main():
"""ゲームのメイン処理"""
"""プレイヤーの名前を入力し、バトルを開始"""
player_name = input("プレイヤーの名前を入力してください: ")
player = Character(player_name, random.randint(18, 25), random.randint(2, 6))
enemies = [
Character("スライム", random.randint(8, 15)),
Character("ゴブリン", random.randint(10, 20)),
Character("ドラゴン", random.randint(15, 30))
]
battle = Battle(player, enemies)
battle.start()
main()
このようにクラスを使うことで、データ(HPやMPなど)と処理(攻撃や魔法など)をまとめて管理でき、コードの見通しが良くなります。今後さらに機能を追加したい場合も、クラスごとに拡張しやすくなります。
例えば、回復コマンドを追加してみましょう
# ...既存のCharacterクラス...
def heal(self):
"""プレイヤー自身を回復するメソッド(新機能の追加例)"""
if self.mp >= 3:
heal_amount = random.randint(8, 12)
self.hp += heal_amount
self.mp -= 3
print(f"{self.name}は回復魔法を使った!HPが{heal_amount}回復した! 残りHP: {self.hp} MP: {self.mp}")
else:
print("MPが足りません!")
# ...Battleクラスのstartメソッド内のコマンド入力部分...
command = input("コマンドを入力してください (1: 攻撃, 2: 魔法, 3: 防御, 4: 回復): ")
if command == "1":
self.player.attack(enemy)
elif command == "2":
self.player.magic(enemy)
elif command == "3":
self.player.defend()
continue
elif command == "4": # 追加
self.player.heal() # 追加
continue
else:
print("無効なコマンドです。")
continue
クラスを使っていることでどこに新しい機能を追加すればよいかなどが明確にわかりますね!
このように、拡張性を高くする面でも可読性を上げるためのコードのブラッシュアップは重要です。
3.3 ゲームの終了条件を追加する
ゲームの終了条件を追加して、プレイヤーが勝利した場合や敗北した場合に適切なメッセージを表示するようにします。また、ゲームを続けるかどうかの選択肢も追加します。以下のように変更します。
```python
import random
class Character:
def __init__(self, name, hp, mp=0):
self.name = name
self.hp = hp
self.mp = mp
def is_alive(self):
return self.hp > 0
def attack(self, target):
damage = random.randint(4, 7)
target.hp -= damage
print(f"{self.name}の攻撃!{target.name}に{damage}のダメージ! 残りHP: {max(target.hp, 0)}")
def magic(self, target):
if self.mp >= 2:
damage = random.randint(7, 10)
target.hp -= damage
self.mp -= 2
print(f"{self.name}の魔法!{target.name}に{damage}のダメージ! 残りHP: {max(target.hp, 0)} MP: {self.mp}")
else:
print("MPが足りません!")
def defend(self):
print(f"{self.name}は防御した!")
return True
class Battle:
def __init__(self, player, enemies):
self.player = player
self.enemies = enemies
def start(self):
for enemy in self.enemies:
print(f"\n敵が現れた!{enemy.name} (HP: {enemy.hp})")
while self.player.is_alive() and enemy.is_alive():
command = input("コマンドを入力してください (1: 攻撃, 2: 魔法, 3: 防御): ")
if command == "1":
self.player.attack(enemy)
elif command == "2":
self.player.magic(enemy)
elif command == "3":
self.player.defend()
continue
else:
print("無効なコマンドです。")
continue
if enemy.is_alive() and command != "3":
enemy.attack(self.player)
if not self.player.is_alive():
print("\nあなたは負けた…")
return
else:
print(f"{enemy.name}を倒した!")
print("\nあなたの勝ち!")
def main():
while True:
player_name = input("プレイヤーの名前を入力してください: ")
player = Character(player_name, random.randint(18, 25), random.randint(2, 6))
enemies = [
Character("スライム", random.randint(8, 15)),
Character("ゴブリン", random.randint(10, 20)),
Character("ドラゴン", random.randint(15, 30))
]
battle = Battle(player, enemies)
battle.start()
play_again = input("もう一度プレイしますか? (y/n): ") # プレイヤーに再プレイの確認をする
if play_again.lower() != 'y': # プレイヤーが再プレイを希望しない場合
print("ゲームを終了します。ありがとうございました!") # ゲーム終了メッセージ
break
main()
このコードでは、ゲームの終了条件を追加し、プレイヤーが勝利した場合や敗北した場合に適切なメッセージを表示します。また、ゲームを続けるかどうかの選択肢も追加しました。プレイヤーはゲームを繰り返し楽しむこともできるようになりました。
第四章 まとめ
ここまでで、基本的なターン制コマンドバトルゲームを作成しながら、CUIアプリケーションの開発方法を学びました。以下のポイントが特に重要です。
- 基本的な構造: ゲームの基本的な構造を理解し、プレイヤーと敵のステータスを管理する方法を学びました。
- ユーザー入力: ユーザーからの入力を受け付け、コマンドに応じてゲームの進行を制御する方法を学びました。
- ランダム要素: ゲームにランダム要素を追加することで、毎回異なる体験を提供する方法を学びました。
- リストと辞書の活用: リストや辞書を使って、複数の敵やプレイヤーのステータスを効率的に管理する方法を学びました。
- 関数の分割: コードを関数に分割することで、可読性を高め、各処理の役割を明確にする方法を学びました。
- UXの向上: ユーザーエクスペリエンスを向上させるために、プレイヤー名の入力やコマンドの簡素化を行いました。
-
ゲームの終了条件: ゲームの終了条件を設定し、プレイヤーが勝利した場合や敗北した場合に適切なメッセージを表示する方法を学びました。
このように、まずは必須機能を言語化し、次にそれを実装していくことで、更なる改良部分が見えてきます。まずは手を動かして作ってみることを強く勧めます。
今回のアプリ開発はいったん終了しますが、自分で考えたアイデアを実装してみるなど、さらに改良を加えてみてください。例えば、敵の種類を増やして、出現する敵をランダムに選ぶ、アイテムを導入してプレイヤーのステータスを変動させる、レベルアップシステムを追加する、攻撃や魔法の種類を増やすなど、様々な改良が考えられます。自分のアイデアを実装して、より面白いゲームにしてみてください。