Knowledge Log

【Java実践】オブジェクト指向を総動員!RPGの「戦闘システム」を設計してみよう

denson
1

はじめに

みなさんどうも、おげんです。

これまで、「カプセル化」「継承」「ポリモーフィズム」「インターフェース」「抽象クラス」と、オブジェクト指向の強力な武器を一つずつ手に入れてきました。

でも、正直こう思っていませんか?

「理屈はわかった。でも、結局これらをどう組み合わせて使えばいいの?」

武器は持っているだけでは意味がありません。

実際にそれらを使って、一つのシステムを組み上げてこそ真価を発揮します。

そこで今回は、これまでの知識を総動員して、RPGの「戦闘システム」をどう設計するかを考えてみます!

バラバラだった知識が一つに繋がる感覚を、ぜひ一緒に体験してみてください。

この記事はこんな人におすすめ!
  • オブジェクト指向の用語は覚えたけれど、使い道に迷っている人
  • 「綺麗なコード」「修正しやすい設計」がどんなものか具体的に知りたい人
  • プログラミングの「点と点が線で繋がる」瞬間を味わいたい人
  • 自分でも簡単なゲームやシステムを作ってみたいと思っている人
2

登場人物の役割分担(設計図)

RPGの戦闘シーンを作る際、ただ闇雲にコードを書くのではなく、手に入れた武器を適材適所で配置していきます。

  • 抽象クラス(Character): 全てのキャラの「骨格」です。「名前」や「HP」といった共通データと、「攻撃せよ(attack)」という絶対のルールを決めます。
  • 継承(Warrior / Wizard): 具体的な「個性」です。親の骨格を引き継ぎつつ、「剣で斬る」「魔法を唱える」といった具体的な中身を書き込みます。
  • インターフェース(Healable): 特定のキャラだけの「特殊スキル」です。職業に関係なく「回復ができる」という資格を、必要なキャラにだけセットします。
  • カプセル化: キャラの「命綱」です。HPが勝手にマイナスになったりしないよう、セッター(setter)でしっかりガードします。

なぜわざわざ分けるのか?

もしこれらを分けずに、一つのクラスに全部詰め込んだらどうなるでしょう?

「もし戦士なら剣で、もし魔法使いなら魔法で、もし回復持ちなら回復して…」と、if文だらけの巨大なコードになってしまいます。

役割を分けることで、「自分の仕事だけに集中するクラス」が集まった、スッキリしたチームが出来上がるんです。

3

コードで見る「連携」の凄さ

実際にこの設計をコードに落とし込むと、メインの戦闘処理は驚くほどスッキリします。

「コードを書くときは、まず『何が必要か(抽象)』を定義してから、『具体的な中身』を作っていくのがコツです。」

まず連携するためのそれぞれのクラスを書きます。

// 1. 土台となる抽象クラス
abstract class Character {
    protected String name; // カプセル化(継承先からは見える)
    public Character(String name) { 
     this.name = name; 
    }
    abstract void attack(); // 攻撃のルールだけ決める
}

// 2. 回復スキルの免許(インターフェース)
interface Healable {
    void heal();
}

// 3. 具体的なジョブ(継承 + 実装)
class Warrior extends Character {
    public Warrior(String name) { 
    super(name); 
    }

    @Override 
    void attack() { 
     System.out.println(name + "の剣攻撃!"); 
    }
}

class Wizard extends Character implements Healable {
    public Wizard(String name) { 
     super(name); 
    }
    @Override 
    void attack() {
     System.out.println(name + "の魔法攻撃!"); 
    }

    @Override public void heal() { 
     System.out.println(name + "は呪文で回復した!"); 
    }
}

次に戦闘処理のコードを書きます。

ポイントは、「相手の正体が誰であれ、共通の型(Character)として扱う」ことです。

// 全員を「Character」のリストにまとめる(ポリモーフィズム)
List<Character> party = new ArrayList<>();
party.add(new Warrior("おげん")); // 戦士を追加
party.add(new Wizard("アリス"));  // 魔法使いを追加

// たったこれだけのループで、全員に一斉指示!
for (Character c : party) {
    // 1. 共通の攻撃指示
    c.attack(); // 中身が戦士なら剣、魔法使いなら魔法が勝手に発動する!

    // 2. 特殊スキルの確認(インターフェースの活用)
    if (c instanceof Healable) {
        ((Healable) c).heal(); // 回復資格がある人だけ、ついでに回復
    }
}

指示を出す側(メイン処理)のコードを見てください。

「戦士のときは〜」「魔法使いのときは〜」というif文が一つもありません。

「君たちが誰であれ、とにかく Character なんだから attack() はできるよね。さあやって!」と丸投げするだけで、各キャラクターが自分の個性を発揮して動いてくれます。

これが、オブジェクト指向を学んだ人だけが書ける、スマートで美しい連携プレイです。

4

設計を分けるメリット

「わざわざクラスを分けるなんて、最初は面倒だな……」と思うかもしれません。

でも、この設計は後からじわじわと、「過去の自分、ナイス!」と思わせてくれるメリットがあります。

1. 「追加」にめちゃくちゃ強い

新しく「武闘家」や「忍者」をパーティに追加したくなったと想像してください。

  • ダメな例: メインの戦闘処理に if (忍者なら) { ... } を追加して、全コードを書き直す。
  • 今回の設計: Character を継承した「忍者クラス」を新しく作るだけ。

なんと、メインの戦闘処理(for文)は1行も触らなくていいんです。

すでにある完成したコードを壊すリスクがゼロなのは、開発において最大の安心材料になります。

2. バグが「迷子」にならない

もし「魔法のダメージ計算がおかしい」というバグが見つかったら、どこを直せばいいでしょうか?

答えは、Wizard クラスの中だけです。

「戦士のコードをいじってたら、なぜか魔法使いまでバグった……」という、プログラミングあるあるの悲劇を防げます。

「ここを直せばここが治る」とハッキリわかるのが、良い設計の証拠です。

5

まとめ:道具を使いこなす楽しさ

今回は、これまで学んだ知識を総動員して、一つのシステム(戦闘シーン)を設計してみました。

最初は「カプセル化」や「ポリモーフィズム」といった難しい名前に戸惑ったかもしれません。

でも、それらが組み合わさったとき、バラバラだったパーツがカチッとはまって動き出す。

この瞬間こそが、プログラミングで最もワクワクする場面の一つです。

  • カプセル化で守り、
  • 継承・抽象クラスで土台を作り、
  • インターフェースで可能性を広げ、
  • ポリモーフィズムで自在に操る。

これらを使いこなすことで、あなたのコードは「ただ動くもの」から、「変化に強く、誰が見ても美しいシステム」へと進化します。

オブジェクト指向という強力な武器を手に入れたあなたなら、もうどんな複雑な世界も一歩ずつ形にしていけるはずです。

次は、あなただけの理想の「村」や「システム」を、自分の手で組み立ててみてくださいね!

今回も最後まで読んでいただき、ありがとうございました!

ABOUT ME
おげん
おげん
駆け出しエンジニア
文系未経験からエンジニアになるために挑戦を開始したアラサー。転職活動を終え、2026年4月からIT企業で勤務予定。自分が学習で苦労した経験を『誰かのための道しるべ』として発信中。
記事URLをコピーしました