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

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

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

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

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

Q&A

解決済

5回答

3985閲覧

ゲームのオブジェクトのためのクラス

prim

総合スコア16

Java

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

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

0グッド

0クリップ

投稿2019/02/27 18:00

前提・実現したいこと

プログラミング初学者のため用語がたどたどしくて申し訳ございません。

現在RPGゲームをjavaで作成しているのですが、敵キャラや味方キャラのデータの保持の仕方についてどの記述がより良いか悩んでいます。
以下の例がダメな理由(下記以外)やより良い保持の仕方を指摘してもらえると助かります。

具体例

例えば、ゴブリンというキャラを実装する場合

(1) Unitクラス(敵キャラ、味方キャラ、主人公キャラ、非戦闘キャラが継承するクラス) //名前等を保持する (2) BattleUnitクラス (Unitクラスを継承: 戦闘を行うキャラ全てが継承するクラス) //攻撃力等を保持する (3) BattleNpcUnitクラス (BattleUnitクラスを継承: 戦闘を行う全てのNPCが継承するクラス) //行動パターンなどを保持する (4) Goblinクラス (BattleNpcUnitクラスを継承: 戦闘NPCであるゴブリンの情報を保持するクラス) //固有のスキルや特性などを保持する

と現在記述しています。

###このようなコードが良いと思う理由

(1) オブジェクト指向っぽい感じがする (2) 戦闘キャラ全般の仕様変更等があった場合(例えば名前の後にレベルをつけるようにするとか)一括でできる。 (3) どの情報がどこに存在するかが明確。 (4) 複雑になると予想される?システム部分の記述量を減らせる。 (5) オブジェクトの使い回しが容易(拡張性が高い?)

###疑問を覚えた理由
この記述に疑問を覚える理由は以下のようにアドバイスされたからです。

(1) わざわざクラスを用意してデータを保持せずとも、システムを管理するクラスに組み込むべきだ。 (2) これは普通のコードではない。 (3) 現状ほとんどのメソッドがget/setならばクラスは必要ない。必要になったらそのようにすればいい。

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

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

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

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

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

guest

回答5

0

一応気になった点だけ書いておきます。

オブジェクト指向を考えているのであれば、静的な構造からだけでなく、動的な視点も必要です。
どんなデータを持ち回る可能性が高く、そのときにどんなI/Fを持つと汎用的になる、のような視点です。
設計段階で悩む場合は、図にするとイメージがわくこともあるので、UMLを使って何か書くのも手ですよ。
静的/動的両方の観点から現状の仕様の大枠を満たせるか、などを考えつつ簡単に箱と属性を書いていくといいとイメージが湧きやすいです。

なお、派生(や実装)は、構造としての制約が強いので、初期設計段階では確実な関係以外は曖昧にしておいた方がいいと思います。継承構造を厳密にしすぎると、実装を継承するような構造が多くなり、保守性が低下してしまいます。何を言いたいかというと、継承構造は実装目処が立たないうちに何段にもしない方がいいということです。

設計は基本的に今見えている範囲のことが、忘れずに表現できていれば良く、後は同様に検討した他の部分との整合が綺麗に取れていて、歪なところがないかを調整すればいいだけです。動きそうな目処が立ったら、パフォーマンスの観点で厳しそうなところを洗い出し、そこから全体を調整して、初回のモデルとして構造を決めてしまえばいいかと思います。

拡張性は気にするだけ無駄です。変更の入る部分は通常予想できません。今ある機能をより簡潔に描けるモデルを考えるべきです。ようは拡張できるようにあれもこれもと機能を詰めすぎるよりは、こんなのでいいの?みたいなシンプルなモデルの方が良い場合が多いということです。

あと、強いていうとしたら、オブジェクト指向の説明ではオブジェクトが属性とメソッドが合わさってクラスみたいな感じに言われると思いますが、実際にはそれを意識すると、小さなクラスに機能を持たせすぎる形になってしまいます。あまりオブジェクト指向だからと言って、データと機能を小さなクラスに詰め込まず、クラスごとの役割を明確にし、このクラスはデータメイン(モデル)、このクラスは機能メイン(コントローラ)、このクラスはプレゼンテーションメイン(ビュー)みたいな役割を重視する形にメリハリを付けてクラスを分けると、全体としての見通しがよくなります。

投稿2019/02/28 05:41

wwbQzhMkhhgEmhU

総合スコア343

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

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

prim

2019/02/28 09:07

