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

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

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

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

2988閲覧

インスタンスごとに個別のメンバ関数を実装したい

mushroom314

総合スコア29

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

2クリップ

投稿2017/01/01 17:43

はじめまして、趣味でプログラムを書いている初心者の学生です。
C++でトレーディングカードゲームを作ろうと構想を練っていた最中に、自力で解決できない問題があったため、質問させていただきます。
###知りたいこと
あるクラスがあるとして、そのメンバ関数の動作をインスタンス毎に個別に実装するうまい方法はないでしょうか。

自力で調べたり考えたりした限りでは、
・仮想関数にして、継承クラスで関数を定義、またはオーバーライドする
・何らかの引数をコールバックして、switch/case文で処理を分岐させる
という方法があることがわかったのですが、どれも別種のインスタンスを増やそうとすると、その分クラスが増えたりswitch文が伸びたりして、保守性が下がるように思えます。

前Unityをさわったとき、ゲームオブジェクトにスクリプトをアタッチすることで個別の処理を実現できていたように思えました。今回も理想的には、クラスは変えず、関数だけ「アタッチ」することで個別の処理を実装できるような設計できないかと思案しています。

よろしくお願いいたします。

###補足(前提、具体的にやりたいこと)
C++で、遊戯王のようなトレーディングカードゲームの簡易版を書いています。
以下に示すような「Cardクラス」を設計して、インスタンスをカードのオブジェクトに対応させようと考えました。

C++

1class Card{ 2private: 3 int id;//識別番号 4 char* name;//名前 5 int attack;//攻撃力 6 int defend;//守備力 7 //... 8public: 9 void effect(){ 10 //ここに「効果」を実装 11 } 12 //... 13}

「効果」には、例えば「選択中のカードの攻撃力が1000以上ならば自分の攻撃力を500加算する」などの処理を書きます。
カードごとに様々な効果を実装したいのですが、インスタンス毎にカードクラスを増やしたり長いswitch文を書いたりしないようにするには、どうすればいいでしょうか。

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

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

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

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

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

guest

回答3

0

典型的なStrategy パターンを適用するのが良いかともいます。

Strategyは戦略です。カードのインスタンスによってどのような効果にするか戦略を変えて行くという物です。応用すれば戦略部分をLuaやJavaScriptなど別のスクリプトファイルにするなんて事もできます(RPGツクールがこんな作りらしい)。やり方は多くあるのですが、共通するのは、戦略はメンバー関数ではなくメンバー変数として持ち、インスタンス生成時に指定するという物です。

大きく分けると、戦略毎にクラスを作るパターンと関数をつくる(ラムダ式)パターンの二通りがあります。(雰囲気だけなので、コンストラクタとか省略しまくっています)

C++

1// クラスを作る 2class Card; 3class Action { 4public: 5 virtual void apply(Card c) = 0; // 純粋仮想関数 6}; 7class AttackUpAction : public Attack { 8 int upPoint; 9public: 10 void apply(Card c) { 11 c.attack += this.upPoint; 12 } 13}; 14class Card { 15public: 16 int id;//識別番号 17 char* name;//名前 18 int attack;//攻撃力 19 int defend;//守備力 20 Action effectAction; // 効果の動作 21 //... 22public: 23 void effect(){ 24 effectAction.apply(this); 25 } 26 //... 27}; 28 29int main() { 30 ... 31 Card *c0 = new Card(0, 'スライム', '100', '200', AttackUpActino(100));; 32 ... 33}

C++

1// 関数をそのまま指定 2class Card { 3public: 4 int id;//識別番号 5 char* name;//名前 6 int attack;//攻撃力 7 int defend;//守備力 8 std::function<void(Card))> effectAction; // 効果の動作 9 //... 10public: 11 void effect(){ 12 effectAction(this); 13 } 14 //... 15} 16 17int main() { 18 ... 19 Card *c0 = new Card(0, 'スライム', '100', '200', [](Card c){c.attack += 100;}); 20 ... 21}

工夫するところは、実際の効果を行うときに、自分自身thisを渡すと言う所です。実際は場面の状況など多くのものを渡す必要があるかも知れません。また、気をつけて欲しいのは、Cardクラスのprivateは戦略からは見えないという点です。上の例では面倒なのですべてpublicにしていますが、戦略だけに見えるとなると、ちょっと色々と工夫が必要になるかと思います。

同じような戦略が多くあるのであれば、それらを一つの戦略としてまとめられるクラス方式がいいかとおもいます。もし、カードによってバラバラで統一感がないと、ラムダ式を使った関数方式がいいかと思います。デザインパターンの本やサイトを一度見てみると良いでしょう(問題は、デザインパターンの例はJavaが多くて、C++はほとんど無いと言ったところでしょうか…)

投稿2017/01/01 22:51

raccy

総合スコア21735

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

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

mushroom314

2017/01/03 01:56

ご回答ありがとうございます。コード例まで付けて下さり、とても理解がすすみました。デザインパターンについて早急に勉強してみようと思います。ラムダ式については、LISPを勉強中に見かけた覚えがあるのですが、C++でも使える機能であることを初めて知りました。一気に視野が広がった気がします。ありがとうございました。
guest

0

ベストアンサー

Lua などのスクリプト言語を組み込んで、カード固有のロジックをスクリプト言語で記述するようにしてはどうでしょうか?

参考:Lua組み込み編

投稿2017/01/01 22:26

mit0223

総合スコア3401

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

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

mushroom314

2017/01/03 01:55

ご回答ありがとうございます。Cにスクリプト言語を組み込む技術の存在を知らなかったので、目からうろこが落ちました。カードゲーム開発以外にも広く使える技術にも思えるので、じっくり習得していきたいと思います。ありがとうございました。
guest

0

「インスタンスごとに別々のメソッドを」というだけならraccyさんの述べるようなOOPの技法を駆使していくところですが、トレカゲームを作ろうという話ならmit0223さんの言うような、別言語にカード定義を切り出すやり方が最も適切と考えます。

カードの追加サイクルはプログラムの寿命よりはるかに短いはずだからです。新しいカードをリリースしようとして、そしたらプログラムをビルドし直して配布し直さないといけないというのは重すぎるのです。

すると、カードの効果はプログラム内に記述するのではなく、定義ファイルかデータベース上に外部化するべきとなります。

そういう外部化形式は一種の設定ファイルですが、単なる設定ファイルと言うよりは「これはすでに一種の言語」と言えるほどの複雑さを持っていそうです。そういう複雑な外部設定技術体系のことをDSLと呼びます。

いくら何でも一から言語処理系を作り上げるのは大変すぎるのでは? そこでmit0223の提案なさっているのが、DSLを実現するためにLuaを利用しようというやり方です。

もっとも組み込みLuaの使い方をマスターするだけでそれはそれで十分大仕事なので「単にトレカゲームを作りたいだけなんですけど…」となるかもしれません。でもむしろこう言えます、トレカゲームというのは最初からそれだけ大変な課題だったということです。

投稿2017/01/03 00:33

yuba

総合スコア5568

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

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

mushroom314

2017/01/03 02:05

ご回答ありがとうございます。私はそもそも技術に無知であるゆえ目先の課題解決しか見えていませんでしたが、今後どううまく運用していくかと考えると、自然とどのように設計するか決まるのですね。幸いにトレカゲームは大好きなので、今回はストラテジーパターンで実装する場合とスクリプトを組み込むやり方のどちらでも実装してみて、さらに勉強していこうと思います。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問