Unity2Dでカードゲームを作っているのですが、ほぼどんなカードゲームを作るにしても以下のロジックは必要になるかと思うので備忘録として記載してみたいと思います。
- デッキを作成する
- 作成したデッキをシャッフルする
おそらくデッキの内容はカードのIDのリストで管理することが多いと思うので、それをシャッフルするという動作を作成したいと思います。
デッキの情報はscriptableobjectで作成するのがオススメ
デッキのデータをどうもたせるかということですが、オンラインゲームの場合はDBを使うのが一般的だと思います。
自分はオフラインで使用するexeを配布する想定なのでDBは使わないようにしました。その際にコードにデータをもたせるのは、いけていないので
- CSV
- XML
- json
- scriptableobject
などに持たせることを考えました。
それぞれメリット・デメリットがあるかと思いますが、scriptableobjectに持たせるとデバッグが非常に容易なためscriptableobjectを採用することにしました。
(ゲームを止めてオブジェクトの値を変更できるため)
まずデッキ用のscriptableobjectを以下のコードで定義します。
using UnityEngine;
using System.Collections.Generic;
[CreateAssetMenu( menuName = "Create Deck", fileName = "Deck" )]
public class Deck : ScriptableObject
{
public List<string> cardlist;
}上記を保存するとUnityのCreateメニューに「Create Deck」というコマンドが追加されるので、こちらを押してscriptableobjectを新規作成します。


とりあえず「Resources」フォルダの中に「Deck1」という名前でscriptableobjectを作成し、そのカードリストに値を入れていきます。

↑今回は1~10というカードを保持している10枚のデッキを作成しました。
scriptableobjectから実体を作る
次に前項で作成したscriptableobjectを実体化させて、変数に入れてみます。
「Resources」フォルダの中に「Deck1」という名前で作成したので以下のようにロードをします。
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
void Start()
{
// scriptableobjectから実体を作る
Deck deck = Resources.Load<Deck>("deck1");
List<string> cardlist = deck.cardlist;
for (int i = 0; i < cardlist.Count; i++)
{
// デッキの内容をコンソールに表示する
Debug.Log(cardlist[i]);
}
}
}
実行結果

上記のように1~10のStringのListを作成することが出来ました。
デッキをシャッフルする
上記を行っただけですと、当然毎回カードが同じ順番になってしまいます。
ゲームを作る場合、ランダムな順番でドローする事がほとんどだと思いますので、デッキをシャッフルするロジックも作ります。
今回は配列ではなくListを使っているので以下のように書けるかなと思います。
using System;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
void Start()
{
// scriptableobjectから実体を作る
Deck deck = Resources.Load<Deck>("deck1");
// カードをシャッフルする
List<string> cardlist = deckInit(deck.cardlist);
for (int i = 0; i < cardlist.Count; i++)
{
// デッキの内容をコンソールに表示する
Debug.Log(cardlist[i]);
}
}
private List<string> deckInit(List<string> _cardlist)
{
System.Random _random = new System.Random((int)DateTime.Now.Ticks);
string temp;
for (int i = 0; i < _cardlist.Count; i++)
{
int index = i + (int)(_random.NextDouble() * (_cardlist.Count - i));
temp = _cardlist[index];
_cardlist[index] = _cardlist[i];
_cardlist[i] = temp;
}
return _cardlist;
}
}
実行結果

これでデッキをシャッフルすることが出来ました。
こんな感じのを元にしてデッキクラスを作成してゲーム内で使用しています。
カードをドローする
上記はただの文字列の配列ですが、カードを引く度にその上から配列のデータを抜き出していかないといけません。
そこでドローを行うメソッドを作成します。
using System;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
void Start()
{
// scriptableobjectから実体を作る
Deck deck = Resources.Load<Deck>("deck1");
// カードをシャッフルする
List<string> cardlist = deckInit(deck.cardlist);
// デッキからカードを引いてログに出力する
Debug.Log(draw(cardlist));
}
private List<string> deckInit(List<string> _cardlist)
{
System.Random _random = new System.Random((int)DateTime.Now.Ticks);
string temp;
for (int i = 0; i < _cardlist.Count; i++)
{
int index = i + (int)(_random.NextDouble() * (_cardlist.Count - i));
temp = _cardlist[index];
_cardlist[index] = _cardlist[i];
_cardlist[i] = temp;
}
return _cardlist;
}
public string draw(List<string> _cardlist){
string ret = _cardlist[0];
_cardlist.Remove(ret);
return ret;
}
}
実行結果

既にシャッフルされているので、単純にカードの0番目を取得して、それをRemoveするだけでいいですね。
実際には全部引き終わった場合の分岐を追加してあげる必要があるかなと思います。


コメント
参考にさせていただいているのですが、実体を作る部分で詰まってしまいました。
何度やっても、
List cardlist = deck.cardlist;
の場所でNullReferenceExceptionエラーが出てしまいます。
コピーペーストでやってもうまくいきませんでした。
可能であれば解決方法をご教授いただけませんでしょうか。
NullReferenceExceptionエラーは(“deck1”);」の行で
何も格納されていない変数を参照した時に出るらしいので
「Deck deck = Resources.Load
scriptableobjectが見つかってないと思います。
Resources.Loadは、Resourcesというフォルダの下にあるファイルを探しに行くので
作成したscriptableobjectが
Resourcesフォルダの下にない可能性があります。
今回の例だと、下記画像のようにDeck1をResourcesというフォルダの下に置く必要があります。
仮にResourcesではなくTestというフォルダの中に置きたいなら以下コードですかね。
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class Test2 : MonoBehaviour(“Test\\Deck1”); cardlist = deck.cardlist;
{
void Start()
{
// scriptableobjectから実体を作る
Deck deck = AssetDatabase.LoadAssetAtPath
List
for (int i = 0; i < cardlist.Count; i++) { // デッキの内容をコンソールに表示する Debug.Log(cardlist[i]); } } }