回答ありがとうございます。 知識として拡張性を高めることを意識していましたが、実践的には拡張性の高いコードとは非常に難しく、シンプルなコードの方が結果的により良いのですね。 参考にさせていただきます。
guest

0

ベストアンサー

最初に

敵キャラや味方キャラのデータの保持の仕方についてどの記述がより良い

オブジェクト指向を学ばれている他の人にそのクラス設計を見せて、どれだけ何となく(抽象的に)理解しやすいかが1つあると思います。あとはそのクラス設計で実際にプログラミングしやすいか否か。

2点を満たしているとprimさんが判断するのであれば、自信を持ってその設計でやってみると良いと思います。

少し気になる点

より良い保持の仕方

クラス設計には日々悩んでるまだまだオブジェクト指向のアマチュアですが、2点。

1点目、UnitNpcUnitBattleNpcUnitは抽象クラスであったほうが良いと思います。

2点目、Npcクラスは、独自の行動で戦闘ができるNPCとして振舞えるということなので、次のようにCAN-DO関係で表現できます。

  • Goblin can be NPC
  • Goblin can do fight automatically

継承はIS-A関係なので、文章にすると「Goblin is a NPC」になります。
つまり「ゴブリンは絶対にNPC」という確固たる性質を持ってしまうわけなので、個人的に違和感があります。

上の関係で違和感がないのはCAN-DOの方なので、NPCの「行動パターンを持つ」という概念はクラスではなくインタフェースで表現したほうが適切な気がします。

ただ、あくまで「個人的に違和感を感じている」だけなので「ゴブリンは絶対NPC」という思想の元でクラス設計をされているのであれば、次に紹介する「気になった点を解決する」欄はスルーしても構いません。

気になった点を解決する

インタフェースにしたらどのような実装になるのか?を一つ考えてみました。
以下のようなクラス設計になるでしょうか。

(1) Unitクラス(敵キャラ、味方キャラ、主人公キャラ、非戦闘キャラが継承する抽象クラス) (2) BattleUnitクラス (Unitクラスを継承: 戦闘を行うキャラ全てが継承する抽象クラス) (3) Goblinクラス (BattleUnitクラスを継承し、IAutoFightableを実装: ゴブリンの情報を保持し、NPCとして振舞える具象クラス)

ゴブリンを表現するなら次のようになります

Java

