ロジックを考える


四川省(二角取り)のルールは、隣り合っている場合と、2つの牌を結ぶ空白が3本以内の直線でつながっている場合に取ることができる、となっています。図にすると、下のような場合にマッチング成立となるわけです。

 

さて、これをプログラム的に表すとどういうことになるか?

1.Aの上下左右にBがあるか?

2.1でなければ、Aの右に空白マスがあるか。

3.その場合、その上下にあるか。

。。。と考えることもできそうです。

 

より一般化して表せないか?

 

AからBへの移動としては、ヨコタテヨコ、あるいはタテヨコタテの2種類だけです。

図の左上のケースだと、ヨコ2、タテ2、ヨコ2です。

その下なら、タテ1、ヨコ4、タテ2です。

隣にある場合は、ヨコ1、タテ0、ヨコ0とか、タテ1、ヨコ0、タテ0とか。

同じ高さにある場合は、ヨコ3、タテ0、ヨコ0とか。

 

 

 

であれば、下の図のように考えることができそうです。


 

 

左上図のように、たとえばヨコタテヨコの場合で言うと、

1.Aの左右にどこまで空白マスがあるか(青色)

2.Bの左右にどこまで空白マスがあるか

3.空白マスに重複部分があるか(茶色)

4.その重複部分に空白マスがまっすぐ続く場所があるか

 

ということでよさそうです。タテヨコタテの場合は、左右を上下に変えればよい。

 

上下左右で隣り合っている場合や、同じ高さにある場合も、AとBもまた空白マスだと考えればよさそうです。

 

 


マッチング判定プログラム(一番難しいところ)

まずは先ほど作りかけたIsMatchingPair関数の形を整えていきましょう。

冒頭にindex1とindex2というのを作っておきます。Vector2Int型というものです。Intを2つ保持します。

そして、BoardSetUpスクリプトを使えるようにしておきます。

private Vector2Int index1;// 一つ目にピックアップした牌(tile1)の座標

private Vector2Int index2;

[SerializeField] private BoardSetUp boardSetup; // ※あとでUnityでアタッチするのを忘れずに!

 

 bool IsMatchingPair(GameObject tile1, GameObject tile2)

 {

     // ペアのマッチングロジックを実装

     // タグと番号の一致を確認し、一致している場合は経路探索。

     if (tile1.tag == tile2.tag && tile1.GetComponent<MahJongPieScript>().number == tile2.GetComponent<MahJongPieScript>().number)

     {

         // 2つの牌の座標を取得しておく。以降、これを使ってマッチング判定する。

         index1 = GetTileArrayIndex(tile1);// これはVector2Intです。

         index2 = GetTileArrayIndex(tile2);

       

 

         //AとBのタグをEmptyにする。その理由はあとでわかりますが、もう書いちゃいます。

         tile1.tag = "empty";

         tile2.tag = "empty";

    

         // A から B への経路を探すメソッドを呼び出す

                  // ヨコタテヨコの場合。関数名の最後をXYXにしてあります。引数に上でセットした変数を使ってます。

         if (HasValidPathBetweenTilesXYX(index1, index2)) //このHasValid・・・はbool型なのでtrueかfalseになってます。

         {

             return true; // 経路が見つかれば true

         }

     //  タテヨコタテの場合。関数名がYXYです。

         else if (HasValidPathBetweenTilesYXY(index1, index2))

         {

             return true;

         }

         

     }

     

     return false; //経路無しなら、false

 }

 

 

 // 配列の中身を見ていって、該当する麻雀牌の入っている座標を返します。

Vector2Int GetTileArrayIndex(GameObject tile) // Vector2Int型の返事を返す関数

{

     

    for (int y = 0; y < boardSetup.tileArray.GetLength(0); y++) //BoardSetUp関数の配列を呼び出してます。

    {

     //GetLength(0)が一番目の要素の数、つまり10、(1)は二番目の要素の数19です。ベクトルと配列で頭が混乱しますね。

 

        for (int x = 0; x < boardSetup.tileArray.GetLength(1); x++) 

        {

            if (boardSetup.tileArray[y, x] == tile)

            {

                return new Vector2Int(y, x);  // yとxに注意です。横軸をxにしてます。

            }

        }

    }

    return Vector2Int.zero; // 該当なしなら、ゼロを返すようにしてます。ありえないけど。

}

 

 

