🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

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

Q&A

解決済

4回答

5891閲覧

カードゲームのカード効果について

kanata_02

総合スコア25

C#

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

0グッド

0クリップ

投稿2019/12/15 12:01

編集2019/12/15 16:14

現在Unityでカードゲームを製作していて、
カードを出したときに発生させる効果(プレイヤーに対するHP回復、ダメージ無効化など)をカードに持たせたいと思っています。

実装方法として、カードにEnumで種類を持たせ、その種類をswitchで見て発動する効果を分ける方法を考えたのですが、カードの種類分Enumの中身とSwitchの分岐が増えてしまうため、カードの種類が増えた時に対応できなくなってきました。
何か他に良い実装方法はありますでしょうか。継承などにあまり慣れておらず、他に方法が思いつきません…
ご教授いただければ幸いです。

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

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

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

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

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

guest

回答4

0

ベストアンサー

トレーディングカードゲームの場合、カードのテキストには「何でも書ける」ので、何らかの(ゲームを構成する)クラスに依存するようなことはできません。
言ってみればカードのテキストは「ゲームマスターの言葉」であり、ゲームマスターがやってもいいこと(=ゲーム全体)は何でもできなくてはならないからです。つまりゲーム全体をコントロールできる権限がなくてはなりません。
※バランスブレイカーになるから誰もやりませんが、「神の怒りが発動して全勢力全滅。ゲーム打ち切り」テキストがあったって、構わないわけです

そこを踏まえると、「何でもできるようにゲームのすべての情報にアクセスできるようにした上で、任意の処理を書けるようにメソッドを呼び出すだけにする」のがいいような気がします。
もし別に何かあるとしたら、「そのカードを出せるタイミングかどうか判定する」のは別にあってもいいかもしれないくらいでしょうか。

投稿2019/12/16 00:17

tacsheaven

総合スコア13703

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

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

退会済みユーザー

退会済みユーザー

2019/12/16 00:26

同意します。 カードはやはり場の状況すべてにアクセスできないとゲームの拡張性が極めて狭くなると思います。 任意の処理をかけるメソッド(私は抽象メソッドがよいかと)と場の状況クラス(各項目は抽象化)を持って実装するべきかと思います。
kanata_02

2019/12/16 03:06

回答ありがとうございます。 各ゲーム情報とスキルの抽象メソッドを持ったクラスを作り、効果の違うカードごとにスクリプトを新しく作成して、そこでそれぞれのスキル内容の実装をする、という実装の解釈でよろしいでしょうか?
tacsheaven

2019/12/16 04:20

そんな感じですね。カードは大まかに言えば種類(土地だったりクリーチャーだったり)で分かれますが、フレーバーテキストに関しては abstract な関数として基底クラスで定義しておいて、カードの派生クラスで実装する格好になるかと思います。 まあ、この場合は柄の違うカードがすべて別のクラスになってしまうので煩雑になる懸念はありますが……途中に一段抽象クラスをかませるのもありでしょう。
kanata_02

2019/12/19 10:43

実装することができました!遅くなりましたが、回答いただきましてありがとうございました!
guest

0

派生を使うと面倒なんで、IDと関数を紐付けるDictionaryを作るほうがいいかな

スクリプト導入も、カード効果作成者とプログラマが別ならありだけど、
同一者で小規模なら無理に入れなくてもいいと思います

//カードIDと効果のDictionary Dictionary <int, Func<AllData, Card, Result>> carddictonary = new Dictionary <int, Func<AllData, Card, Result>>(); // カードIDと実行効果を紐付けていく void Resister(){ carddictonary[1] = CardDestory; carddictonary[2] = CardAoE; ... } // カードを使う関数 void Action(Card card, Card target){ var result = carddictonary[card.id](alldata, target); } // パワー3以下の対象のカードを破壊 Result CardDestory(AllData data, Card target){ Result result = new Result(); if (target == null){ result.Add(new Error("ユニットを選んでください")); return result; } if (3 < target.power){ result.Add(new Error("パワー3以下ではありません")); return result; } result.Add(target.Destory); return result; } // すべての相手のユニットに1ダメージ Result CardAoE(AllData data, Card target){ Result result = new Result(); foreach(var unit in data.oppoinent.units){ result.Add(unit.Damage(1)); } return result; }

