質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.38%

  • Java

    16504questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

テキストアドベンチャーゲームを作っているのですが…

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 221

Neru_K

score 5

Javaの学習のため、CUIによるテキストアドベンチャーを作っています。
現在学習開始から一ヶ月ほどで、基本的な文法と、クラスや継承、カプセル化、ポリフォーミズムなどを学びました。
(基本的な理解は一応したつもりですが、もちろんまだ使いこなせるレベルではないです)

テキストアドベンチャーはいわゆる美少女ゲーム的な内容にしようと思い、Mainクラスでゲームの主なループをさせつつ(このクラス内にmainメソッドがあります)、他に主人公やヒロインのクラスを作っていくという設計を考えました。

仮に主人公クラスにTaro、ヒロインクラスにHanako、Yoshikoを作るとします。
ヒロインは随時増やすという体で、オブジェクト指向の練習も兼ねてHeroineというクラスをそれぞれに継承させて作ることにしました。
Heroineクラスは以下↓のようなものです。

該当のソースコード

public class Heroine{
  private String name;
  private int koukando;

  public void koukandoUp(){
    koukando++;
  }

  public void koukandoDown(){
    koukando--;
  }

    public int getKoukando(){
    return this.koukando;
  }

  public String getName(){
    return this.name;
  }

  public void setName(String name){
    this.name = name;
  }

  //-------------コンストラクタで初期化-----------
  Heroine(){
      this.koukando = 10;
  }
}

発生した問題

Hanako,Yoshikoに上記のHeroineクラスを継承させたうえで、MainクラスとTaroクラスのそれぞれで好感度パラメーターを受け取ろうとして、やり方がわからなくなりました。
(※下部「追記」にて詳細を書き加えました)

getKoukando();を使うには、まずHanakoやYoshikoをインスタンス化しないといけませんよね?
ですがMainクラスとTaroクラスでそれぞれにHanako hanako = new Hanako();としてしまうと、作られた2つのhanakoインスタンスは全く別物となるのでしょうか?
これはMainとTaroの二つのクラスで好感度パラメーターを受け取ろうとする設計自体が、そもそもおかしいのでしょうか。
それとも何かもっとスマートなやり方がありますでしょうか?

教本やwebページで調べてみると項目ごとに詳しい説明が書かれてはいます。
インスタンス化の仕方、カプセル化の使い方、継承のやりかた……等々。
しかしながら「こういったプログラムを作る場合にどういうやり方がセオリーであるのか」ということが全く分からず、つまづいてしまっています。
ご教示いただけたら幸いです。

補足情報(他に試してみたこと)

であるならば、koukandoやnameをstatic変数にすればいいのかな? と思いHeroineクラスのメンバをあらかたstaticにしてみたところ、ヒロインの名前が両方とも「よしこ」になるなどHanakoとYoshikoのパラメーターがごっちゃに混ざってしまいました……汗

追記(MainクラスとTaroクラスのそれぞれで好感度パラメーターを受け取ろうとしたことについて)

Taroクラスには、以下のようにたろうくんが実行する行動を記載しました。

import java.util.Scanner;
public class Taro{
  public String name;
  public int hp;
  public final int max_hp;
  public int intelligence;

  public void action(){
    Scanner scan = new Scanner(System.in);
    System.out.println("行動を選んでください。");
    System.out.println("1.移動 2.勉強 3.寝る");
    int val = scan.nextInt();
    switch(val){
      case 1:
        this.move();
        break;

      case 2:
        this.study();
        break;

      case 3:
        this.sleep();
        break;
    }
  }

  public void sleep(){
    this.hp = 100;
    System.out.println(this.name + "は眠った。体力が回復した!\n");
   }

