背景
現在オンラインPvP型デジタルカードゲーム(例:シャドウバースやハースストーン)の開発を行っております。
自身のカードゲームのシステム構成に関しては、以下の通りです。
- クライアント:Unity + C#
- サーバ:Golang
- 通信プロトコル:Websocket
ゲームは、サーバクライアント間で通信データを受け渡しすることで、進行していきます。
カードの種類は、100枚くらいで、今後増やしていきたいと考えています!
質問内容
質問内容としては、「カード固有のアビリティを、ゲーム中にどう生成するか」に関してです。
各カードの情報は、ゲーム外だとJSONにまとめています。以下の様な形です。
json
1{ 2 "card_id_1": { 3 "card_name": "スラッシュファイター", 4 "cost": 3, 5 "power": 3, 6 "health": 2, 7 ..., 8 }, 9 "card_id_2": { 10 "card_name": "ビーストハンター", 11 "cost": 5, 12 "power": 5, 13 "health": 5, 14 ..., 15 }, 16 ... 17}
ゲーム開始時に、サーバサイドのGolangによってこのJSONファイルを読み込み、各カードをインスタンス化させます。
ここで、card_name
やcost
、power
のような基本パラメータは、どのカードでも持ちうるものなので、1つのクラスのフィールドとして定義可能です。
しかし、アビリティに関しては、カード毎に様々な実装が考えられるため、1つのクラスからインスタンス化させることは難しいかな?と感じました。
そこで、以下の2つの方法を考えました。
考えてみたこと
① Go側で各カードの専用クラスを作る
各カードの専用クラスを作ることで、カードそれぞれのアビリティを実装できるようにする方法です。
ただし、効果の呼び出し側で相手がどんなクラスのインスタンスなのかは意識しなくていいように、インタフェースを用います。
イメージとしては、以下のような感じです。
go
1type Ability interface { 2 OnCardPlayed() // カードプレイ時に発動したい効果を実装するメソッド 3 OnCardDestroyed() // カードが破壊された時に発動したい効果を実装するメソッド 4 OnYourTurnStarted() // 自身のターン開始時に発動したい効果を実装するメソッド 5 ... 6}
go
1// カード「スラッシュファイター」の専用クラス 2// 多態性を実現するために、Abilityインタフェースを満たす様にメソッドを実装する 3type SlashFighter struct { 4} 5 6func (card *SlashFighter) OnCardPlayed() { 7 // 固有処理の実装 8} 9 10func (card *SlashFighter) OnCardDestroyed() { 11 // 固有処理の実装 12} 13 14func (card *SlashFighter) OnYourTurnStarted() { 15 // 固有処理の実装 16} 17 18...
go
1// カード「ビーストハンター」の専用クラス 2// 多態性を実現するために、Abilityインタフェースを満たす様にメソッドを実装する 3type BeastHunter struct { 4} 5 6func (card *BeastHunter) OnCardPlayed() { 7 // 固有処理の実装 8} 9 10func (card *BeastHunter) OnCardDestroyed() { 11 // 固有処理の実装 12} 13 14func (card *BeastHunter) OnYourTurnStarted() { 15 // 固有処理の実装 16} 17 18...
こうすることで、各カードの固有アビリティを、各カードの専用クラス内に自由に実装できます。
ここでの課題は、
- カード情報が記載されたJSONから、どのようにして各カードの専用インスタンスを生成するべきか
ということです。
1つ考えられるのは、JSONに記載しておいたクラス名の文字列(以下のようなイメージです)から、
動的にクラス名に対応するインスタンスを生成することです。
json
1 "card_id_1": { 2 "card_name": "スラッシュファイター", 3 "cost": 3, 4 "power": 3, 5 "health": 2, 6 "ability_class": "SlashFighter" // このようにアビリティのクラス名を文字列として設定しておく 7 },
この場合、Reflect
のようなリフレクション機能を用いることが想定されますが、この機能は遅いからあまり使わないほうがいい?という記事をちょこちょこ見ます。。。
あるいは、アビリティクラス名とアビリティクラスを紐づけたmap(連想配列)を予め用意しておき、ゲーム中は、そのmapのKeyにアビリティクラス名を入れることで、Valueとしてのアビリティクラスを取得してインスタンス化する、という方法もあります。(ただしこちらは、カードの種類が増加していくと、mapが持つ情報も肥大化していくので、設計上あまり良くはないかもです)
② Luaで各カードの専用スクリプトを作る
もう1つは、Luaのような組み込み可能なスクリプト言語を使って、そちらのスクリプトでカードのアビリティを実装する方法です。
こちらに関しては、各カードに対応するLuaスクリプトを作成します。
SlashFighter.lua
やBeastHunter.lua
といった感じです。
これらのスクリプトをゲーム中に読み込んで、中に記載してあるロジックを実行するという形式です。
Luaに関しては、スクリプトファイル名からスクリプトを指定して実行する(LuaState.DoFile("Script Name")
のように!)ことが簡単にできるので、①のようなリフレクション機能を使う必要がないと思っています。
カード情報が記載されたJSONでは、各カードに対応するLuaスクリプト名(文字列)を記述しておき、ゲーム中はLuaスクリプト名からスクリプトファイルを実行するという設計が実現できます。
ただしこちらでの課題は、
- GoとLuaを繋ぐ外部パッケージが別途必要(
gopher-lua
のような素晴らしいパッケージはある) - GoとLuaでデータの受け渡しが必要
- そもそもGoとLuaを繋ぐ様なユースケースをあまり見ないので困ったときの調査が難しそう。。。
というように、①と比べるとGolangだけで完結できないため、少し複雑になっちゃうかな...と感じています。
結論
①でも②でも、カード固有のアビリティを実装するという点では自由にできそう、、、と思っているのですが、そのアビリティをゲーム中にどう生成(インスタンス化)するかに関しては、なかなか考えがまとまらず。。。
個人的には、できれば①のようにGolangで完結させたいと考えているのですが、固有アビリティをスクリプトに任せることができる②のような方法もありなのかも、、、と思っています。
「こっちのほうがいい」や「こういう方法もある」など、アドバイスいただけるととても嬉しいです。
よろしくお願い致します!
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。