投稿2019/12/16 03:35

izmktr

総合スコア2856

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

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

0

すいません、投稿場所を間違えました。

投稿2019/12/15 21:04

編集2019/12/15 21:34
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

退会済みユーザー

退会済みユーザー

2019/12/15 21:23

すいません、投稿場所を間違えました。スマホで修正できないのであとで文面は消しておきます。
退会済みユーザー

退会済みユーザー

2019/12/15 21:35

スマホでも修正できました(事由を書いてなかったのでエラーが出ているだけでした) お騒がせしました。
guest

0

プログラムの規模感と周囲の部分の設計に寄るので何とも言えませんが「増えたときに対応できない」とある点からそこそこ規模があるものと考えますと、やはり継承を使った方が良いでしょう。
抽象クラスとしてCardを用意してやり、それを継承する形で具体的な効果をもったカードを実装するのが良いと思います。

この手の質問はUnityに限らない(というより質問内容にUnity要素がない)のでUnityタグはつけない方が答えが集まりやすいかもしれません。

投稿2019/12/15 12:47

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

Zuishin

2019/12/15 13:10 編集

継承だと複合的な効果がある場合に困ります。(回復の効果を持つクラス、ダメージ無効化を持つクラスの二つのクラスを作った場合、その両方の効果を持つクラスを作るのが難しくなります) キーワードとしては継承よりインターフェースの方が重要だと思いますが、この場合はインターフェースも使わず、単純にすべてのカードに全ての効果を持たせ、個々の効果を無効にするには効果値を 0 にするなどでいいのではないかと思います。 その場合はインスタンスのプロパティを変化させるだけなので、クラスは一つで事足ります。そうすればソースコード中ではなくカードの個性をデータベースやファイルに保存できます。
退会済みユーザー

退会済みユーザー

2019/12/15 14:42

抽象メソッドを個々のクラスで実装するのでその問題は起きないかと(全カードに共通する他のメンバの有無が分からないので実質インターフェース的) 大規模なカードゲームだと複数のステータスの数値変化では対応不能な複雑な効果を持つカードも存在する、あるいは追加したくなるのではないかと思うので、ご指摘の管理方式は管理しやすい反面拡張性に制限があるかと(例えば~の数に応じて効果値が変動するなど) とはいえ確かに全カードのクラスを定義するのも過剰で大変なので、ご指摘いただいた内容もとり入れて、多項目数値変化など類似の効果を持つものは単一のクラスとし、パラメータで対応、そうでないものは個別定義とし、プロトタイプ化して管理するのは如何でしょうか。 ※今回インターフェースでなく抽象クラスで示した理由は、個人的に物に付随する概念を扱うときは抽象クラスを、動作に付随する概念を扱うときはインターフェースを使うようにしてるからで、それ以上の意味はございません。
Zuishin

2019/12/15 15:38 編集

例えば回復系のカードと停止系のカードを実装した後で両方の特徴を持つカードを実装したくなった時に、両方の親を持つわけにはいかないので困ります。 なるべく分岐させず、特殊なメソッドが必要ならインターフェースを使い、効果値で対応できるものは効果値で対応するのが後々困らないんじゃないでしょうか。 ここは継承より合成・委譲を使う典型的な例だと思います。むしろ継承を使わなければならないケースが思い当たりません。 特殊能力はカードが「持っている」ものであり、「カード is a 特殊能力」ではないと思います。「回復カード is a カード」という設計をしていたら、そのうちうっかり「カバンを持っている人 is a 人」と、「人」を継承した「カバンを持っている人」を作り出しそうになります。
退会済みユーザー

退会済みユーザー

2019/12/15 21:33

