見出し画像

Unityで「しちならべ」を作る(4)

こんにちは「つけらっとゲームス」プログラム担当のとちです。

情報系学科のある専門学校で、外部講師として「七並べ」のシステム設計を授業で扱ったこともあり解答例として設計書を作りながら、Unityで「しちならべ」を作ったお話です(前回分はコチラ ↓ )

「ゲームを作ってみよう!」と考えている方、「ゲーム作りってどんな感じ?」と興味がある方向けの記事です。

ちなみに専門学校の外部講師としての記事はこんなのがあります。ご興味がございましたら以下もご覧くださいね。

前回は七並べのゲーム中に頻繁に使用する「どのカードをクリックしたか?」を説明しました。今回は七並べのルールをゲームで再現する方法を考えていきましょう。



まずはルールの確認

以下は解答例として作った「システム設計書」の「システム概要」の頁。
七並べの代表的なルールと言えば、こんな感じでしょうか?
(お手数をおかけしますが拡大してご覧ください)

システム設計書より

カードデータを管理する

次に必要なのはカードのデータです。
グラフィックは前々回で用意が済んでいますが、誰がその札を持っているのかの判断基準を設ける必要があります。

以下は今回の「七並べ」で使用したスクリプト内の変数配列です。

//─────────────────────────────────────────────────────
//		CardFull	トランプのカードデータ
//─────────────────────────────────────────────────────
public struct CardFull											// 
{																//
    static public int[]        CardID  = new int[53];           //【変数定義】トランプ - カードID
    static public int[]        CardPt  = new int[53];           //【変数定義】トランプ - 数値		[1]A [11]J [12]Q [13]K [15]ジョーカー
    static public int[]        CardMk  = new int[53];           //【変数定義】トランプ - マーク	[1]スペード [2]ハート [3]ダイヤ [4]クラブ [5]ジョーカー
    static public int[]        CardPos = new int[53];			//【変数定義】トランプ - 場所		[0]山札 [1]手札 [2]場
    static public Sprite[]     CardSp  = new Sprite[53];        //【変数定義】トランプ - イメージ  
    static public Sprite       CardBk  = null;					//【変数定義】トランプ - イメージ(裏面)
}																//

二次元配列でもよいのですが、今回は通常の変数配列をそれぞれ53個作り、
index(添え字)で管理しています。

CardFull.CardPosは、その札の場所を意味します。「0」だと山札、「1」だと誰かの手札、「2」だと場に出ている札だとわかります。
CardFull.CardSpは、その札のグラフィックが入っています。
CardFull.CardBkは、全部の札の裏面のグラフィックです。

それ以外の項目は図で説明したほうが把握しやすいでしょう。
というわけで以下の図をご覧ください~。

CardFull の中身を図で表現すると、こんな感じ

手札と「7札」の管理

こちらの配列は「手札」と「パスの残り回数」を管理しています。

//─────────────────────────────────────────────────────
//		PcardSv		プレイヤーのデータ
//─────────────────────────────────────────────────────
public struct PcardSv											//
{																//
    static public GameObject[] Pobj    = new GameObject[14];	//【変数定義】手札のゲームオブジェクト
    static public int[]        PcdID   = new int[14];			//【変数定義】手札のカードID
    static public int          Ppass   = 3;						//【変数定義】パス回数 [8]失格 [9]クリア
}																//
//─────────────────────────────────────────────────────
//		E1cardSv	対戦相手(Com1)のデータ
//─────────────────────────────────────────────────────
public struct E1cardSv											//
{																//
    static public GameObject[] E1obj   = new GameObject[14];	//【変数定義】Com1 - 手札のゲームオブジェクト
    static public int[]        E1cdID  = new int[14];			//【変数定義】Com1 - 手札のカードID
    static public int          E1comID = 0;						//【変数定義】Com1 - Com1の個性
    static public int          E1pass  = 3;						//【変数定義】Com1 - パス回数 [8]失格 [9]クリア
}																//

この画面ではプレイヤーとCom1のみですが、Com2~3の分もあります。
やっぱり二次元配列でもよかったんですが、今回はこんな感じにしました。

PcardSv.PcdIDには「CardFull.CardID」で定義したIDが入ります。
図で表すとこんな感じですね。

手札データを図で表すとこんな感じ
int[] SevenID = new int[4]{0,0,0,0};	//【変数定義】7札のID保持	[0]スペードの7 [1]ハートの7 [2]ダイヤの7 [3]クラブの7

上記は後々使用する「7札」のIDを持っておく配列です。
前述の「CardFull.CardID」に紐づけられているIDがこの配列に入ります。
これも図にすると以下のような感じになります。

SevenIDの中身はこんな感じですね

「7札」は一旦誰かの手札に入りますが、手札を配り終わった後で場に出されます。
SevenID[0~3]で保持している値と「CardFull.CardID」が同じIndex(添え字)のCardFull.CardPosは「2」(場に出された札)となります。


隣り合った数字の札を判断する

七並べといえば最初に各マークの「7」を場に出します。
そして「7」と隣り合った数字の札を出していくことでゲームが進みます。
「8」が場に出たら「9」を出せるようになります。

場に出せないカードはグレイアウトされます