// 肝心なところです。とりあえずメモ書きだけ。麻雀牌2つの座標を引数に、経路探索します。

// まずXYXタイプのケースを考える

bool HasValidPathBetweenTilesXYX(Vector2Int tile1Index, Vector2Int tile2Index) //bool型です。間違えないように!

{   

    

    //二つの麻雀牌のもっとも遠い左右の空白セルのX座標(横軸)を調べる。

    

    //重複部分があるか調べる準備

  

    // 経路が見つかったらtrue

  return true; // とりあえず。

   

    //見つからなければfalse

}

 

    // YXYタイプのケースを考える

bool HasValidPathBetweenTilesYXY(Vector2Int tile1Index, Vector2Int tile2Index)

{

 

    //二つの麻雀牌のもっとも遠い上下の空白セルのY座標(縦軸)を調べる。

 

  //

    // 経路が見つかったらtrue

  return true; // とりあえず。

   

    //見つからなければfalse

}

 

ここまで書きました。

Unityエディターにもどって、GameBoardをBoardSetUpのところにアタッチしておいてください。

 

さあ経路探索のスクリプトを書いていきます。

まずは選んだ麻雀牌の左側にどこまで空白マスがあるか調べるスクリプトです。

//選んだ麻雀牌の左側で、ほかの牌があるx座標を調べる。

int GetTileXPositionToLeft(Vector2Int tileIndex) // Int型です。

{

    // 自分の左のマスから0まで調べます。0は配列の端っこ、0列目ということです。x--に注意。

    for (int x = tileIndex[1] - 1; x >= 0; x--) // [1]というのが2番目の要素、つまり横軸の情報を指しています。

    {

     //もしそのマスに何か入っていれば。

        if (boardSetup.tileArray[tileIndex[0], x].tag != "empty")//配列は1番目の要素が高さを表していることに注意。

        {

            return x + 1; // 牌の左側で最も遠い空白ますのx座標を返します。

        }

    }

 

    return 0; // 左側に牌が存在しない場合。0列目まで空っぽということです。

}

同じ要領で、右側、上側、下側の関数です。

 

   int GetTileXPositionToRight(Vector2Int tileIndex)

   {

       // Aの右側にある牌のX座標を取得

       for (int x = tileIndex[1] + 1; x < boardSetup.tileArray.GetLength(1); x++)

       {

           if (boardSetup.tileArray[tileIndex[0], x].tag != "empty")//X座標を調べるときは配列の2番目の要素を変動させる。

           {

               return x-1; // 右側にある牌のX座標を返す

           }

       }

 

       return 18; // 右側に牌が存在しない場合

   }

 

   //YXYタイプの判定・・・・・・上下のチェックーーーーーーーーーーーー

 

   int GetTileYPositionToDown(Vector2Int tileIndex)

   {

       // 下側にあるほかの牌のy座標を取得

       for (int y = tileIndex[0] - 1; y >= 0; y--)

       {

           if (boardSetup.tileArray[y, tileIndex[1]].tag != "empty") //X座標は自身の位置で固定です。[1]

           {

               return y + 1; // 牌の下側で最も遠い空白

           }

       }

 

       return 0; // 下側に牌が存在しない場合

   }

 

   int GetTileYPositionToUp(Vector2Int tileIndex)

   {

      

       // 上側にある牌のy座標を取得

       for (int y = tileIndex[0] + 1; y < boardSetup.tileArray.GetLength(0); y++)

       {

           if (boardSetup.tileArray[y, tileIndex[1]].tag != "empty")

           {

               return y - 1; // 牌の上側で最も遠い空白

           }

       }

       return 9; // 上側に牌が存在しない場合

   }

 

ここまで書いたら、HasValidPathBetweenTilesXYXに書き加えていきます。

 