多重継承が不可能なのは当然存じております。 >なるべく分岐させず、特殊なメソッドが必要ならインターフェースを使い それだと結局のところ特殊カード各々に対して個別のインターフェースを用意する必要があり カードの効果を発動させる部位に if (card is Ixxxx) といった判定をさせる必要があるので 結局の分岐が必要な上カード効果実行部とカードの種類(=特殊インターフェース)の関係性が密になってしまいます。 それよりは発動部分はカードがなんであれ抽象Cardクラスの発動メソッドに、場の情報を渡して呼ぶことで効果を発揮できるように設計しておけば、カードを追加・変更する際に呼び出し元の記述変更が不要です。 そもそもの話として「カード以外にもその特殊能力を発揮することがある」のでなければinterfaceとする意義は薄いでしょう(カード間でどの程度共通するinterfaceを想定されているのかによりますが) >特殊能力はカードが「持っている」ものであり、「カード is a 特殊能力」ではないと思います。 その理屈であれば、特殊能力はinterfaceではなく抽象クラス化してDIで対応するべきかと思います。 特定の特殊効果が使いまわされることが多いのであればこれ一番良いかもしれません。 個人的にはカードゲームにおいて効果発動系のカードとその特殊能力は系統にこそまとめられても基本ほぼ1対1対応になってしまうのではないか、と考えてDIまでは提案しておりませんが(ご指摘の数値変化は確かに1対1とは言い難いので同じクラスから生成した方がよいと思います) 思ったのはイメージしてるカードゲームが異なるから設計が変わってきているのではないでしょうか。
Zuishin

2019/12/15 21:36

カードではなく普通はゲームクラスとそれに委譲された効果クラスがカードやプレイヤーなどのプロパティを見て様々な効果を判定すると思います。プレイヤーやシーンに影響を及ぼす効果をカードに持たせるのはやりすぎではないでしょうか。
Zuishin

2019/12/15 21:48 編集

例えばデバフ効果をカードに持たせる場合、そのカードはプレイヤークラスに依存します。 しかし、その効果を効果クラスとして独立させ、カードのプロパティとして実装すれば、ゲームクラスはカードにどんな効果があるのか列挙して、その効果に、効果クラスの持つインターフェースに応じて対戦中のプレイヤーとカードやその他を渡すことができます。カードは効果を複数持っていますが、それが何の効果かは知りません。 これをカードの継承でカード自身に効果を発揮させようとすると、カードはゲーム中のほぼ全てに依存しなければならなくなりますし、複数の系統の違うカードクラスが同じ効果を持つことができません。 カードは「効果を複数持つ」というインターフェースを実装するだけで十分ではないでしょうか。
退会済みユーザー

退会済みユーザー

2019/12/15 23:27

やはりカードゲームに対するイメージの違いで設計が変わってると思います。 私のイメージではトレーディングカードゲームのような物を想定しており >ゲームクラスはカードにどんな効果があるのか列挙して ここが膨れ上がります(効果の組み合わせで用が足りないくらい複雑な前提条件と効果を持つ物が多数ある) >カードはゲーム中のほぼ全てに依存しなければならなくなり これについても、相手の手札や場の状況に複雑に関係する前提条件や効果を視野に入れているので、場の状況を参照せざるを得ないのはいずれにせよ避けられないと思います。 トレーディングカードゲームでは場の構成要素(手札、山札、トラッシュなど)の有無はゲームルールが劇的に変わらない限りは基本変更されませんし、新しい要素が追加されても過去のカードとの整合性をとるために通常ゲームルール上の後方互換性は保たれます。 原則的にメソッドまたはクラスには必要な情報のみを参照させるべきではありますが、こういった背景からカードの場の状況の仕様変更に対する依存性問題が発生しないことから、場の状況自体を参照させる形でバリエーション豊かなカードを追加しやすいようにした方がよいかと考えた次第です。 >カードではなく普通はゲームクラスとそれに委譲された効果クラスがカードやプレイヤーなどのプロパティを見て様々な効果を判定すると思います。 その場合ゲームクラスがカードに依存してしまい、特殊な効果を持つカードが追加しにくくなります。 また、特殊効果がカードによって千差万別なので効果クラスが何かのカードという概念と1対1対応してしまうことも多いと思います。 また効果クラスの発動I/Fが統一されていないと結局is判定の列挙につながります。 これはもうやはりカードゲームの規模とルールの想定の違いの問題ではないでしょうか。 (どんな前提でも絶対に最適と言える設計はないと思いますので...)
退会済みユーザー

退会済みユーザー

2019/12/15 23:29

ゲームクラスはあくまでカードの効果を発動するだけ、ゲームの流れを管理するだけで、どういう効果が存在するのかとか、プレイヤーの状況はどうかというじょうほうとはむかんけいでいたいというのが私のスタンスです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問