【Unity】カードゲームのデッキを作成してシャッフルする

Unity

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を新規作成します。

UnityのCreateメニュー
「Create Deck」というコマンドが追加

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

CardLiistを設定する

↑今回は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するだけでいいですね。

実際には全部引き終わった場合の分岐を追加してあげる必要があるかなと思います。

コメント

タイトルとURLをコピーしました