bool HasValidPathBetweenTilesXYX(Vector2Int tile1Index, Vector2Int tile2Index)

    //二つの麻雀牌のもっとも遠い左右の空白セルの横軸(X座標)を調べる。

    int leftA = GetTileXPositionToLeft(tile1Index); //一つ目の麻雀牌の左側をチェック

    int rightA = GetTileXPositionToRight(tile1Index); //右側

    int leftB = GetTileXPositionToLeft(tile2Index); //二つ目の麻雀牌の左側をチェック

    int rightB = GetTileXPositionToRight(tile2Index); //右側

  

   //重複部分があるか調べる準備。Mathfのfは小文字です。

    int leftX = Mathf.Max(leftA, leftB); // ABの左側にある空白のうち、より右側にあるものを選ぶ。

    int rightX = Mathf.Min(rightA, rightB); // ABの右側の空白のうち、より左側にあるもの

  

    // A(一つ目のタイル)の方が左にある場合(同じ列にある場合も含む)、

  // かつAの右側の空白が少なくともBの左の空白より右側にあること。つまり、重複しているということ。

  //   Aの右側の空白が、Bの左の空白に届いていない場合は重複部分が無いということです。

    if (index1[1] <= index2[1] && rightA >= leftB)

    {

        // マッチング成立時に true を返す。

        return(HasVerticalBlanksInRectangle(leftX, rightX)); // 経路探索関数です。下にあります。

    }

    else if (index1[1] > index2[1] && rightB >= leftA)//A(一つ目のタイル)が右側にある場合

    {

        // マッチング成立時に true を返す。

        return(HasVerticalBlanksInRectangle(leftX, rightX));

      

    }

    else//上記条件を満たさない場合は、経路無しなのでfalse

    {

        return false;

    }

}

 

// 指定された長方形の範囲内でタテに直線で空白マスが続いているかを確認

bool HasVerticalBlanksInRectangle(int Left, int Right) // bool型です。

{

  //  引数(left, Right)で長方形の左右の座標が分かっているので、

        //    長方形の高さを計算しておく。

    int minY = Mathf.Min(index1[0], index2[0]); // AとBの低い方

    int maxY = Mathf.Max(index1[0], index2[0]); //AとBの高い方

    int absoluteValue = maxY - minY; // 高さ

    int emptyCellCount; // emptyタグのオブジェクトを数えます。

              //  IsMatchingPair関数でAとBをemptyタグにしておいた理由です。自身もカウントできます。

 

    for (int x = Left;  x <= Right;  x++) 

    {

        emptyCellCount = 0;

 

        for (int y = minY; y <= maxY; y++)

        {

            if (boardSetup.tileArray[y, x].tag == "empty") // 配列の中身(y、x)に注意。

            {

                emptyCellCount++; // タグがemptyだったら加算。

         

          //加算していって高さ+1になったら、true。自身の位置から数えるので+1

                if (emptyCellCount == absoluteValue + 1) 

                {

                    return true;

                }

            }

        }

    }

 

    // タテに直線で空白マスが続いていない場合。

    return false;

}

 

次は、YXYの方です。同じようなものですが配列の座標に注意です。

bool HasValidPathBetweenTilesYXY(Vector2Int tile1Index, Vector2Int tile2Index)

{

 

    //二つの麻雀牌のもっとも遠い上下の空白セルのY座標(縦軸)を調べる。

    int downA = GetTileYPositionToDown(tile1Index);

    int upA = GetTileYPositionToUp(tile1Index);

    int downB = GetTileYPositionToDown(tile2Index);

    int upB = GetTileYPositionToUp(tile2Index);

 

    int downY = Mathf.Max(downA, downB);

    int upY = Mathf.Min(upA, upB);

  

    //A(一つ目のタイル)の方が下にある場合(同じ列にある場合も含む)、かつ空白マスに重複がある場合。

    if (index1[0] <= index2[0] && upA >= downB)

    {

               return (HasHorizontalBlanksInRectangle(downY, upY));

    }

    else if (index1[0] > index2[0] && upB >= downA)//A(一つ目のタイル)が下にある場合

    {

        // マッチング成立時に true を返す。

        if (HasHorizontalBlanksInRectangle(downY, upY))

        {

           

            return true;

        }

        else

        {

            return false;

        }

    }

 

 

    else//上記条件を満たさない場合は、経路無しなのでfalse

    {

        return false;

    }

 

}

 

 // 指定された長方形の範囲内でヨコに直線で空白マスが続いているかを確認

 bool HasHorizontalBlanksInRectangle(int Down, int Up)

 {

     int minX = Mathf.Min(index1[1], index2[1]);

     int maxX = Mathf.Max(index1[1], index2[1]);

     int absoluteValue = maxX - minX;

     int emptyCellCount;

     for (int y = Down; y <= Up; y++)

     {

         emptyCellCount = 0;

         for (int x = minX; x <= maxX; x++)

         {

             if (boardSetup.tileArray[y, x].tag == "empty")

             {

                 emptyCellCount++;

                 if (emptyCellCount == absoluteValue + 1)

                 {

                     return true;

                 }

                 

             }

         }

     }

        

     return false;

 }

 

