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

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

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

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

オブジェクト指向

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

Q&A

解決済

1回答

4000閲覧

キャラクターと移動の振る舞いに対する設計

tmt

総合スコア11

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

オブジェクト指向

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

0グッド

2クリップ

投稿2017/01/26 15:30

###前提・実現したいこと
質問させていただいた意図としては、
オブジェクト指向として適切な設計というのを理解したいという背景がございます。
タイトルはうまくまとめることができなかったので、例題としての内容を記載させていただきました。
下記の問題に沿ってご回答がいただけますと幸いです。

###発生している問題
Unity/C#でゲームの製作するにあたって、ゲーム内のキャラクター用のクラスを実装しています。
キャラクターは、移動をはじめとした様々なアクションを行うことを想定しています。
が、これをキャラクターのクラス内でガシガシ書いていくと、
将来的なことを考えるとぶくぶくと肥えていきそうな懸念があります。
そのうえ、単一責任の原則から見るとあまり好ましくなさそうなので、
アクションごとに切り分けることにしました。移動も例外なく切り分けます。

移動はキャラクターによってまちまちなので、移動用のインタフェース(MoveInterface)を作り、
その上に歩行(Walk)、ワープ(Warp)と移動方法毎のクラスを用意しました。

このような形で各々のアクションについても実装することで役割がはっきりするとおもうのですが、
最終的にこのキャラクターをプレイヤーが動かせるようになった場合に、
結局インタフェース上に存在するMoveTo()やStop()をCharacterクラスの外から呼び出す必要が出てくるため、
自分のやっていることがラッパクラスを作っているに過ぎないのではという疑念が浮かび、
しっくりきていません。
疑念が晴れず、以下のコードのような設計がオブジェクト指向として適切なのか、
悩みが尽きません。

本来はどのような実装が適切なのか、ご指摘を賜れればと考えております。
よろしくお願いいたします。

※Unityならコンポーネント指向だからそもそもオブジェクト指向を使う意味が分からない、
という話は今回はご容赦いただけますと幸いです。

###該当のソースコード

C#

1public interface MoveInterface 2{ 3 void MoveTo(Vector3 destination); 4 void Stop(); 5} 6public class Walk : MoveInterface 7{ 8 // 中身は割愛 9} 10public class Warp : MoveInteface 11{ 12 // 中身は割愛 13} 14 15class Character 16{ 17 private MoveInterface _moveLogic = new Walk(); 18 19 public void MoveTo(Vector3 destination) 20 { 21 if (null != _moveLogic) 22 { 23 _moveLogic.MoveTo(destination); 24 } 25 } 26 public void Stop() 27 { 28 if (null != _moveLogic) 29 { 30 _moveLogic.Stop(); 31 } 32 } 33}

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

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

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

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

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

guest

回答1

0

ベストアンサー

私はゲームプログラミングをしませんが、この手の質問は色々な意見があった方が良いと思うので回答してみます。


Strategyパターンとしては正しい切り分けをしていると思います。表層的なクラス(今回はキャラクター)は各種状態とアルゴリズムを纏めることが要求されているので、細かい動作の変更によってこのクラスを修正するのはおかしいですよね。

ゲームの進行上キャラクターをある地点に移動させたい

ゲームエンジンがCharacterにアクセス
CharacterがMoveInterfaceにアクセス ⇒ 〇

ゲームエンジンがCharacterのMoveInterfaceにアクセス ⇒ ×

というように私は考えるので、上記コード自体には違和感はありませんでした。
複雑なオブジェクトを構成する場合、一次的に中身がスカスカのクラスができてしまうことはあります。
ただし、MoveInterfaceは移動方法と言いつつ、座標等も管理していそうなのでもしも移動している座標を管理しているならそれはちょっと違うだろう、と思いました。移動方法は進行状況を管理するべきではありません。座標情報自体はキャラクターと紐づくべきですから、そうなるとキャラクターのインターフェースと移動アルゴリズムのインターフェースは変わってくるはずです。

他の考え方としては…

キャラクターが状態だけを持っていればいいのでは?ということを気にされていると思います。
ようは、MoveKindだけキャラクターは持てばいいんじゃないのか、ということです。

3Dのゲーム等ではシーン切り替えで1人のキャラクターが複数の座標位置と紐づいたりする場合があります。
そうなると、シーン毎に座標等の状態は管理しなくてはいけなくなります。

こうなると、キャラクター自体をもっと抽象的な状態に保つために
・キャラクターに移動方法という状態を持たせる
・ゲームエンジン側で移動方法とアルゴリズムのマッチングを行い、表示したモデルを移動させる
・ゲームエンジン側がモデルとキャラクターの紐づきを把握している
という切り分けが行われているんじゃないか、と思っています。
キャラクターが保持する情報に直接的なアルゴリズムが含まれていないと、簡単なフラグの切り替えだけで移動方法が変更できる等の利点もあります。

ただこれはシーン切り替えが無ければ抽象化レイヤを追加して複雑にしただけと言えますから、作るゲームの規模で考えれば良いんじゃないかと思います。

あとは強いて気になった点を挙げるならMoveInterfaceという名前でしょうか、これはC#的な名前ではないですね。

投稿2017/01/27 03:00

haru666

総合スコア1591

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

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

tmt

2017/01/28 02:32

ご回答ありがとうございます。 ご指摘の通り、この構造だと目的地の座標を持っていなければならないので、その点では適切な設計ではないと考えています。 実際は毎フレーム目的地に対して移動するための更新処理も必要になります。 Characterに目的地を管理するメンバを追加して、MoveInterfaceは更新時にCharacterを引数にとって座標の更新をする…といったところでしょうか。 抽象化を進めることで仕様の改変に対応できる幅は広がり変更コストがある程度抑えられるけど、複雑さと実装コストがかかってくるかと思うのでそこはトレードオフですね。 もともとC++でコードを書いていた時期があってMoveInterfaceという名前になっています… C#的なネーミングだとIMoveという形が適切でしょうか…?
tmt

2017/01/29 12:44

広く回答をいただきたいところではあったのですが、 今一つ質問をうまく伝えられていないところもあって、これ以上は回答がつかないだろうと判断しました。 haru666様のご回答をベストアンサーとさせていただきます。 >複雑なオブジェクトを構成する場合、一次的に中身がスカスカのクラスができてしまうことはあります。 少なくとも設計として大きな誤りをしているわけではないことが理解できましたので、 少し自信をもってコーディングに努めていこうと思います。 ご回答いただきありがとうございました。
haru666

2017/01/31 02:18

そうですね、皆さん発言に責任持ちたいと思うので、気軽には答えづらいかもしれません。 IMoveだと自身が動くようなイメージになってしまうので、(といっても今回は状態を保持している関係上それで間違っていませんが)ファンクタに近い性質を表すために私だとIMoveActionとかにすると思います。 一方でIMoveという名前で常にCharacterクラスが同じ各キャラクターやエネミークラスもIMoveを継承するのであれば違和感はないです。 キャラクターと各アクションの関係は複雑なので、これを扱いやすくするためにFacadeパターンを適用していると考えれますしね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問