【実践OOPプロジェクト #1】PythonでテキストRPG開発!キャラクタークラスの設計と実装

ALL

こんにちは、Tech Samuraiです!
これまでの冒険で、私たちはオブジェクト指向プログラミング(OOP)の三大要素、クラス、継承、カプセル化という強力な武器を手に入れました。さあ、その全ての武器を手に、壮大なプロジェクトに挑戦する時が来ました!

今回から始まる新シリーズでは、Pythonを使って、シンプルなテキストベースのRPG(ロールプレイングゲーム)をゼロから開発していきます。

このプロジェクトは、これまでのOOPの知識を総動員する最高の舞台です。自分で設計したクラス(設計図)からキャラクター(オブジェクト)を生み出し、彼らが互いに影響を与え合い、一つの物語を紡いでいく… そんな創造の喜びを、ぜひ一緒に体験しましょう。

記念すべき第1回は、この世界の基礎となる**キャラクターの設計図(クラス)**を構築します。英雄もモンスターも、ここから生まれるのです!


1. ゲームの設計図:クラス階層を考える

いきなりコードを書き始める前に、どのようなクラスが必要で、それらがどのような関係にあるのか、設計図を描きましょう。今回は、**継承**の考え方を最大限に活かします。

クラス階層図:

Character(全てのキャラクターの親クラス)
 ├── Player(プレイヤーキャラクター)
 │    └── Warrior(戦士などの専門職)
 └── Enemy(敵キャラクター)
      └── Slime(スライムなどの具体例)
  • まず、プレイヤーにも敵にも共通する能力(名前、HP、攻撃力、攻撃する、ダメージを受けるなど)を持つ、大元となる**Characterクラス**を親として定義します。
  • 次に、Characterクラスを継承して、プレイヤー専用の**Playerクラス**と、敵専用の**Enemyクラス**を作ります。
  • 将来的には、さらにPlayerを継承して専門職を作ったり、Enemyを継承して様々なモンスターを作ったりと、世界を拡張していきます。

この階層構造が、私たちのRPGの骨格となります。


2. すべての基礎:「Character」親クラスの実装

まずは、全てのキャラクターの基礎となるCharacterクラスを実装します。ここでは、HPがマイナスにならないように**カプセル化**の知識も活用します。

import random

class Character:
    """
    全てのキャラクターの基礎となる親クラス
    """
    def __init__(self, name, hp, attack_power):
        self.name = name
        self._hp = hp  # HPは内部変数として扱う(カプセル化)
        self.max_hp = hp # 最大HPを記憶しておく
        self.attack_power = attack_power

    # @property を使って、HPの読み取りを簡単にする(ゲッター)
    @property
    def hp(self):
        return self._hp

    # HPへの書き込みを制御する(セッター)
    @hp.setter
    def hp(self, new_hp):
        # HPは0未満にも、最大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}")

ポイント:
HPを@property@hp.setterを使って実装することで、player.hp = 100のように自然に値を設定しつつも、裏側では「HPが0未満にならない」という重要なルールを自動で守らせることができます。これぞカプセル化の力です。


3. 継承で作る「Player」と「Enemy」クラス

基礎となるCharacterクラスができたので、それを継承してPlayerEnemyクラスを作るのは、驚くほど簡単です。

class Player(Character):
    """
    プレイヤーキャラクターのクラス
    """
    # 今は特別な機能はないので、passで中身は空にしておく
    # しかし、Characterクラスの能力は全て引き継いでいる!
    pass

class Enemy(Character):
    """
    敵キャラクターのクラス
    """
    # 同じく、今は特別な機能はない
    pass

たったこれだけです! 中身は空っぽですが、この2つのクラスは、親であるCharacterが持つ全ての属性とメソッドを、まるごと受け継いでいます。


4. 命を吹き込む:オブジェクトの生成とテスト

それでは、作成したクラス(設計図)から、キャラクター(オブジェクト)を生成して、簡単なやり取りをさせてみましょう。

# --- メインの処理 ---
# キャラクターを生成
hero = Player("Tech Samurai", 100, 15)
slime = Enemy("スライム", 30, 5)

print("冒険の始まりだ!")
hero.show_status()
slime.show_status()

print("\n--- BATTLE START ---")

# ヒーローがスライムを攻撃
hero.attack(slime)

print("-" * 20)
hero.show_status()
slime.show_status() # HPが減っているはず!

print("\n--- ENEMY'S TURN ---")

# スライムがヒーローを攻撃
slime.attack(hero)

print("-" * 20)
hero.show_status() # HPが減っているはず!
slime.show_status()

実行結果(例):

冒険の始まりだ!
[Tech Samurai] HP: 100/100
[スライム] HP: 30/30

--- BATTLE START ---
💥 Tech Samurai の攻撃!
   スライム は 15 のダメージを受けた!
--------------------
[Tech Samurai] HP: 100/100
[スライム] HP: 15/30

--- ENEMY'S TURN ---
💥 スライム の攻撃!
   Tech Samurai は 5 のダメージを受けた!
--------------------
[Tech Samurai] HP: 95/100
[スライム] HP: 15/30

完璧です! これで、私たちのRPGの世界の住人たちが生まれ、互いに影響を与え合う準備が整いました。


まとめと次回予告

今回は、実践OOPプロジェクトとしてテキストベースRPGの開発を開始し、その土台となるキャラクタークラスを設計・実装しました。

  • RPGの世界観を、**クラスの階層構造**として設計する方法。
  • 全てのキャラクターの共通機能を持つ、**親クラス `Character`** の実装。
  • HP管理に**カプセル化(@property)**を使い、データの安全性を高める方法。
  • **継承**を使い、PlayerEnemyクラスを効率的に作成する方法。

私たちの手で、RPGの登場人物たちが生まれました。しかし、彼らはまだ、私たちが手動で動かしているだけの人形にすぎません。

次回、第2回では、いよいよゲームの心臓部である**「ターン制の戦闘ループ」**を実装します! プレイヤーと敵が交互に自動で攻撃し合い、どちらかのHPが0になるまで戦いが続く… そんな、手に汗握るバトルシステムを構築していきましょう。お楽しみに!

コメント

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