  public void move(){


    Scanner s = new Scanner(System.in);
    Hanako hanako = new Hanako();
    Yoshiko yoshiko = new Yoshiko();
    System.out.println("どこに移動しますか?");
    System.out.println("1.校舎裏 2.公園 3.プール 4.帰宅する");
    int val = s.nextInt();
    this.hp-=5;
    switch(val){
      case 1: 
        System.out.println("校舎裏に移動した。");
        hanako.upKoukando();
        break;

      case 2: 
        System.out.println("公園に移動した。");
        yoshiko.upKoukando();
        break;

      case 3: 
        System.out.println("プールに移動した。");
        break;

      case 4: 
        System.out.println("帰宅した。");
        break;
        }

   }

  public void study(){
    this.hp -= 10;
    this.intelligence += 5;
    System.out.println("知力が5上がった。\n");
  }

  //-------------コンストラクタで初期化-----------
  public Taro(){
    this.hp=100;
    this.max_hp=100;
    this.intelligence = 60;
    }
}


はなこさんはいつも校舎裏に、よしこさんはいつも公園にいるということにして、会いにいくと(それだけで)好感度が上がるということにしました。
この際にupKoukando()メソッドを使うため、HanakoクラスとYoshikoクラスのそれぞれをインスタンス化しています。

そしてMainクラスでは、

public class Main{
  static int dayCount=1;

  public static void main(String[] args){
    Taro taro = new Taro();
    taro.name = "たろう";

    for(int i=1;i<11;i++){
      putStatus(taro.intelligence,taro.hp);
      taro.action();
      dayCount++;
    }
  }
//------------------
  public static void putStatus(int intel,int hp){
    System.out.println("+++++++++++++++++++++++++++");
    System.out.println("  "+dayCount+"日目"+"  "+"知性"+intel+"  "+"体力"+hp);
    System.out.println("の好感度");
    System.out.println("の好感度");
    System.out.println("+++++++++++++++++++++++++++");
  }
}


for文で10日間のループを、putStatus()で現在のパラメーターを表示させました。
そしてputStatus()の中、3,4行目の「System.out.println("の好感度");」のところにそれぞれはなこ、よしこの名前と好感度を呼び出そうと、mainメソッド内で再度Hanako hanako = new Hanako();としたところ、MainクラスとTaroクラスにそれぞれ別のhanakoインスタンスができてしまいうまくいきませんでした。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • jimbe

    2019/07/10 11:15

    「MainクラスとTaroクラスのそれぞれで好感度パラメーターを受け取ろう」というのは, 何をしようとして何が問題で出来ないのでしょう. やろうとしたコードをご提示願えますか.

    キャンセル

  • Neru_K

    2019/07/10 12:32

    ありがとうございます。
    少し長くなりましたがコードを追記しました。
    プログラミング初心者ゆえめちゃくちゃなことをしているかもしれないと思いお恥ずかしいのですが……
    なにかアドバイスいただけたら嬉しいです!

    キャンセル

回答 3

checkベストアンサー

+3

MainクラスとTaroクラスでそれぞれにHanako hanako = new Hanako();としてしまうと、作られた2つのhanakoインスタンスは全く別物となるのでしょうか?

はい。

これはMainとTaroの二つのクラスで好感度パラメーターを受け取ろうとする設計自体が、そもそもおかしいのでしょうか。

ゲーム全体を統括するクラスを書けば良いです。
Taroくんは必要に応じてそのクラスに問合せします。

シングルトンを使っても解決出来ますが、安易に使うとスパゲッティ化するのでお勧めしません。

それとも何かもっとスマートなやり方がありますでしょうか?

Hanakoクラス、Yoshikoクラスは要らないと思います。
Heroine hanako = new Heroine(花子の情報); で充分ですよね。

クラスや継承、カプセル化、ポリフォーミズム

