Java

オブジェクト指向でRPGを作ってみる

更新日:

まず、VSCodeで新しくJavaプロジェクトを作成します。

public class App {
    public static void main(String[] args) throws Exception {
        System.out.println("Hello world!");
    }
}

RPGには勇者が必要なので、勇者に対応する Brave クラスを用意します。
名前を付けられるようにしておきましょう。

public class Brave {
  private String name;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

Appクラスで勇者を作って名前を付けてみます。
ついでに自己紹介もできるようにしてみます。

public class App {
    public static void main(String[] args) throws Exception {
        Brave brave = new Brave();
        brave.setName("ヨシヒコ");
        brave.introduce();
    }
}
public class Brave {
  private String name;
  private Arms arms;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void introduce() {
    System.out.println("ども、" + name + "です!!!");
  }
}

勇者が武器を持てるようにします。
武器として Arms クラスを用意します。
武器の名前はコンストラクタで決めてしまい、後から変更できないようにしておきます。

public class Arms {
  private String name;

  public Arms(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

勇者に武器を持たせて、自己紹介で武器の有無を出力するようにします。

public class Brave {
  private String name;
  private Arms arms;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void introduce() {
    System.out.println("ども、" + name + "です!!!");
    if (arms == null) {
      System.out.println("武器はまだない。");
    } else {
      System.out.println("武器 " + arms.getName() + "を装備した。");
    }
  }
  public void setArms(Arms arms) {
    this.arms = arms;
  }
}

勇者にはいろんなアイテムが必要なので、複数のアイテムを保持できるようにします。

import java.util.ArrayList;
import java.util.List;

public class Brave {
  private String name;
  private Arms arms;
  private List<Item> items = new ArrayList<>();
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setArms(Arms arms) {
    this.arms = arms;
  }
  public void addItem(Item item) {
    items.add(item);
  }

  public void introduce() {
    System.out.println("ども、" + name + "です!!!");
    if (arms == null) {
      System.out.println("武器はまだない。");
    } else {
      System.out.println("武器 " + arms.getName() + "を装備した。");
    }
    for (Item item: items) {
      System.out.println("アイテム " + item.getName() + " を持ってるぞ。");
    }
  }
}

Itemクラスを用意する。

public class Item {
  private String name;
  public Item(String name) {
    this.name = name;
  }
  public String getName() {
    return name;
  }
}

Appクラスで勇者にアイテムを持たせてみる。

public class App {
    public static void main(String[] args) throws Exception {
        Brave brave = new Brave();
        brave.setName("ヨシヒコ");
        brave.introduce();
        Arms arms = new Arms("ひのきの棒");
        brave.setArms(arms);
        brave.introduce();
        Item item = new Item("やくそう");
        brave.addItem(item);
        item = new Item("やくそう");
        brave.addItem(item);
        brave.introduce();
    }
}

ターミナルには、以下の内容が出力されます。

PS Z:\rpg> java -cp bin App
ども、ヨシヒコです!!!
武器はまだない。
ども、ヨシヒコです!!!
武器 ひのきの棒を装備した。
ども、ヨシヒコです!!!
武器 ひのきの棒を装備した。
アイテム やくそう を持ってるぞ。
アイテム やくそう を持ってるぞ。

RPGで使われるアイテムにはいろいろな種類があり、使った時の効果も異なるので、Itemクラスですべてを実装するのは難しくなります。

このような場合にオブジェクト指向では継承を使用します。

「やくそう」に対応するクラスとしてHerbを作成してみましょう。

public class Herb extends Item {
  public Herb(String name) {
    super(name);
    System.out.println("Herb constructor");
  }
}

super()でスーパークラスのコンストラクタを呼び出しています。

呼び出し順などを確認するために、標準出力にメッセージを表示するコードも追加してみましょう。

public class Item {
  private String name;
  public Item(String name) {
    this.name = name;
    System.out.println("Item constructor");
  }
  public String getName() {
    return name;
  }
}

App.java の「やくそう」をnewしている部分をItemからHerbに変更します。

public class App {
    public static void main(String[] args) throws Exception {
        Brave brave = new Brave();
        brave.setName("ヨシヒコ");
        brave.introduce();
        Arms arms = new Arms("ひのきの棒");
        brave.setArms(arms);
        brave.introduce();
        Item item = new Herb("やくそう");
        brave.addItem(item);
        item = new Herb("やくそう");
        brave.addItem(item);
        brave.introduce();
    }
}

Itemクラスには、アイテムを使った時に何かの効果があるはずなので、effect()メソッドを追加してみます。

public class Item {
  private String name;
  public Item(String name) {
    this.name = name;
    System.out.println("Item constructor");
  }
  public String getName() {
    return name;
  }

  public void effect() {
    
  }
}

HerbクラスではItemクラスのeffect()メソッドを再定義します。
これをメソッドのオーバーライドと呼びます。

public class Herb extends Item {
  public Herb(String name) {
    super(name);
    System.out.println("Herb constructor");
  }
  @Override
  public void effect() {
    System.out.println("HPが10回復した。");
  }
}

勇者がアイテムを使うための動作を実装します。
BraveクラスにuseItem()メソッドを追加します。

import java.util.ArrayList;
import java.util.List;

public class Brave {
  private int hp = 10;
  private String name;
  private Arms arms;
  private List<Item> items = new ArrayList<>();
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setArms(Arms arms) {
    this.arms = arms;
  }
  public void addItem(Item item) {
    items.add(item);
  }

  public void introduce() {
    System.out.println("ども、" + name + "です!!!");
    if (arms == null) {
      System.out.println("武器はまだない。");
    } else {
      System.out.println("武器 " + arms.getName() + "を装備した。");
    }
    for (Item item: items) {
      System.out.println("アイテム " + item.getName() + " を持ってるぞ。");
    }
  }

  public void useItem(Item item) {
    System.out.println(item.getName() + "を使った。");
    item.effect();
  }
}

App.javaに勇者がやくそうを使う処理を追加します。

public class App {
    public static void main(String[] args) throws Exception {
        Brave brave = new Brave();
        brave.setName("ヨシヒコ");
        brave.introduce();
        Arms arms = new Arms("ひのきの棒");
        brave.setArms(arms);
        brave.introduce();
        Item item = new Herb("やくそう");
        brave.addItem(item);
        item = new Herb("やくそう");
        brave.addItem(item);
        brave.introduce();
        brave.useItem(item);
    }
}

「やくそう」よりも効果がある「上やくそう」を作ってみます。

public class SuperHerb extends Item {
  public SuperHerb(String name) {
    super(name);
  }

  @Override
  public void effect() {
    System.out.println("HPが30回復した。");
  }
}

「上やくそう」を勇者に持たせて使ってみましょう。

public class App {
    public static void main(String[] args) throws Exception {
        Brave brave = new Brave();
        brave.setName("ヨシヒコ");
        brave.introduce();
        Arms arms = new Arms("ひのきの棒");
        brave.setArms(arms);
        brave.introduce();
        Item item = new Herb("やくそう");
        brave.addItem(item);
        item = new Herb("やくそう");
        brave.addItem(item);
        brave.introduce();
        brave.useItem(item);
        item = new SuperHerb("上やくそう");
        brave.introduce();
        brave.useItem(item);
    }
}

「やくそう」や「上やくそう」などのアイテムの名前はクラスごとに決まるので、App.javaで名前を引数で渡すのはやめて、各アイテムのコンストラクタで名前を指定するようにしましょう。

public class Herb extends Item {
  public Herb() {
    super("やくそう");
    System.out.println("Herb constructor");
  }
  @Override
  public void effect(Brave brave) {
    System.out.println("HPが10回復した。");
  }
}
public class SuperHerb extends Item {
  public SuperHerb() {
    super("上やくそう");
  }

  @Override
  public void effect(Brave brave) {
    System.out.println("HPが30回復した。");
  }
}
public class App {
    public static void main(String[] args) throws Exception {
        Brave brave = new Brave();
        brave.setName("ヨシヒコ");
        brave.introduce();
        Arms arms = new Arms("ひのきの棒");
        brave.setArms(arms);
        brave.introduce();
        Item item = new Herb();
        brave.addItem(item);
        item = new Herb();
        brave.addItem(item);
        brave.introduce();
        brave.useItem(item);
        item = new SuperHerb();
        brave.introduce();
        brave.useItem(item);
    }
}

次は、「やくそう」や「上やくそう」が勇者のHPを回復する実装を追加してみます。

BraveクラスにHPを回復する recoverHp()メソッドを追加します。

import java.util.ArrayList;
import java.util.List;

public class Brave {
  private int hp = 10;
  private String name;
  private Arms arms;
  private List<Item> items = new ArrayList<>();
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setArms(Arms arms) {
    this.arms = arms;
  }
  public void addItem(Item item) {
    items.add(item);
  }

  public void introduce() {
    System.out.println("ども、" + name + "です!!!");
    if (arms == null) {
      System.out.println("武器はまだない。");
    } else {
      System.out.println("武器 " + arms.getName() + "を装備した。");
    }
    for (Item item: items) {
      System.out.println("アイテム " + item.getName() + " を持ってるぞ。");
    }
  }

  public void useItem(Item item) {
    System.out.println(item.getName() + "を使った。");
    item.effect(this);
  }

  public void recoverHp(int hp) {
    this.hp += hp;
  }
}

Itemのeffect()メソッドには、効果の対象となる勇者を引数で渡せるように変更します。

public class Item {
  private String name;
  public Item(String name) {
    this.name = name;
    System.out.println("Item constructor");
  }
  public String getName() {
    return name;
  }

  public void effect(Brave brave) {

  }
}

HerbとSuperHerbのeffect()メソッドを修正して、勇者のHPを回復するようにします。

public class Herb extends Item {
  public Herb() {
    super("やくそう");
    System.out.println("Herb constructor");
  }
  @Override
  public void effect(Brave brave) {
    System.out.println("HPが10回復した。");
    brave.recoverHp(10);
  }
}
public class SuperHerb extends Item {
  public SuperHerb() {
    super("上やくそう");
  }

  @Override
  public void effect(Brave brave) {
    System.out.println("HPが30回復した。");
    brave.recoverHp(30);
  }
}

次は、MPを回復させるアイテム「せいすい」を作ってみましょう。
クラス名は HolyWater にします。

public class HolyWater extends Item {
  public HolyWater() {
    super("せいすい");
  }
}

勇者のMPを回復するためのメソッドを追加します。

import java.util.ArrayList;
import java.util.List;

public class Brave {
  private int hp = 10;
  private int mp = 10;
  private String name;
  private Arms arms;
  private List<Item> items = new ArrayList<>();
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setArms(Arms arms) {
    this.arms = arms;
  }
  public void addItem(Item item) {
    items.add(item);
  }

  public void introduce() {
    System.out.println("ども、" + name + "です!!!");
    System.out.println("HP:" + hp + " MP:" + mp);
    if (arms == null) {
      System.out.println("武器はまだない。");
    } else {
      System.out.println("武器 " + arms.getName() + "を装備した。");
    }
    for (Item item: items) {
      System.out.println("アイテム " + item.getName() + " を持ってるぞ。");
    }
  }

  public void useItem(Item item) {
    System.out.println(item.getName() + "を使った。");
    item.effect(this);
  }

  public void recoverHp(int hp) {
    this.hp += hp;
  }
  
  public void recoverMp(int mp) {
    this.mp += mp;
  }
}

「せいすい」の効果として、勇者のMPを回復する処理を追加します。

public class HolyWater extends Item {
  public HolyWater() {
    super("せいすい");
  }
  public void effect(Brave brave) {
    brave.recoverMp(10);
  }
}

App.java に「せいすい」を追加して、勇者に「せいすい」を使うようにします。

public class App {
    public static void main(String[] args) throws Exception {
        Brave brave = new Brave();
        brave.setName("ヨシヒコ");
        brave.introduce();
        Arms arms = new Arms("ひのきの棒");
        brave.setArms(arms);
        brave.introduce();
        Item item = new Herb();
        brave.addItem(item);
        item = new Herb();
        brave.addItem(item);
        brave.introduce();
        brave.useItem(item);
        item = new SuperHerb();
        brave.introduce();
        brave.useItem(item);
        brave.introduce();
        item = new HolyWater();
        brave.useItem(item);
        brave.introduce();
    }
}

アイテムを持っていないと使えないようにします。そして、アイテムを使うと消費されて持ち物からなくなるようにします。

import java.util.ArrayList;
import java.util.List;

public class Brave {
  private int hp = 10;
  private int mp = 10;
  private String name;
  private Arms arms;
  private List<Item> items = new ArrayList<>();
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setArms(Arms arms) {
    this.arms = arms;
  }
  public void addItem(Item item) {
    items.add(item);
  }

  public void introduce() {
    System.out.println("ども、" + name + "です!!!");
    System.out.println("HP:" + hp + " MP:" + mp);
    if (arms == null) {
      System.out.println("武器はまだない。");
    } else {
      System.out.println("武器 " + arms.getName() + "を装備した。");
    }
    for (Item item: items) {
      System.out.println("アイテム " + item.getName() + " を持ってるぞ。");
    }
  }

  public void useItem(Item item) {
    if (items.contains(item)) {
      System.out.println(item.getName() + "を使った。");
      item.effect(this);
      items.remove(item);
    } else {
      System.out.println(item.getName() + "を持ってなかった!");
    }
  }

  public void recoverHp(int hp) {
    this.hp += hp;
  }
  
  public void recoverMp(int mp) {
    this.mp += mp;
  }
}

勇者の introduce() の出力が読みにくいので、少し整理してみます。

import java.util.ArrayList;
import java.util.List;

public class Brave {
  private int hp = 10;
  private int mp = 10;
  private String name;
  private Arms arms;
  private List<Item> items = new ArrayList<>();
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setArms(Arms arms) {
    this.arms = arms;
  }
  public void addItem(Item item) {
    items.add(item);
  }

  public void introduce() {
    System.out.println();
    System.out.println("名前:" + name);
    System.out.println("HP:" + hp + " MP:" + mp);
    if (arms == null) {
      System.out.println("武器: なし");
    } else {
      System.out.println("武器:" + arms.getName());
    }
    System.out.println("アイテム:");
    for (Item item: items) {
      System.out.println(item.getName());
    }
  }

  public void useItem(Item item) {
    if (items.contains(item)) {
      System.out.println(item.getName() + "を使った。");
      item.effect(this);
      items.remove(item);
    } else {
      System.out.println(item.getName() + "を持ってなかった!");
    }
  }

  public void recoverHp(int hp) {
    this.hp += hp;
  }
  
  public void recoverMp(int mp) {
    this.mp += mp;
  }
}

勇者にアイテムを持たせる部分をApp.javaの最初のほうにまとめてみます。

public class App {
    public static void main(String[] args) throws Exception {
        Brave brave = new Brave();
        brave.setName("ヨシヒコ");
        brave.introduce();
        Arms arms = new Arms("ひのきの棒");
        brave.setArms(arms);
        Item item = new Herb();
        brave.addItem(item);
        item = new Herb();
        brave.addItem(item);
        item = new SuperHerb();
        brave.addItem(item);
        item = new HolyWater();
        brave.addItem(item);
        brave.introduce();

        brave.useItem(item);
        brave.introduce();
        brave.useItem(item);
        brave.introduce();
        brave.useItem(item);
        brave.introduce();
    }
}

item 変数の値が最後の「せいすい」のままなので、他のアイテムを使えなくなりました。
なので、他のアイテムを使えるようにするために、勇者の持ち物からアイテム名で取り出せるようにします。

import java.util.ArrayList;
import java.util.List;

public class Brave {
  private int hp = 10;
  private int mp = 10;
  private String name;
  private Arms arms;
  private List<Item> items = new ArrayList<>();
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setArms(Arms arms) {
    this.arms = arms;
  }
  public void addItem(Item item) {
    items.add(item);
  }

  public void introduce() {
    System.out.println();
    System.out.println("名前:" + name);
    System.out.println("HP:" + hp + " MP:" + mp);
    if (arms == null) {
      System.out.println("武器: なし");
    } else {
      System.out.println("武器:" + arms.getName());
    }
    System.out.println("アイテム:");
    for (Item item: items) {
      System.out.println(item.getName());
    }
  }

  public Item getItem(String name) {
    for (Item item : items) {
      if (item.getName().equals(name)) return item;
    }
    return null;
  }
  
  public void useItem(Item item) {
    if (items.contains(item)) {
      System.out.println(item.getName() + "を使った。");
      item.effect(this);
      items.remove(item);
    } else {
      System.out.println(item.getName() + "を持ってなかった!");
    }
  }

  public void recoverHp(int hp) {
    this.hp += hp;
  }
  
  public void recoverMp(int mp) {
    this.mp += mp;
  }
}

App.java では、アイテムを使う前に、勇者の持ち物からアイテム名を指定してアイテムを取得して使うようにします。
ゲームの画面上で持ち物リストからアイテムの名前を探し出して、それを使うイメージですね。

public class App {
    public static void main(String[] args) throws Exception {
        Brave brave = new Brave();
        brave.setName("ヨシヒコ");
        brave.introduce();
        Arms arms = new Arms("ひのきの棒");
        brave.setArms(arms);
        Item item = new Herb();
        brave.addItem(item);
        item = new Herb();
        brave.addItem(item);
        item = new SuperHerb();
        brave.addItem(item);
        item = new HolyWater();
        brave.addItem(item);
        brave.introduce();

        item = brave.getItem("やくそう");
        brave.useItem(item);
        brave.introduce();
        item = brave.getItem("上やくそう");
        brave.useItem(item);
        brave.introduce();
        item = brave.getItem("せいすい");
        brave.useItem(item);
        brave.introduce();
    }
}

勇者のHPやMPを回復するときに、HPやMPが最大値になっていたらアイテムを使えないようにします。

import java.util.ArrayList;
import java.util.List;

public class Brave {
  private int hp = 10;
  private int mp = 10;
  private String name;
  private Arms arms;
  private List<Item> items = new ArrayList<>();
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public void setArms(Arms arms) {
    this.arms = arms;
  }
  public void addItem(Item item) {
    items.add(item);
  }

  private int maxHp() {
    return 20;
  }

  public boolean isMaxHp() {
    return maxHp() == hp;
  }

  private int maxMp() {
    return 20;
  }

  public boolean isMaxMp() {
    return maxMp() == mp;
  }

  public void introduce() {
    System.out.println();
    System.out.println("名前:" + name);
    System.out.println("HP:" + hp + " MP:" + mp);
    if (arms == null) {
      System.out.println("武器: なし");
    } else {
      System.out.println("武器:" + arms.getName());
    }
    System.out.println("アイテム:");
    for (Item item: items) {
      System.out.println(item.getName());
    }
  }

  public Item getItem(String name) {
    for (Item item : items) {
      if (item.getName().equals(name)) return item;
    }
    return null;
  }

  public boolean isUsable(Item item) {
    return item.isUsable(this);
  }

  public void useItem(Item item) {
    if (!isUsable(item)) {
      System.out.println(item.getName() + "は、使えません。");
      return;
    }
    if (items.contains(item)) {
      System.out.println(item.getName() + "を使った。");
      item.effect(this);
      items.remove(item);
    } else {
      System.out.println(item.getName() + "を持ってなかった!");
    }
  }

  public void recoverHp(int hp) {
    this.hp += hp;
    if(this.hp > maxHp()) this.hp = maxHp();
  }
  
  public void recoverMp(int mp) {
    this.mp += mp;
    if(this.mp > maxMp()) this.mp = maxMp();
  }

}

Itemにも、使えるかどうかを調べるメソッドを用意します。

public class Item {
  private String name;
  public Item(String name) {
    this.name = name;
  }
  public String getName() {
    return name;
  }

  public void effect(Brave brave) {

  }

  public boolean isUsable(Brave brave) {
    return true;
  }
}

各アイテムで、isUsabel()をオーバーライドします。

public class Herb extends Item {
  public Herb() {
    super("やくそう");
  }
  @Override
  public void effect(Brave brave) {
    brave.recoverHp(10);
  }
  @Override
  public boolean isUsable(Brave brave) {
    return !brave.isMaxHp();
  }
}
public class SuperHerb extends Item {
  public SuperHerb() {
    super("上やくそう");
  }

  @Override
  public void effect(Brave brave) {
    brave.recoverHp(30);
  }
  @Override
  public boolean isUsable(Brave brave) {
    return !brave.isMaxHp();
  }
}
public class HolyWater extends Item {
  public HolyWater() {
    super("せいすい");
  }
  public void effect(Brave brave) {
    brave.recoverMp(10);
  }
  @Override
  public boolean isUsable(Brave brave) {
    return !brave.isMaxMp();
  }
}

-Java
-, ,

Copyright© UMLとJavaで学ぶオブジェクト指向プログラミング入門 , 2024 All Rights Reserved Powered by STINGER.