いやー、大変だ。

前半で書いたHandleTileClick関数を手直ししていきます。

冒頭にフラグを設定。

 

public int remainArrayCount;

public bool isStacked;

 

 // 麻雀牌を選んだ時の操作

 void HandleTileClick(GameObject clickedTile)

 {

 

     if (selectedTiles.Contains(clickedTile))

     {

         // すでに選択された麻雀牌を再度クリックした場合、選択を解除

         selectedTiles.Remove(clickedTile);

         SetTileTransparency(clickedTile, 1.0f); // 1.0f は通常の透明度

         

     }

     else

     {

         // クリックされた麻雀牌をリストに追加

         selectedTiles.Add(clickedTile);

        

         // クリックされた麻雀牌を半透明にする

         SetTileTransparency(clickedTile, 0.5f);

 

         // リストに2つの麻雀牌がある場合、ペアのマッチングを試みる

         if (selectedTiles.Count == 2)

         {

             // tile1,2をセッティング。

             tile1 = selectedTiles[0];

             tile2 = selectedTiles[1];

             //それぞれのタグを取得しておく(あとで戻すときのために)

             string tile1Tag = tile1.tag;

             string tile2Tag = tile2.tag;

             

             

             // 判定処理へ

             if (IsMatchingPair(tile1, tile2))

             {

                 // ペアのマッチングが成立した場合の処理をここに記述

                 Debug.Log("Matching Pair!");

                 

                 // 成立したペアを非表示、かつtagをemptyに。すでになってるけれど念のため。

                 tile1.tag = "empty";

                 tile2.tag = "empty";

                 tile1.SetActive(false);

                 tile2.SetActive(false);

 

                 // 手詰まりかどうかチェックして。

                 //IsStuckCheck(); // 最後に作ります。とりあえずコメントアウトしておく。

                

                 //  残りの牌を数えて。

                 remainArrayCount = 0;

                 foreach (var item in boardSetup.tileArray)

                 {

                     if (item.tag != "empty" && item != null) // 配列の中でemptyじゃないものを数える。

                     {

                         remainArrayCount++;

                     }

                 }

                 if (isStacked && remainArrayCount == 0)

                 {

                     Debug.Log("おめでとう!");

                 }

                 else if (isStacked)

                 {

                     Debug.Log("手詰まりです");

                 }

 

             }

             else

             {

                 // ペアのマッチングが成立しなかった場合の処理をここに記述

                 Debug.Log("Not a Matching Pair");

 

                 // 選択した2つの麻雀牌を再び非選択状態に戻す・・元のタグにするよ。

                 tile1.tag = tile1Tag;

                 tile2.tag = tile2Tag;

          // 色を戻します。

                 foreach (var item in selectedTiles)

                 {

                     SetTileTransparency(item, 1.0f);

                 }

 

             }

 

             // 選択した麻雀牌をリストから削除

             selectedTiles.Clear();

         }

     }

 

 }

 

あと少しです。


手詰まりの判定

最後です。ゲームが終わっているか確認するスクリプトを作ります。

四川省は「打つ手なし」で終わることも多いです。というか、必ずクリアできるように作ることもできるでしょうが、今回はランダムなんです。

 

