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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Java

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

Q&A

解決済

3回答

2822閲覧

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

Neru_K

総合スコア19

Java

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

0グッド

1クリップ

投稿2019/07/09 19:37

編集2019/07/10 03:30

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

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

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

該当のソースコード

Java

1public class Heroine{ 2 private String name; 3 private int koukando; 4 5 public void koukandoUp(){ 6 koukando++; 7 } 8 9 public void koukandoDown(){ 10 koukando--; 11 } 12 13 public int getKoukando(){ 14 return this.koukando; 15 } 16 17 public String getName(){ 18 return this.name; 19 } 20 21 public void setName(String name){ 22 this.name = name; 23 } 24 25 //-------------コンストラクタで初期化----------- 26 Heroine(){ 27 this.koukando = 10; 28 } 29}

発生した問題

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クラスには、以下のようにたろうくんが実行する行動を記載しました。

Java

1import java.util.Scanner; 2public class Taro{ 3 public String name; 4 public int hp; 5 public final int max_hp; 6 public int intelligence; 7 8 public void action(){ 9 Scanner scan = new Scanner(System.in); 10 System.out.println("行動を選んでください。"); 11 System.out.println("1.移動 2.勉強 3.寝る"); 12 int val = scan.nextInt(); 13 switch(val){ 14 case 1: 15 this.move(); 16 break; 17 18 case 2: 19 this.study(); 20 break; 21 22 case 3: 23 this.sleep(); 24 break; 25 } 26 } 27 28 public void sleep(){ 29 this.hp = 100; 30 System.out.println(this.name + "は眠った。体力が回復した!\n"); 31 } 32 33 public void move(){ 34 35 36 Scanner s = new Scanner(System.in); 37 Hanako hanako = new Hanako(); 38 Yoshiko yoshiko = new Yoshiko(); 39 System.out.println("どこに移動しますか?"); 40 System.out.println("1.校舎裏 2.公園 3.プール 4.帰宅する"); 41 int val = s.nextInt(); 42 this.hp-=5; 43 switch(val){ 44 case 1: 45 System.out.println("校舎裏に移動した。"); 46 hanako.upKoukando(); 47 break; 48 49 case 2: 50 System.out.println("公園に移動した。"); 51 yoshiko.upKoukando(); 52 break; 53 54 case 3: 55 System.out.println("プールに移動した。"); 56 break; 57 58 case 4: 59 System.out.println("帰宅した。"); 60 break; 61 } 62 63 } 64 65 public void study(){ 66 this.hp -= 10; 67 this.intelligence += 5; 68 System.out.println("知力が5上がった。\n"); 69 } 70 71 //-------------コンストラクタで初期化----------- 72 public Taro(){ 73 this.hp=100; 74 this.max_hp=100; 75 this.intelligence = 60; 76 } 77}

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

そしてMainクラスでは、

Java

1public class Main{ 2 static int dayCount=1; 3 4 public static void main(String[] args){ 5 Taro taro = new Taro(); 6 taro.name = "たろう"; 7 8 for(int i=1;i<11;i++){ 9 putStatus(taro.intelligence,taro.hp); 10 taro.action(); 11 dayCount++; 12 } 13 } 14//------------------ 15 public static void putStatus(int intel,int hp){ 16 System.out.println("+++++++++++++++++++++++++++"); 17 System.out.println(" "+dayCount+"日目"+" "+"知性"+intel+" "+"体力"+hp); 18 System.out.println("の好感度"); 19 System.out.println("の好感度"); 20 System.out.println("+++++++++++++++++++++++++++"); 21 } 22}

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2019/07/10 02:15

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

2019/07/10 03:32

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

回答3

0

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

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

Mainクラス

java

1public class Main{ 2 //修正: static を削除, ヒロインを追加 3 int dayCount=1; 4 Taro taro; 5 Hanako hanako; 6 Yoshiko yoshiko; 7 8 //修正: "世界"を生成して起動する. 9 public static void main(String[] args){ 10 Main main = new Main(); 11 main.start(); 12 } 13 14 //コンストラクタ追加: "世界"もオブジェクトとして扱うため 15 Main() { 16 taro = new Taro(); 17 taro.name = "たろう"; 18 //ヒロイン追加 19 hanako = new Hanako(); 20 hanako.setName("はなこ"); 21 yoshiko = new Yoshiko(); 22 yoshiko.setName("よしこ"); 23 } 24 25 //追加: "世界"は動き出す 26 void start() { 27 dayCount=1; 28 for(int i=1;i<11;i++){ 29 putStatus(taro.intelligence,taro.hp); 30 taro.action(this); 31 dayCount++; 32 } 33 } 34 35 //追加: その場所に居るヒロインを返す. 居ない場合は null. 36 public Heroine getHeroine(int location) { 37 switch(val){ 38 case 1: //校舎裏 39 return hanako; 40 case 2: //公園 41 return yoshiko; 42 } 43 return null; 44 } 45//------------------ 46 public void putStatus(int intel,int hp){ 47 System.out.println("+++++++++++++++++++++++++++"); 48 System.out.println(" "+dayCount+"日目"+" "+"知性"+intel+" "+"体力"+hp); 49 System.out.println(hanako.getName()+"の好感度"+hanako.getKoukando()); //修正 50 System.out.println(yoshiko.getName()+"の好感度"+yoshiko.getKoukando()); //修正 51 System.out.println("+++++++++++++++++++++++++++"); 52 } 53}

Taroクラス

java

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

投稿2019/07/11 01:24

編集2019/07/11 01:43
jimbe

総合スコア12639

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Neru_K

2019/07/12 07:55

jimbeさん、丁寧なご回答ありがとうございます。 書いてくださったソースコード、なんども読み返しながら考えたのですがどうしてもわからないところがありますので質問させてください。 Taroクラスのメソッドでaction(Main world)とmove(Main world)がありますが、このMain worldというのはなんのためにあるのでしょうか? 「Mainクラス型のworldという引数」だということはわかるのですが、どうもクラス型について調べてもよくわからないのです……。 たとえばnumber(int num)でしたら整数値を、text(String txt)でしたら文字列(の参照場所)を受け取るのですよね? ではactionメソッド内にあるようにthis.move(world);とすることでいったいworldとしてなんの情報を受け渡しているのでしょうか?
jimbe

2019/07/12 12:02

Main クラスのインスタンスを渡していっているのは, move メソッド内の Heroine heroine = world.getHeroine(val); のためです. Taro が行った先(校舎裏や公園, 他)に誰がいるかを知っているのは "世界"(Main) ということにして, Taro が移動したときに Main にアクセスできるように引数で渡しています. Main を LouiS0616 さんが回答で言われた「ゲーム全体を統括するクラス」として使っている感じでしょうか.
Neru_K

2019/07/14 18:45 編集

ご回答ありがとうございます! なんとか理解しようと、いろいろと考えたり検証したりしていました。返信が遅くなりすみません。 おかげさまでイメージはだいぶつかめてきたように思います! jimbeさんの「オブジェクトは, 現実的なイメージを基にすることで, 分かり易く, 扱い易くするもの」というお言葉もすごく腑に落ちました。moveのたびにはなこやよしこが生まれるのは確かにとても変ですね……納得です。 「Main world」に関しても、詳しいご説明ありがとうございます。 ゲーム全体を統括するクラスとしてMainを使うということ、そのために引数worldを渡しているということも、イメージとしてはよくわかりました。 ただ、なんでこういう書き方になるのかが分からず、その後また混乱しています(汗 Heroine heroine = world.getHeroine(val); この一文での「world」というのは、インスタンスだということになるのでしょうか? もしそうだとすると、newしていないのに、このworldというインスタンスはいつできたのでしょうか? action(Main world)としたときに自動的にインスタンスとして作られたのですか? その場合は、action(Main world)とmove(Main world)と繰り返しても、同じ名前の「world」というインスタンスが2つ作られる、ということはないのでしょうか。 質問ばかりで申し訳ありません(汗 もう少しですべてが氷解しそうな感じがするのですが……。
jimbe

2019/07/15 02:04 編集

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

2019/07/16 03: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
jimbe

2019/07/16 08:36

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

0

ベストアンサー

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

はい。

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

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

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

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

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

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

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

投稿2019/07/09 22:48

LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Neru_K

2019/07/10 03:50

ご返信ありがとうございます! ゲームを統括するクラスを作って、好感度等のパラメーターのやりとりは全部そこからするということですね。 ということはきっとそのクラスでHeroine hanako = new Heroine(花子の情報);とするんでしょうか。 まだあまり具体的なイメージが湧かないのですが、いただいたアドバイスを参考に考えてみます! またシングルトンという言葉も初めて知りました。ありがとうございます。 確かに、好感度と名前くらいならばわざわざHanako,Yoshikoクラスを作る必要はないですね汗 継承を使ってみたくてそうしたところもあります。もっとはなことよしこが複雑になり、それぞれに独自のパラメーターができてきたら改めてクラスを作ればいいですね。 x ポリフォーミズム o ポリモーフィズム こ、これは恥ずかしい間違い……(笑)
guest

0

インスタンス化の仕方、カプセル化の使い方、継承のやりかた……等々。

しかしながら「こういったプログラムを作る場合にどういうやり方がセオリーであるのか」ということが全く分からず、

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

投稿2019/07/10 02:24

m.ts10806

総合スコア80850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Neru_K

2019/07/10 03:57

ご返信ありがとうございます! 「デザインパターン」という言葉は目にしたことがあったのですが、調べてみると書籍などもたくさん出ているのですね。 確かに先人の作り上げたデザインパターンを真似してプログラムを書いてみるととても力がつきそうですね。 難しそうですし今の自分に理解できるかは怪しいですが、少しずつ勉強してみます!
m.ts10806

2019/07/10 04:01

というより「パターン」なので実はフレームワークのようにほぼ決まった形になっています。 ここにデータアクセスを作ってここにサービスを作ってここにコントローラを作って それぞれがこういう役割で といった具合に。 1本書籍を購入して通してやってみると良いと思います。
Neru_K

2019/07/10 04:12

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

2019/07/10 04:14 編集

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

2019/07/10 04:18

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問