1interface IAutoFightable{ 2 void AutoFight(); 3} 4 5public class Goblin extends BattleUnit implements IAutoFightable{ 6 public void AutoFight(){ 7 //敵ゴブリンの戦闘パターンを定義 8 } 9}

上記コードのようにモンスター毎に別々で定義するのではなく、「NPCとして共通の振る舞いを定義したい」という話であれば、そこでBattleNpcUnitクラスの登場になるかなと思います。

Java

1public class BattleNpcUnit extends BattleUnit implements IAutoFightable{ 2 public void AutoFight(){ 3 //敵のデフォルト戦闘パターンを定義 4 } 5}

Interfaceをつけておくと何が良いかというと、抽象的なクラスやインタフェースに対して振る舞いを要求できます。例えばプレイヤーと敵を戦わせる際は次のようなメソッドを作れます。

Java

1public Fight(Player player, BattleNpcUnit enemy){ 2 player.Attack(); //プレイヤーが攻撃 3 enemy.AutoFight(); //NPCが自動的に戦う 4}

GoblinBattleNpcUnitを継承させた場合は、上記のFightメソッドの引数に渡すことができます。
GoblinクラスでAutoFightメソッドをオーバーライドした場合は、Gobrin独自の戦闘が行えます。(パーティー戦ならあるプレイヤーだけ一人狙いするとかがあるかもしれません)

そうするとFightメソッドでenemyに保持されているインスタンスの型を知らないで実装を作ることができるので作りやすくなります。
このように抽象クラスやインタフェースに対してプログラミングすることを「ポリモーフィズム」と言います。

もっとオブジェクト指向チックに設計したいのであれば、継承することだけにこだわらず抽象に対してプログラミングするという考え方があると良いと思います。

所感

(1) オブジェクト指向っぽい感じがする

(2) 戦闘キャラ全般の仕様変更等があった場合(例えば名前の後にレベルをつけるようにするとか)一括でできる。
(3) どの情報がどこに存在するかが明確。
(4) 複雑になると予想される?システム部分の記述量を減らせる。
(5) オブジェクトの使い回しが容易(拡張性が高い?)

これ等5点を見て思うのは、オブジェクト指向っぽいというよりかは継承という機能のメリットを説明しているように見えます。なので、継承がオブジェクト指向の全てではないことは認識しておくと良いかもしれません。

(1) わざわざクラスを用意してデータを保持せずとも、システムを管理するクラスに組み込むべきだ。

(2) これは普通のコードではない。
(3) 現状ほとんどのメソッドがget/setならばクラスは必要ない。必要になったらそのようにすればいい。

全体的に同意できかねます。

データを保持する方法は人によって様々ですが、HPや攻撃力のステータスはシステムで静的に持つのではなく各々のオブジェクトに保持しておくのが良いと思います。

システムを管理させるデータというのはステータスなどの具体的な値ではなく、どちらかというとUnitオブジェクトだとか、何かしらのオブジェクトではないでしょうか。

2はよくわかりません。関数チックに定義しろということでしょうか。

3もよくわかりません。データを保持するためのクラスというものもあるくらいですから合っても良いと思います。むしろ、そのクラスを用意しなければ各々のステータスをどのように管理すれば良いのでしょうか。是非そのアドバイスをされた人からより良い方法を伺いたいです。

投稿2019/02/28 02:41

編集2019/02/28 03:00
BluOxy

総合スコア2663

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

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

prim

2019/02/28 09:10

回答ありがとうございます。 インターフェースはこれまで使いどころがわからず避けていましたが、CAN-DO、IS-A関係の説明が非常にわかりやすかったです。 参考にさせていただきます。
guest

0

検索したらこんなのが出てきました。参考にどうぞ。

【Breed】RPGのモンスターなどのキャラクターを作成するときに使うデザインパターン - Qiita

おそらく参考資料として挙げられている「Game Programming Patterns ソフトウェア開発の問題解決メニュー impress top gearシリーズ」が役に立ちそう。

投稿2019/02/28 02:18

編集2019/02/28 02:19
hayataka2049

総合スコア30933

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

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

prim

2019/02/28 09:11

回答ありがとうございます。 参考にさせていただきます。
guest

0

敵キャラや味方キャラのデータの保持に関しては、
色々とやり方がありますが、下手にクラスを継承しなくても構いません。

疑問を覚えた理由欄の2番目以降は誰でも抱く疑問なので気にしなくていいと思いますよ。

投稿2019/02/28 01:12

stdio

総合スコア3307

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

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

prim

2019/02/28 09:18

回答ありがとうございます。 継承の具体的な使い方がJComponent系でしかわからない中、今回継承の具体的な前述以外の使い道を思いつき試しに継承を用いて記述しました。 継承を用いないキャラのデータ保持の方法についても調べ直すことにします。
stdio

2019/03/01 03:34

継承を用いないキャラのデータ保持は管理クラスにデータを保管するやり方で、Pimplイディオムを利用したシングルトンクラスが必要な為、ツール制作等の経験がないとおススメはできません。 逆にあるなら、良い方法ではあります。 キャラの情報を一か所で管理できる為、テクスチャやSE等の読み込みも楽に行えます。
prim

2019/03/01 18:08

ご丁寧にありがとうございます。 基礎をしっかりとしてから挑戦してみようと思います。
guest

0

提示されたクラス設計ですが、クラスの多さよりも多重継承「継承されたクラスをさらに継承」が使われていることの方が気になります。

多重継承のデメリットとして、

  • クラス構造が複雑になり、全体を把握しにくい。(どの情報がどこに存在するかが分かりにくい)
  • クラス設計が変更しにくいため、拡張性が低い。
  • たくさんのクラスを用意する必要があり、全体の記述量が増え、メンテナンスコストが増える。

などがあり、オブジェクト指向の考え方と矛盾します。

あと素朴な疑問なのですが、モンスターは全部で何体いるんでしょうか?

極端な例かもしれませんが、ポケモン最新作「Let’s Go!ピカチュウ/イーブイ」では 809 種類のポケモンが登場しますが、この設計ですと 809 個のクラスが必要になります。
現実的にメンテナンス不可能な気がします。


「全体の記述量が増え、メンテナンスコストが増える」の理由を、以下の挙げさせていただきます。

  • 単純にクラスが増えれば増えるほどが全体の記述量が多くなり、開発者が把握しなければならない範囲が広くなる。
  • クラスが増えることにより、コンパイル時間が長くなる。(使用するプログラミング言語やコンパイラの性能にもよりますが)
  • クラス継承により、他のクラスとの関係性が複雑になる。

最後の「クラス継承により、他のクラスとの関係性が複雑になる」は、少し理解しづらいと思いますので、一つだけ例を挙げます。

イメージ説明

Bird(鳥)クラスを継承する子クラスとして、Owl(フクロウ)クラスと Pigeon(ハト)クラスがあり、そこに Penguin(ペンギン)クラスを追加したいのですが、一つ問題があります。
Bird(鳥)クラスは Fly メソッドを持っていますが、ペンギンは空を飛ぶことができません。
さて、この場合はクラス設計をどう変更すべきでしょうか?

本題ではないので、解説はリンク先の記事に譲りますが、クラス継承にはこのような問題がたくさん出てきます。
この例では、全部で 4 つしかクラスが出てきませんでしたが、これがもしポケモンのように数百個のクラスが必要になった場合、果たしてうまくクラス設計することができるでしょうか?

参考:
【Bad & Good】拝啓 継承による問題を間違って解決した私へ

投稿2019/02/27 22:20

編集2019/02/28 15:57
nskydiving

総合スコア6500

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

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

momon-ga

2019/02/28 00:58

> 多重継承(継承されたクラスをさらに継承) 親-子-孫クラスの関係を多重継承というのは聞いたことがなく、少し調べてみたけど出てきませんでした。 出典あれば教えてほしい。 > クラス設計が変更しにくいため、拡張性が低い。 継承は、変更が波及することが前提の設計になるので本来なら問題ないはず。 > たくさんのクラスを用意する必要があり は、同意。色えんぴつとか良い例で、属性で管理できるものをクラス作るメリットはなさそう。
hayataka2049

2019/02/28 01:07 編集

>多重継承(継承されたクラスをさらに継承) 多重継承の意味を誤解していませんか? 1つのクラスを定義する際に複数のクラスから継承することです。Javaではそもそもできないので基本的に多重継承を心配する必要はありません。 「継承されたクラスをさらに継承」を意図してそう書いているとしても、「オブジェクト指向の考え方と矛盾します」とまでは言えないんじゃないでしょうか。そもそも普通に使う方法ですし。 --- かぶった・・・
stdio

2019/02/28 01:16 編集

> 809 種類のポケモンが登場しますが、この設計ですと 809 個のクラスが必要になります。 ポケモンの行動パターンはある程度仕様が決まっているため、ツールを作成して809種類を作成しているのだと思いますよ。
nskydiving

2019/02/28 01:56

momon-gaさん、hayataka2049さん ご指摘ありがとうございます。 「多重継承」という表現は誤りでしたので、修正させていただきました。 >「継承されたクラスをさらに継承」を意図してそう書いているとしても、「オブジェクト指向の考え方と矛盾します」とまでは言えないんじゃないでしょうか。そもそも普通に使う方法ですし。 程度の問題だと思いますが、「継承を繰り返すことでクラス構造が複雑になってしまっては元も子もないですよ。」くらいに受け取っていただければと思います。
stdio

2019/02/28 02:16

しっかりと仕様が決まっているなら今の方法でいいと思いますよ。 継承を繰り返すことはオブジェクト指向の考え方そのものですから...
hayataka2049

2019/02/28 02:25

>stdioさん 継承 ⊂ オブジェクト指向は言えるかもしれませんが(ただしそれにも疑問符はつけようと思えば付けられる:インスタンスベースなど)、「継承を繰り返すことはオブジェクト指向の考え方そのものです」と書かれてしまうと継承 = オブジェクト指向と誤読され得るので、少し問題含みです。言葉尻の話ですが・・・
prim

2019/02/28 09:32

回答ありがとうございます。 継承を重ねることでよりデータの場所がカテゴライズされて明瞭になると考えていましたが、そうとも限らないのですね。 「たくさんのクラスを用意する必要があり、全体の記述量が増え、メンテナンスコストが増える」とのことですが、Goblinクラス等末端のクラスにはユニークなパラメータとメソッドを、そのほかには共通するパラメータとメソッドを持たせようと思うのですが、その場合でもメンテナンスコストは増えるのでしょうか。 差し支えなければメンテナンスコストが増える理由をご教授願えますでしょうか。
nskydiving

2019/02/28 16:05

> 差し支えなければメンテナンスコストが増える理由をご教授願えますでしょうか。 回答に追記させていただきました。
prim

2019/03/01 18:13

追記ありがとうございます。 不適切な継承を行ってしまった場合の修正の手間が大変そうなのがよくわかりました。 ご丁寧にありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問