手詰まり判定は、配列のすべての麻雀牌に対して、今まで書いてきたマッチング判定をすればよいのです。

 

   // 手詰まりチェック(マッチング成立の都度、呼ばれます)

   public void IsStuckCheck()

   {

       List<GameObject> matchingList = new List<GameObject>();

       HashSet<GameObject> processedTiles = new HashSet<GameObject>(); // Listみたいなものだけどちょっと機能が違う。

       // 配列内の各麻雀牌を順に調べる

       for (int y = 0; y < boardSetup.rows; y++)

       {

           for (int x = 0; x < boardSetup.columns; x++)

           {

               GameObject currentTile = boardSetup.tileArray[y, x]; // x、yに注意。

 

               // emptyセルはスキップ

               if (currentTile.tag == "empty" || currentTile == null)

               {

                   continue;

               }

 

               // タグと数字を取得

               string currentTag = currentTile.tag;

               string otherTileTag;

  

               // 同じ麻雀牌を処理済みであれば以降の処理はスキップ。

               if (processedTiles.Contains(currentTile))

               {

                   continue;

               }

 

               //同じタグのものをまず探しておく。

                              // 配列の中身をすべて見てもいいけど、ちょっと省略できる。

               GameObject[] sameSuit = GameObject.FindGameObjectsWithTag(currentTag);

 

               foreach (var item in sameSuit)

               {

                   otherTileTag = item.tag;

                   if (item != currentTile)

                   {

                       if (IsMatchingPair(currentTile, item))//この中身でタグがemptyに変わるので、あとで戻す。

                       {

                           matchingList.Add(currentTile);

                           matchingList.Add(item);

 

                           processedTiles.Add(currentTile);

                           processedTiles.Add(item);

 

                           //タグを元に戻しておく。忘れずに!

                           currentTile.tag = currentTag;

                           item.tag = otherTileTag;

 

                       }

                       else//マッチングしてるものがなければタグを戻す。

                       {

                           

                           currentTile.tag = currentTag;

                           item.tag = otherTileTag;

                       }

                   }

               }

 

           }

       }

       //

       if (matchingList.Count > 0)// 手詰まりではないなら。

       {

           isStacked = false;

          

       }

       else

       {

           // 全ての麻雀牌について調査し、経路が一つもないなら手詰まりである。

           isStacked = true;

          

       }

       matchingList.Clear();

       processedTiles.Clear();

   }

 

さきほどのHandleTileClick関数で、このIsStackCheck関数をコメントアウトしてましたから、コメントアウト//を外します。

 

 

ゲーム開始時にもこの関数を動かす必要がありそうです。

最初の配置で手詰まりの時に、再配置するためです。

BoardSetUpスクリプトを開きます。

 

[SerializeField] private PlayerScript PlayerScript; // PlayerScriptを使えるように。Unityで後でアタッチ。

 

 void SetupBoard()

 {

   // 麻雀牌を配置するスクリプト。前半で書きました。

  // for-forが終わった後に、以下を追記。

     

     PlayerScript.IsStuckCheck();

     if (PlayerScript.isStacked) // もし初期配置で手詰まりなら、再配置。

     {

         Debug.Log("再生成してます");

         SetupBoard();

     }

    

 }

 

Unityエディターに戻って、GameBoardのInspectorにて、Playerオブジェクトをアタッチ。

これで、ゲームはほとんど完成です。テストプレイしてみてください。遊べるようになってると思います。

 


おまけ(ヒントを見せる機能など)

四川省は難しいので、ヒントボタンを作りたいと思います。

マッチングしている牌をプレイヤーに教えます。

 

Unityエディターにて、Hierarchy > 右クリック > UI > Button-TextMeshPro

Canvasオブジェクトが生成されますので、Canvasを選択。

 

 

 

 

Inspector > Canvasにて

Render Mode > Screen Space - Cameraに

Render CameraにMain Cameraオブジェクトをアタッチ

Order in Layer を15くらいに。(あとで理由は分かります)

 

Canvas Scaler > UI Scale Mode > Scale with Screen Size

Reference Resolution > X:1080, Y:1920 //ここはそれぞれのプラットフォームで違いますが。

Screen Match Mode > Expand

 

 

 

Hierarchy > Canvas > Button

Buttonの名前をHintButtonにしておく。

四角いボタンのままでも機能は同じですが、ちょっとかわいらしくするために丸くしてみましょう。

 

Project > Imagesにて右クリック > Create > 2D > Sprite > Circle

 

Hierarchy > HintButton > Inspector > Image > Source Image 右の丸印を押して、Circleの画像を選択

Color を好きな色に。青とか。

 

Hierarchy > HintButton > text(TMP) > Inspector 

文字を"?"とかにして、文字サイズも適度なサイズにいじりましょう。

 

ボタンの位置を決めましょう。画面の右下で、麻雀牌にあたらないところがよいでしょう。

 

スクリプトにいきます。PlayerScriptに書いちゃいましょう。

 

PlayerScriptを開きます。

 

// ヒントボタン(マッチングしている牌を教えてくれます)

//  中身はIsStackCheckとほぼ同じです。コピペすればいいです。最後に色を変える指示を出しています。

public void HintButton() // publicにしてください。あとでボタンオブジェクトにアタッチします。