手札は出せるカードを通常表示、出せないカードはグレイに表示されます。
これを判断する方法を考えてみましょう。

まず、場に出せるカードは何種類あるでしょう。
ゲーム開始時、場の中央に4つのマークの「7」が並んでいます。その隣り合っている数字は4つのマークの「6」または「8」の2つです。

つまり、場に出せるカードは4×2=8枚だけとなります。
おっと、ジョーカーはどこでも出せるので+1=9枚が正解ですね。

そこで以下のような配列を準備しました。

int[] PutOKTbl = new int[9]{0,0,0,0,0,0,0,0,0};  // 場に置けるカードIDの保持配列
int   PutOkIx  = 0;                              // 場に置けるカードIDの保持配列用Index

場に置けるカードは最大で9枚なので9個の箱があれば大丈夫でしょう。
では置けるカードのIDをPutOKTblに入れるスクリプトを作りましょう。

PutOKTbl = new int[9]{0,0,0,0,0,0,0,0,0};							// 配列の初期化
PutOkIx  = 0;														// 配列の初期化(index)
for(int SvIx = 0; SvIx < 4; SvIx++)									// マーク4種類(4回ループ)
{
	int wkID   = SevenID[SvIx];										// 各マーク「7札」のID
	int wkDwIx = Array.IndexOf(CMain.CardFull.CardID,wkID);			// 各マーク「7札」のIDのindex
	int wkUpIx = wkDwIx;											// 各マーク「7札」のIDのindex
	int wkMk   = CMain.CardFull.CardMk[wkDwIx];						// 現在処理中のマークを保持
	while(true)														// 「7札」から連続した下の数字を探しに行くループ (6,5,4,3,2,A)
	{
		wkDwIx--;													// ひとつ下のIndex
		if(wkDwIx <= -1){break;}									// ひとつ下のIndexが-1以下ならループを抜ける
		if(CMain.CardFull.CardPos[wkDwIx] == 1)						// ひとつ下のカードの場所が[1:手札]の場合
		{
			PutOKTbl[PutOkIx] = CMain.CardFull.CardID[wkDwIx];		// この札は場に出せる >> PutOKTbl に入れる
			PutOkIx++;												// PutOKTbl用のIndex +1
			break;													// このループを抜ける
		}
		if(CMain.CardFull.CardMk[wkDwIx] != wkMk){break;}			// もしマークが異なったら ループを抜ける
		if(CMain.CardFull.CardPt[wkDwIx] ==    1){break;}			// もしこの札がAだったら ループを抜ける
	}
	while(true)														// 各マークの7札から連続した上の数字を探しに行くループ (8,9,10,J,Q,K)
	{
		wkUpIx++;													// ひとつ上のIndex
		if(wkUpIx >= 53){break;}									// Jokerを除くトランプデータの終端
		if(CMain.CardFull.CardPos[wkUpIx] == 1)						// ひとつ上のカードの場所が[1:手札]の場合
		{
			PutOKTbl[PutOkIx] = CMain.CardFull.CardID[wkUpIx];		// この札は場に出せる >> PutOKTbl に入れる
			PutOkIx++;												// PutOKTbl用のIndex +1
			break;													// このループを抜ける
		}
		if(CMain.CardFull.CardMk[wkUpIx] != wkMk){break;}			// もしマークが異なったら ループを抜ける
		if(CMain.CardFull.CardPt[wkUpIx] ==   13){break;}			// もしこの札がKだったら ループを抜ける
	}
}
if(CMain.CardFull.CardPos[52] == 1){PutOKTbl[PutOkIx] = 53;}		// Jokerは置けることにする

ちょっと長くて小難しいプログラムコードが並んでいますね。
中身を簡単に説明すると、SevenID[0~3]から「7札」のIDを取得し、そのIDから該当するCardFull.CardIDのIndexを取得しています。

場の中央にある7から隣り合い、且つ連続した数字なら、その札は出せるので「7からAに向けて」と「7からKに向けて」のループ2つを、マーク数つまり4回まわしているプログラムとなります。

ループから抜ける条件は…

  1. その札は手札か?

  2. 現在処理中のマークと異なるか?

  3. 現在処理中のマークの始端(または終端)か?

上記条件で出せるカードだと判断できるのは「1.その札は手札か?」です。
出せるカードであればPutOKTbl配列に格納しておきます。

最後にジョーカーの場合は常に出せるカードだと判断しています。

PutOKTbl配列に格納されているカードIDが手札にあったらそのまま表示、
それ以外のカードIDの手札はグレイアウトします。


続きます!

今回は「どのカードが出せるカードなのか?」を判断する方法を考え実装しました。これで七並べの主要ルールのひとつをクリアできましたが…
七並べって意外とルール的にも戦略的にも考える部分があるんですよね。

次回以降はゲーム前のシャッフルの仕方を解決してみましょう。
今回は長くなったのでここまでです。

ちなみに外部講師以外では、普段はこんなゲームをいじっている人です。
もしよかったら以下の記事もご覧くださいませ~。

次回【Unityで「しちならべ」を作る(5)】はコチラです(↓)


この記事が参加している募集

ゲームの作り方

この記事が気に入ったらサポートをしてみませんか?