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

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

詳細はこちら
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

Lua

Luaは、汎用のスクリプト言語の一つで、 移植性が高く、高速な実行速度などの特徴を持ち 手続き型・オブジェクト指向言語としても利用可能で 関数型言語、データ駆動型の要素も併せ持っている言語です。

Q&A

解決済

1回答

2492閲覧

PvP型デジタルカードゲームにおけるカード固有アビリティの生成方法

massu2357

総合スコア17

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

Lua

Luaは、汎用のスクリプト言語の一つで、 移植性が高く、高速な実行速度などの特徴を持ち 手続き型・オブジェクト指向言語としても利用可能で 関数型言語、データ駆動型の要素も併せ持っている言語です。

0グッド

0クリップ

投稿2020/12/28 06:56

編集2020/12/28 07:11

背景

現在オンライン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_namecostpowerのような基本パラメータは、どのカードでも持ちうるものなので、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.luaBeastHunter.luaといった感じです。

これらのスクリプトをゲーム中に読み込んで、中に記載してあるロジックを実行するという形式です。
Luaに関しては、スクリプトファイル名からスクリプトを指定して実行する(LuaState.DoFile("Script Name")のように!)ことが簡単にできるので、①のようなリフレクション機能を使う必要がないと思っています。
カード情報が記載されたJSONでは、各カードに対応するLuaスクリプト名(文字列)を記述しておき、ゲーム中はLuaスクリプト名からスクリプトファイルを実行するという設計が実現できます。

ただしこちらでの課題は、

  • GoとLuaを繋ぐ外部パッケージが別途必要(gopher-luaのような素晴らしいパッケージはある)
  • GoとLuaでデータの受け渡しが必要
  • そもそもGoとLuaを繋ぐ様なユースケースをあまり見ないので困ったときの調査が難しそう。。。

というように、①と比べるとGolangだけで完結できないため、少し複雑になっちゃうかな...と感じています。

結論

①でも②でも、カード固有のアビリティを実装するという点では自由にできそう、、、と思っているのですが、そのアビリティをゲーム中にどう生成(インスタンス化)するかに関しては、なかなか考えがまとまらず。。。

個人的には、できれば①のようにGolangで完結させたいと考えているのですが、固有アビリティをスクリプトに任せることができる②のような方法もありなのかも、、、と思っています。

「こっちのほうがいい」や「こういう方法もある」など、アドバイスいただけるととても嬉しいです。

よろしくお願い致します!

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

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

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

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

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

guest

回答1

0

自己解決

本件に関しまして、サーバーサイド言語を

GolangRuby

とし、スクリプト言語(Ruby)で実装していくことで、実現できそうなイメージが持てました。

いったん、解決イメージが持てたので、終了とさせていただきます。

ありがとうございました!

投稿2021/01/02 18:36

編集2021/01/02 18:38
massu2357

総合スコア17

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問