ゲームキャラクター開発
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 90/91。
チャレンジ
簡単異なるキャラクタークラスが独自の能力を使って戦う、ゲームキャラクター開発システムを構築しましょう。継承、インターフェース、トレイト、そしてカプセル化がゲームの文脈でどのように連携するかを示すために、経験値とレベルアップシステムを備えたキャラクターの階層構造を作成します。
コードは以下の6つのファイルに分けて構成します:
Ability.php— 2つのメソッドgetName(): stringとuse(Character $target): stringを持つAbilityインターフェースを定義します。この規約により、具体的な効果に関わらず、すべての能力を一貫して使用できるようになります。CombatLogger.php—logAction(string $action): stringメソッドを提供するCombatLoggerトレイトを作成します。このメソッドは、アクション文字列の先頭に"[COMBAT] "を付けて返す必要があります。キャラクターはこのトレイトを使用して、戦闘メッセージを統一された形式で出力します。Character.php— AbilityとCombatLoggerのファイルを取り込みます。CombatLoggerトレイトを使用する抽象クラスCharacterを作成します。name(string, public readonly) にはコンストラクタのプロパティ昇格を使用します。health(int, 初期値 100)、level(int, 初期値 1)、experience(int, 初期値 0) のプライベートプロパティを含めます。以下を実装してください:getHealth(): intおよびgetLevel(): int— プライベートプロパティのゲッターtakeDamage(int $amount): void— 体力を減らしますが、0未満にはなりませんheal(int $amount): void— 体力を増やしますが、100を超えませんgainExperience(int $xp): string— XPを加算します。経験値が100以上に達した場合はレベルアップし(XPを0にリセット、レベルを上げ、HPを20回復)、"[name] leveled up to [level]!"を返します。それ以外の場合は"[name] gained [xp] XP"を返します。isAlive(): bool— 体力が0より大きい場合に true を返しますabstract getSpecialAbility(): Ability— 各キャラクタータイプが独自の能力を提供しますattack(Character $target): string— ターゲットに10のダメージを与え、logAction()を使用して"[COMBAT] [name] attacks [target name] for 10 damage"を返します
Warrior.php— Characterファイルを取り込みます。Characterを継承したWarriorクラスを作成します。getSpecialAbility()を実装し、Abilityを実装する匿名クラスを返します:getName()は"Shield Bash"を返しますuse()は25のダメージを与え、"[COMBAT] Shield Bash hits [target name] for 25 damage!"を返します(トレイトのlogAction()を使用してください)
Mage.php— Characterファイルを取り込みます。Characterを継承したMageクラスを作成します。getSpecialAbility()を実装し、Abilityを実装する匿名クラスを返します:getName()は"Fireball"を返しますuse()は35のダメージを与え、"[COMBAT] Fireball engulfs [target name] for 35 damage!"を返します
main.php— WarriorとMageのファイルを取り込みます。キャラクターデータ (JSON) と戦闘アクションのシーケンス (JSON) の2つの入力を受け取ります。キャラクターのJSON形式:
[{"type": "warrior", "name": "Thorin"}, {"type": "mage", "name": "Gandalf"}]アクションのJSON形式:
[{"action": "attack", "attacker": 0, "target": 1}, {"action": "special", "attacker": 1, "target": 0}, {"action": "xp", "character": 0, "amount": 100}, {"action": "status", "character": 0}]すべてのキャラクターを作成し、配列(インデックス 0, 1 など)に保存します。各アクションを処理します:
"attack"— attackerインデックスのキャラクターがターゲットを攻撃します。結果を出力します。"special"— キャラクターがターゲットに対して特殊能力を使用します。結果を出力します。"xp"— キャラクターが経験値を獲得します。結果を出力します。"heal"— キャラクターが指定された量だけ回復します。"[name] healed to [health] HP"と出力します。"status"—"[name] - Level [level], HP: [health], Alive: [Yes/No]"と出力します。
各結果を新しい行に出力してください。
このシステムは、抽象クラスが共有のキャラクター動作を定義し、インターフェースが能力の一貫した規約を保証し、トレイトが再利用可能なロギング機能を提供し、匿名クラスが使い捨てのクラスでコードベースを散らかすことなく独自の能力実装を作成する方法を示しています。
自分で試してみよう
<?php
require_once 'Warrior.php';
require_once 'Mage.php';
// 入力を読み込む
$charactersJson = trim(fgets(STDIN));
$actionsJson = trim(fgets(STDIN));
// JSON入力をパースする
$charactersData = (array)json_decode($charactersJson, true);
$actionsData = (array)json_decode($actionsJson, true);
// TODO: キャラクターオブジェクトを格納するための配列を作成する
$characters = [];
// TODO: $charactersDataをループし、"type"フィールドに基づいて
// WarriorまたはMageオブジェクトを作成し、$characters配列に格納する
// TODO: $actionsData内の各アクションを処理する
// 以下の各アクションタイプを処理すること:
// - "attack": 攻撃者インデックスのキャラクターがターゲットを攻撃し、結果を表示する
// - "special": キャラクターがターゲットに対してgetSpecialAbility()->use()を使用し、結果を表示する
// - "xp": キャラクターが経験値を獲得し、結果を表示する
// - "heal": キャラクターが回復し、"[name] healed to [health] HP"を表示する
// - "status": "[name] - Level [level], HP: [health], Alive: [Yes/No]"を表示する
?>