x ポリフォーミズム
o ポリモーフィズム

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/10 12:50

    ご返信ありがとうございます!
    ゲームを統括するクラスを作って、好感度等のパラメーターのやりとりは全部そこからするということですね。
    ということはきっとそのクラスでHeroine hanako = new Heroine(花子の情報);とするんでしょうか。
    まだあまり具体的なイメージが湧かないのですが、いただいたアドバイスを参考に考えてみます!
    またシングルトンという言葉も初めて知りました。ありがとうございます。

    確かに、好感度と名前くらいならばわざわざHanako,Yoshikoクラスを作る必要はないですね汗
    継承を使ってみたくてそうしたところもあります。もっとはなことよしこが複雑になり、それぞれに独自のパラメーターができてきたら改めてクラスを作ればいいですね。

    x ポリフォーミズム
    o ポリモーフィズム

    こ、これは恥ずかしい間違い……(笑)

    キャンセル

+3

オブジェクトは, 現実的なイメージを基にすることで, 分かり易く, 扱い易くするものです.
イメージしてみてください. 「たろう(Taro)が移動(move)する度に, はなこ(Hanako)やよしこ(Yoshiko)は生まれる(new)」ことは現実的でしょうか?
私なら, ヒロインは主人公と同等にゲームの世界に存在するオブジェクトのほうが良いようにイメージします.
ですので move の度に new はせず, Taro 同様に先に Main で new し, Taro の行動に応じてその場所に居るヒロインを得て変化するようにします.

注: 以下のコードは切り貼りしただけで, コンパイル等確認していません.

Mainクラス

public class Main{
  //修正: static を削除, ヒロインを追加
  int dayCount=1;
  Taro taro;
  Hanako hanako;
  Yoshiko yoshiko;

  //修正: "世界"を生成して起動する.
  public static void main(String[] args){
    Main main = new Main();
    main.start();
  }

  //コンストラクタ追加: "世界"もオブジェクトとして扱うため
  Main() {
    taro = new Taro();
    taro.name = "たろう";
    //ヒロイン追加
    hanako = new Hanako();
    hanako.setName("はなこ");
    yoshiko = new Yoshiko();
    yoshiko.setName("よしこ");
  }

  //追加: "世界"は動き出す
  void start() {
    dayCount=1;
    for(int i=1;i<11;i++){
      putStatus(taro.intelligence,taro.hp);
      taro.action(this);
      dayCount++;
    }
  }

  //追加: その場所に居るヒロインを返す. 居ない場合は null.
  public Heroine getHeroine(int location) {
    switch(val){
      case 1: //校舎裏
        return hanako;
      case 2: //公園
        return yoshiko;
    }
    return null;
  }  
//------------------
  public void putStatus(int intel,int hp){
    System.out.println("+++++++++++++++++++++++++++");
    System.out.println("  "+dayCount+"日目"+"  "+"知性"+intel+"  "+"体力"+hp);
    System.out.println(hanako.getName()+"の好感度"+hanako.getKoukando()); //修正
    System.out.println(yoshiko.getName()+"の好感度"+yoshiko.getKoukando()); //修正
    System.out.println("+++++++++++++++++++++++++++");
  }
}

