【実践OOPプロジェクト #2】RPGの心臓部!ターン制バトルシステムを構築しよう

ALL

こんにちは、Tech Samuraiです!
前回の記事「【実践OOPプロジェクト #1】PythonでテキストRPG開発!キャラクタークラスを作ろう」では、継承とカプセル化を駆使して、RPGの世界に生きるキャラクターたちの設計図を完成させましたね。

私たちの手で生み出された英雄「Tech Samurai」と敵「スライム」。しかし、彼らはまだ、私たちが一つずつ命令を与えなければ動けない、ただの人形です。今回の冒険の目的は、この人形たちに命を吹き込み、自律的に戦うための**「戦闘システム」**を構築することです。

その核心となるのが、多くのRPGで採用されている**「ターン制バトル」**の仕組みです。プレイヤーのターン、敵のターン、と交互に行動を繰り返す、あの馴染み深いシステムを、`while`ループを使って実装していきます。この記事を読み終える頃には、あなたのターミナルは、手に汗握る戦いの舞台へと変貌しているでしょう!


1. バトルの設計図:ターン制の流れを考える

コードを書き始める前に、戦闘がどのような流れで進むのか、そのルールを明確にしましょう。

  1. 戦闘開始!
  2. ループ開始: プレイヤーと敵の両方が生きている限り、戦闘を続ける。
  3. プレイヤーのターン:
    • 現在のステータスを表示する。
    • プレイヤーに「攻撃」や「逃げる」などの行動を選択させる。
    • 選択に応じて、プレイヤーが行動を実行する。(例: hero.attack(enemy)
  4. 行動後、敵がまだ生きているかチェック。もし倒れていれば、戦闘はプレイヤーの勝利で終了。
  5. 敵のターン:
    • 敵が行動を実行する。(例: enemy.attack(hero)
  6. 行動後、プレイヤーがまだ生きているかチェック。もし倒れていれば、戦闘はプレイヤーの敗北で終了。
  7. ループの先頭に戻る。

この一連の流れを、`while`ループを使ってプログラムに落とし込んでいきます。


2. 戦闘を司る監督役:「Battle」クラスの導入

戦闘のロジックを、前回のキャラクターコードとごちゃ混ぜに書くのはスマートではありません。OOPの考え方に従い、戦闘の進行を専門に管理する、新しいクラス**`Battle`**を導入しましょう。このクラスが、戦闘全体の監督役(ディレクター)を務めます。

# (前回のCharacter, Player, Enemyクラスの定義はここにあると仮定)

class Battle:
    """
    戦闘の進行を管理するクラス
    """
    def __init__(self, player, enemy):
        self.player = player
        self.enemy = enemy

    def start(self):
        """戦闘ループを開始するメソッド"""
        print("\n--- BATTLE START ---")
        turn = 1

        # プレイヤーと敵の両方が生きている間、ループを続ける
        while self.player.is_alive() and self.enemy.is_alive():
            print(f"\n--- ターン {turn} ---")
            self.player.show_status()
            self.enemy.show_status()

            # --- プレイヤーのターン ---
            action = input("どうする? (1: 攻撃, 2: 逃げる): ")
            if action == '1':
                self.player.attack(self.enemy)
            elif action == '2':
                print(f"{self.player.name}は逃げ出した!")
                break  # 戦闘ループを中断して終了
            else:
                print("コマンドが認識できませんでした。")

            # 敵が倒れたら、敵のターンをスキップしてループを抜ける
            if not self.enemy.is_alive():
                break

            # --- 敵のターン ---
            print("\n敵のターン!")
            self.enemy.attack(self.player)

            turn += 1

        # --- 戦闘終了後の結果表示 ---
        print("\n--- BATTLE END ---")
        if self.player.is_alive():
            print(f"🎉 {self.player.name} は勝利した!")
        else:
            print(f"💀 {self.player.name} は敗北した...")


3. 全体コード:キャラクターと戦闘システムの統合

それでは、第1回で作成したキャラクタークラスと、今回作成した`Battle`クラスを統合し、実際にゲームを実行してみましょう。

import random
import time

# --- 第1回で作成したクラス群 ---
class Character:
    # (ここに前回のCharacterクラスのコードを全て貼り付け)
    def __init__(self, name, hp, attack_power):
        self.name = name
        self._hp = hp
        self.max_hp = hp
        self.attack_power = attack_power
    @property
    def hp(self): return self._hp
    @hp.setter
    def hp(self, new_hp): self._hp = max(0, min(new_hp, self.max_hp))
    def is_alive(self): return self.hp > 0
    def attack(self, target):
        print(f"💥 {self.name} の攻撃!")
        damage = self.attack_power
        target.take_damage(damage)
    def take_damage(self, damage):
        print(f"   {self.name} は {damage} のダメージを受けた!")
        self.hp -= damage
        if not self.is_alive(): print(f"   {self.name} は倒れた...")
    def show_status(self): print(f"[{self.name}] HP: {self.hp}/{self.max_hp}")

class Player(Character):
    pass

class Enemy(Character):
    pass

# --- 今回作成したBattleクラス ---
class Battle:
    # (ここに今回のBattleクラスのコードを全て貼り付け)
    def __init__(self, player, enemy):
        self.player = player
        self.enemy = enemy
    def start(self):
        print("\n--- BATTLE START ---")
        turn = 1
        while self.player.is_alive() and self.enemy.is_alive():
            print(f"\n--- ターン {turn} ---")
            self.player.show_status()
            self.enemy.show_status()
            action = input("どうする? (1: 攻撃, 2: 逃げる): ")
            if action == '1': self.player.attack(self.enemy)
            elif action == '2': print(f"{self.player.name}は逃げ出した!"); break
            else: print("コマンドが認識できませんでした。")
            if not self.enemy.is_alive(): break
            print("\n敵のターン!")
            time.sleep(1) # 敵の思考時間を表現
            self.enemy.attack(self.player)
            turn += 1
        print("\n--- BATTLE END ---")
        if self.player.is_alive() and not self.enemy.is_alive(): print(f"🎉 {self.player.name} は勝利した!")
        elif not self.player.is_alive(): print(f"💀 {self.player.name} は敗北した...")
        else: print("戦闘は引き分けに終わった。")


# --- ゲームの実行 ---
if __name__ == '__main__':
    # 1. キャラクターを生成する
    hero = Player("Tech Samurai", 100, 15)
    monster = Enemy("ゴブリン", 50, 8)

    # 2. 戦闘オブジェクトを生成し、戦闘を開始する
    battle = Battle(hero, monster)
    battle.start()

このスクリプトを実行すると、あなたのターミナル上で、ヒーローとゴブリンの壮絶な(?)戦いが繰り広げられます!


まとめと次回予告

今回は、テキストベースRPGの心臓部である、ターン制バトルシステムを構築しました。

  • `while`ループを使い、ゲームのメインとなる戦闘ループを実装する方法。
  • `input()`関数を使い、ループの中でプレイヤーの行動選択を受け付ける方法。
  • 戦闘ロジックを独立したBattleクラスにまとめる、オブジェクト指向的な設計アプローチ。

あなたのプログラムは、もはや単なるコードの羅列ではありません。ルール(クラス)に従って相互作用し、一つの結果(勝敗)へと収束していく、小さな「世界」になったのです。

さて、基本的な戦闘はできるようになりましたが、キャラクターも敵もまだ個性がありません。次回、いよいよシリーズ完結編では、**継承**を本格的に活用して、

  • 独自のスキルを持つ専門職「戦士」クラス
  • 少し手強い中ボス「オーク」クラス
  • 戦闘中に使える「回復薬」アイテム

などを追加し、このRPGの世界をより豊かで、より戦略的なものへと進化させていきます。お楽しみに!

コメント

タイトルとURLをコピーしました