ゲーム化!tomo_manaのブログ

ゲーム化!tomo-manaのブログ

Unityでゲームを作る方法について紹介しています

ポーカーを作る#1 (Unity2019.4.4f1)

f:id:tomo_mana:20211209123639p:plain
イベントをランダムに発生させる練習として、ポーカーを作ってみようと思います。

ただし、作るポーカーは、完全に乱数というわけではなく、手持ちのカードによって特定の役がちょっと揃いやすくなる、ちょっと不思議なポーカーです。

題して「ちょっとだけそろいやすいポーカー」!!

f:id:tomo_mana:20211117100921p:plain
ゲーム画面


第1回、2回は一般的なポーカーゲームの作り方です。乱数調整は第3回以降に実装していきます。

画面設計

カジノでおなじみのトランプカードゲームであるポーカーは、手札5枚を交換して役を作っていくゲームです。

ゲームオブジェクトの配置

まず、カードとトグルスイッチ5個、ボタン1個を配置して、おおまかな座標を決めます。

f:id:tomo_mana:20211116211317p:plain
おおまかな配置


以下のようにグループ分けしています。

f:id:tomo_mana:20211116232743p:plain
グループの決定

(1) 一対のカードとトグルをひとまとめにします。これは交換可能な最小セット(SelectiveCard)を表します。このセットはVerticalLayoutGroupで体裁を整えます。この最小セットはプレハブにして最終的に削除します。

(2) カードとトグルのセット5枚分をさらにひとまとめにして、スート(CardSuit)というグループにします。スートにはHorizontalLayoutGroupを設定します。

(3) さらに、スートと交換決定ボタン(ButtonChange)を整列させるためにグループにして、VerticalLayoutGroupで囲みます。これは言わばカジノのテーブル(CasinoTable)です。

出来上がった階層構造は以下のようになっています。

f:id:tomo_mana:20211116223452p:plain
Hierarchy - メイン画面

(4) カードとチェックボックスはプレハブにして複製し、体裁を整えた後で消去します。

f:id:tomo_mana:20211117101051p:plain
プレハブ消去

処理の流れ

まずはゲームとしてのポーカーの流れを確認しておきます。

(1) ゲーム開始時:5枚ドローします。
(2) 役を確認します(次回)。
(3) 交換するカードにチェックをつけて、Change! を押します。
(4) チェックが付いているカードを捨てて、捨てた枚数だけドローします。

以降、(2)役の確認、(3)チェック、(4)ドロー、を繰り返します※


※本来のポーカーは1回の交換で終わることが多いですが、交換回数の制限は後から設定できるのと、偏りのある乱数振り出しのシミュレートを兼ねているので、今回はカードが残っている限り、無限に交換できるようにします。


なお、チェックを付けないでChange!を押したら上がりとします(上がり処理は次回以降に)。


ここからは、「(4) チェックが付いているカードを捨てて、捨てた枚数だけドローする」を、さらに細分化します。
(a) チェックが付いているカードを捨てる(交換するカードの確認)
(b) ドローしたカードの決定(乱数の振り出し)
(c) ドローしたカードに基づいて表示を切り替え(イメージ切り替え)

それぞれの処理を、簡単に見ていきます。

チェックが付いているカードを捨てる

Change!が押された時に、チェックが付いていた場合は手持ちのカードを捨て、新しいカードを取得します。トグルのチェックは、Toggle::isOn で確認します。

void カード切り替え()
{
    for( int i = 0; i < 5; i++ ){
        if( トグル.isOn ){
            カード変更();
        }
    }
}

ドローしたカードの決定

カードのドローは、乱数を使用します。Unityでは、System.Random と UnityEngine.Random が使えます。System と UnityEngine の両方を使っている場合、UnityEngine側の乱数を使用する時はUnityEngine.Random で呼び出します。

今回使用したトランプのカードは、たとえばスペードのエースの場合は "card_spade_01" のように、"card_"(定型句)+マーク名+"_"+序数(01~13)という名前になっています。この文字列を乱数から生成します。

今の段階では、とりあえず以下のようにします。
●マーク:カード÷13
●序数:カード÷13 のあまり

string カードのドロー()
{
    int カード = UnityEngine.Random.Range(0f, 51f);  // ジョーカーは後から
    return "card_" + マーク[カード/13] + "_" + (カード%13+1).ToString().PadLeft(2, '0');
}

ドローしたカードに基づいて表示を切り替え

カードを取得できたら、カードのイメージを変更します。

void カード変更()
{
    string カード名 = カードのドロー();
    Texture2D texture = Resources.Load(カード名) as Texture2D;
    image.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
}

コード

スート管理(CardSuit.cs)とイベントトリガ(ButtonChange.cs)を作成します。

ButtonChange はイベントを CardSuit に伝えるだけです。カードの切り替えは CardSuit 内で行います。

スート管理(CardSuit.cs)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CardSuit : MonoBehaviour
{
    [SerializeField]
    GameObject SelectableCardPrefab = default;
    
    Toggle dummyToggle = default;
    Image dummyImage = default;
    
    string[] mark = {"spade", "heart", "diamond", "club"};
    
    Toggle GetToggle(int no)
    {
        if( 0 <= no && no < 5 ){
            return gameObject.transform.GetChild(no).GetChild(0).GetComponent<Toggle>();
        } else {
            return dummyToggle;
        }
    }
    
    Image GetImage(int no)
    {
        if( 0 <= no && no < 5 ){
            return gameObject.transform.GetChild(no).GetChild(1).GetComponent<Image>();
        } else {
            return dummyImage;
        }
    }
    // カードのドロー
    string DrawCard()
    {
        int card = (int)UnityEngine.Random.Range(0f, 51f);  // 52枚(ジョーカー含まず)
        return "card_" + mark[(card/13)] + "_" + ((card%13)+1).ToString().PadLeft(2, '0');
    }
    // カード変更
    void ChangeCard(int i)
    {
        Image image = GetImage( i );
        if( !ReferenceEquals(image, null) ){
            string card = DrawCard();
            Texture2D texture = Resources.Load(card) as Texture2D;
            image.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);
        }
    }
    // カード切り替え
    public void ChangeCards()
    {
        for( int i = 0; i < 5; i++ ){
            if( GetToggle( i ).isOn ){
                ChangeCard( i );
            }
        }
    }
    void Start()
    {
        UnityEngine.Random.InitState(100);
        
        for( int i = 0; i < 5; i++ ){
            GameObject obj = Object.Instantiate( SelectableCardPrefab );
            obj.transform.SetParent( gameObject.transform, true );
            ChangeCard( i );
        }
    }
}

カード交換イベントトリガ(ButtonChange.cs)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ButtonChange : MonoBehaviour
{
    [SerializeField]
    CardSuit suit = default;
    
    void ButtonClicked(int buttonNo)
    {
        suit.ChangeCards();
    }
    void Start()
    {
        Button button = gameObject.GetComponent<Button>();
        // クリックイベントの登録
        button.onClick.AddListener( () => ButtonClicked(0) );
    }
}

作成したコードは、それぞれ CardSuitオブジェクト と ButtonChangeオブジェクト にそれぞれ取り付けます。

動作テスト

公開準備中です。

f:id:tomo_mana:20211117100921p:plain
動作テスト

イラストについて

トランプのイラストは、「いらすとや」さんのイラストを使用させていただきました。ありがとうございます。
www.irasutoya.com


次回は、役の認識と上がり処理を実装します。