{

    List<GameObject> matchingList = new List<GameObject>();

    HashSet<GameObject> processedTiles = new HashSet<GameObject>();

    // 配列内の各麻雀牌を順に調べる

    for (int y = 0; y < boardSetup.rows; y++)

    {

        for (int x = 0; x < boardSetup.columns; x++)

        {

            GameObject currentTile = boardSetup.tileArray[y, x];

 

            // emptyセルはスキップ

            if (currentTile.tag == "empty" || currentTile == null)

            {

                continue;

            }

          

            // タグと数字を取得

            string currentTag = currentTile.tag;

            string otherTileTag;

            //int currentNumber = currentTile.GetComponent<MahJongPieScript>().number;

 

            // 同じ麻雀牌を処理済みかどうかをチェック

            if (processedTiles.Contains(currentTile))

            {

                continue;

            }

            //同じタグのものをまず探しておく。

            GameObject[] sameSuit = GameObject.FindGameObjectsWithTag(currentTag);

            

            foreach (var item in sameSuit)

            {

                otherTileTag = item.tag;

                if (item != currentTile)

                {

                    if (IsMatchingPair(currentTile, item))//この中身でタグがEmptyに変わるので、あとで戻す。

                    {

                        matchingList.Add(currentTile);

                        matchingList.Add(item);

 

                        processedTiles.Add(currentTile);

                        processedTiles.Add(item);

                        //タグを元に戻しておく。

                        currentTile.tag = currentTag;

                        item.tag = otherTileTag;

                        

                    }

                    else//マッチングしてるものがなければタグを戻す。

                    {

                        //

                        currentTile.tag = currentTag;

                        item.tag = otherTileTag;

                    }

                }

            }

            

        }

    }

 

  //  ここまでIsStackCheck関数と同じです。

    //マッチング可能なものの色を変える。

    if (matchingList.Count > 0)// 手詰まりではないなら。

    {

        isStacked = false;

   

        foreach (var item in matchingList)

        {

            if (item != null)

            {

         // 牌が青くなります。tileColor.r = 0.5f;で赤い色を半分にしています。

                Color tileColor = item.GetComponent<SpriteRenderer>().color;

                tileColor.r = 0.5f;

                item.GetComponent<SpriteRenderer>().color = tileColor;

 

                // 2秒後に元の色に戻す

                StartCoroutine(ResetHintTileColor(item));

            }

 

        }

    }

    else

    {

        // 全ての麻雀牌について調査し、経路が一つもないなら手詰まりである。

        isStacked = true;

  

    }

    matchingList.Clear();

    processedTiles.Clear();

}

    

IEnumerator ResetHintTileColor(GameObject tile)

{

    yield return new WaitForSeconds(2.0f); // 2秒待つ

    if (tile != null)

    {

        // 元の色に戻す

        Color tileColor = tile.GetComponent<SpriteRenderer>().color;

        tileColor.r = 1f;

        tile.GetComponent<SpriteRenderer>().color = tileColor;

    }

}

 

Unityに戻って、HintButton > Inspector > Button > On Click()に、

Playerオブジェクトをアタッチして、HintButton()を選択する。

 

テストプレイしてみてください。うまくボタンが動きましたか?

 

 

ゲームオーバーメッセージを表示する。

 

ゲームをクリアしたか手詰まりになったらメッセージを表示しつつ、ゲームを再開する機能をつけます。

ボタンを作ります。

 

Hierarchy > UI > Button-TextMeshPro

名前はGameOverButtonにしておきます。

 

さっきと同じ要領で、今後はボタンの画像を麻雀牌の「白」にしてみましょう。

そして、ボタンのサイズを大きめの横長にしてみます。

たとえば Inspector > RectTransform > Width:1200, Height:800とか

位置は画面の真ん中はどうでしょう?

 

次に、ボタンの文字サイズを調整します。

GameOverButton > Text(TMP) > Inspector > TextMeshPro

 

テキストの内容を、仮に「CLEAR!」にしておきます。あとでスクリプトで変えられます。しかし、日本語入力する場合は、ひと手間二手間必要になるので、今回は英語にします。

文字サイズを100くらいにするのはいかがでしょうか? 文字色もはっきりとした黒にしてみましょう。

 

PlayerScriptを開きます。

 

 

using UnityEngine.UI;

using TMPro; // TextMeshProを使うときにはこれを追加。

using UnityEngine.SceneManagement; // ゲームを再開するとき用にこれを追加。

 

public class PlayerScript : MonoBehaviour