Taroクラス

  public void action(Main world){ //パラメータ追加
    Scanner scan = new Scanner(System.in);
    System.out.println("行動を選んでください。");
    System.out.println("1.移動 2.勉強 3.寝る");
    int val = scan.nextInt();
    switch(val){
      case 1:
        this.move(world); //パラメータ追加
        break;
(略)
  public void move(Main world){ //パラメータ追加: Mainクラスを登場人物の住む"世界"とする
    Scanner s = new Scanner(System.in);
    //Hanako hanako = new Hanako(); //削除
    //Yoshiko yoshiko = new Yoshiko(); //削除
    System.out.println("どこに移動しますか?");
    System.out.println("1.校舎裏 2.公園 3.プール 4.帰宅する");
    int val = s.nextInt();
    this.hp-=5;
    switch(val){
      case 1:
        System.out.println("校舎裏に移動した。");
        //hanako.upKoukando(); //削除
        //その場所に居るヒロインを得て, 居たら(null で無ければ)好感度アップ
        Heroine heroine = world.getHeroine(val);
        if(heroine != null) {
          System.out.println(heroine.getName() + "に会った");
          heroine.upKoukando(); //注意: 定義と使用でメソッド名が違っています.
        } else {
          System.out.println("誰も居なかった");
        }
        break;
      //case 2 も上と同様の為省略
(略)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/15 10:58 編集

    > newしていないのに、このworldというインスタンスはいつできたのでしょうか?
    world は Main クラスのインスタンスです.
    インスタンスもメソッドの引数として(int 等と同様)受け渡しできます. ですので, この一文のために move や action に引数を追加しました.
    world の実体である「Main クラスのインスタンス」の生成は, main メソッドの 「Main main = new Main();」です. ですが引数として出てくる最初は「taro.action(this);」の this になります. インスタンスメソッドでの "this" は, そのインスタンスを示すためです.

    キャンセル

  • 2019/07/16 12:05

    jimbeさん、何度もありがとうございます!ようやく理解できたと思います!
    つまりは以下のような流れというわけですね。

    Mainクラスをインスタンス化したmainをつくり、それをゲーム全体を統合する「世界」のオブジェクトとする。

    コンストラクタを通じて登場人物たちが生まれ、名前もつけられる。
    またgetHeroineメソッドに、ヒロインたちの居場所が記録されている。

    start()で世界が動き出し、パラメータ表示メソッド等々とともにTaroクラスのaction()メソッドが実行される。

    action(this)と、実引数にthisを入れておくことで「mainインスタンスを参照してね」という情報を仮引数に渡す

    Taroクラスのaction(Main world)で、worldという仮引数を通じて上記のthis、つまり「mainインスタンスを参照してね」という情報を受け取る。move()メソッドが実行されたら、それをさらにmove()メソッドに渡す。

    move()の処理を実行。ヒロインの名前や居場所を呼び出す。

    という流れなのですね!
    こうすることでaction()やmove()のみならず、必要であればstudy(),sleep(),またその他、今後作成するかもしれないメソッドから、いくらでも登場人物たちの情報を引き出すことができるのですね。
    もしかしたらまだ何か誤解しているかもしれませんが、一応は自分の中でパズルが解けたという感じでかなりすっきりした気分です!
    初心者の質問に根気よく付き合って下さり感謝です。初投稿でしたが勇気を出して質問してよかったです。
    ほんとうにありがとうございますm(__)m

    キャンセル

  • 2019/07/16 17:36

    お分かり頂けているように思います^^

    キャンセル

+1

インスタンス化の仕方、カプセル化の使い方、継承のやりかた……等々。
しかしながら「こういったプログラムを作る場合にどういうやり方がセオリーであるのか」ということが全く分からず、

ここだけにアドバイスしますと、
「デザインパターン」を学ぶと良いと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/10 13:12

    なるほど……お決まりのひな形?のようなものがあるのですね。
    やはりググって調べるよりも、まずは内容のまとまった書籍で勉強するのが近道ですよね。
    いくつか見繕って買ってみます!
    ちなみに重ね重ねで申し訳ありませんが、これだけは買っとけ!的なおすすめの書籍はありますでしょうか……?

    キャンセル

  • 2019/07/10 13:14 編集

    んー、私もデザインパターンも書籍は1つしか買ってないので(しかもずいぶん前に人にあげたので名前も覚えてない)なんとも言えませんが、とりあえずAmazonで評価を見て高いもので良いのではないでしょうか。
    仰っている通り「ひな形」「形式が決まっているもの」なので大きく違いは出ないと思っています。

    キャンセル

  • 2019/07/10 13:18

    そうなのですね。ご親切にいろいろと教えていただきありがとうございます。
    さっそくAmazonで見繕ってみます。
    どうもありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.38%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Java

    16504questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。