{

 

 //ゲームオーバーボタン関係

 [SerializeField] private Image gameOverBtnImage;

 [SerializeField] private Color gameOverColor, gameClearColor;

 [SerializeField] private TMP_Text gameOverMessage;

 [SerializeField] private GameObject gameOverButton;

 

  // Start is called before the first frame update

  void Start()

  {

      gameOverButton.SetActive(false); // ゲーム開始時点では、ボタンには眠っておいてもらいます。

  }

 

//以下はHandleTileClick()の中の判定処理をしているところです。

 

 // 判定処理へ

 if (IsMatchingPair(tile1, tile2))

 {

     // ペアのマッチングが成立した場合の処理をここに記述

     Debug.Log("Matching Pair!");

   

   // ~~省略~~

 

     // 手詰まりかどうかチェックして。

     IsStuckCheck();

    

     //  残りの牌を数えて。

     remainArrayCount = 0;

     foreach (var item in boardSetup.tileArray)

     {

         if (item.tag != "empty" && item != null)

         {

             remainArrayCount++;

         }

     }

     if (isStacked && remainArrayCount == 0)

     {

     // ここから追記部分。

 

         Debug.Log("おめでとう!すごいね");

         gameOverButton.SetActive(true); // ボタンに起きてもらって、

         gameOverBtnImage.color = gameClearColor;  // ボタンの色を変えて

         gameOverMessage.text = "CLEAR!"; // メッセージを入れる。

     }

     else if (isStacked)

     {

         Debug.Log("手詰まりです");

         gameOverButton.SetActive(true);

         gameOverBtnImage.color = gameOverColor;

         gameOverMessage.text = "DEAD LOCK\nTRY AGAIN?"; // 改行は¥nです。

     }

 

 

// 上記はIsMatchingPair内です。コピペするときは気を付けて。

 

 //ゲーム終了後にボタンを押してニューゲームです。

 public void NewGame() // publicに。

 {

     SceneManager.LoadScene("Game"); // "Game"はSceneの名前です。間違えないように。

 }

 

Unityエディターに戻って、いろいろ設定します。

まずはGameOverButton > Inspector > Button > On Click()に、PlayerScriptをアタッチ、NewGame()を選択。

 

 

Player > Inspector > PlayerScript 

GameOverBtnImage > GameOverButton をアタッチ

GameOverColor > 好きな色でいいです。

GameClearColor > 好きな色にしてください。ハッピーな色がよいですね。

GameOverMessage > GameOverButtonについてるText(TMP)をアタッチ

GameOverButton > GameOverButtonをアタッチ

 

 

そういえば、さきほどCanvasのOrder In Layerを15くらいにしましたけど、そうしないと、ゲームオーバーボタンが麻雀牌に隠れてしまうんですね。麻雀牌のOrder In Layerがかなり高くなっているので(配置したときにOrder In Layerをあげているから)。

 

それから、上のGameOverColorの設定で、アルファの値に気を付けてください。0だと透明です。

 


さあ。これで完成です。

Android実機にビルドして遊んでみましょう。

 

File > Build Setting

左下にPlayerSettingというのがあります。これをクリック。

Resolution and Presentation > Orientation > Landscape Right (Leftでも)

 

Other Settings > Identification > Override default package 一度チェックマーク外して、またチェック。

Other Settings > Configuration > Scripting Backend > IL2CPP

                              > Target Architectures > ARM64 (ARMv7は外してもよいと思います)

 

だいたいこんなところでOKかと思います。

Build Settingsに戻って、Scenes In BuildにScenes/Gameが入っていることを確認。

 

スマホを開発者モードにして。

USBケーブルでスマホをPCにつないで。

スマホでUSB接続のなんか画面になったら、「ファイル転送」にして。

そうするときっとUnityがスマホを認識するので、Build Settings > Run Device > Refleshしてスマホを選んで。

右下のBuild And Runボタンをクリック。

ファイル名は何でもいいです。TestGame_1103.apkとか、日付でも入れておくといいかもしれません。

 

終わり!

 

長かった!

 

お疲れさまでした!

 

それでは!

Feel free to Share and Comment!

 

ホームへ戻る。

 

 

 

Profile

string name = "Renoboy";

string message = "《Unity,C#》に詳しくない私による、詳しくない人のためのチュートリアルです。詳しくないので、難しいことは決して教えません。";

 

Appale-Takemuroid