見出し画像

[Unity Blocks 11]ブロックスのルールに則ってピースを置く。反転・回転を実装する

次はブロックスのルールである、「ピースの辺同士が接触してはいけない」「頂点のどこかが自分のピースと接していなければならない」部分を実装します。

ボードのスクリプトに追加する

BoardのSetPieceでの確認事項を増やす形にしています。

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

public class Board : MonoBehaviour
{
   public int[,] BoardInfo{get; set;} //ボードのピースの位置を管理する2重配列
   List<int> SetPlayer = new List<int>(); //ピースを置いたことのあるプレイヤーリスト

   void Start()
   {
       BoardInfo = new int[ConstList.BoardSize,ConstList.BoardSize];
   }

   void Update()
   {
   }

   public bool SetPiece(List<int[]> PieceDesign, int[] Pivot, int PlayerNum)
   {
       List<int[]> NewPieceDesign = new List<int[]>();
       foreach (int[] Point in PieceDesign)
       {
           int[] TmpPoint = new int[]{0, 0};

           TmpPoint[0] = Point[0] + Pivot[0];
           TmpPoint[1] = Point[1] + Pivot[1];
           NewPieceDesign.Add(TmpPoint);
       }
       return SetPiece(NewPieceDesign, PlayerNum);
   }

   //ピースを置く。置けない場合はfalseを返し、置けた場合はBoardInfoに書き込んで、trueを返す。
   //初めて置く場合は頂点が接している条件を満たせないので、別関数での確認
   public bool SetPiece(List<int[]> PieceDesign, int PlayerNum)
   {
       foreach (int[] Point in PieceDesign)
       {
           if (!CheckBaseRule(Point, PlayerNum))
               return false;
       }
       bool IsPossible = false;
       foreach (int[] Point in PieceDesign)
       {
           if (!SetPlayer.Contains(PlayerNum + 1))
           {
               IsPossible = CheckStartRule(Point, PlayerNum);
           }
           else
           {
               IsPossible = CheckApplicationRule(Point, PlayerNum);
           }
           if (IsPossible)
               break;
       }
       if (!IsPossible)
           return false;
       foreach (int[] Point in PieceDesign)
       {
           BoardInfo[Point[0], Point[1]] = PlayerNum + 1;
       }
       if (!SetPlayer.Contains(PlayerNum + 1))
           SetPlayer.Add(PlayerNum + 1);
       return true;
   }

   //スタート時はプレイヤーごとのスタート地点が違うので、CheckStartPointsで定義
   //そこに入っているかを確認
   bool CheckStartRule(int[] Point, int PlayerNum)
   {
       int[][] CheckStartPoints = new int[][]{new int[]{0, 0}, new int[]{13, 13}};
       if (Point[0] == CheckStartPoints[PlayerNum][0] && Point[1] == CheckStartPoints[PlayerNum][1])
           return true;
       return false;
   }

   //頂点同士が接しているか
   bool CheckApplicationRule(int[] Point, int PlayerNum)
   {
       int[][] CheckDiagonalPoints = new int[][]{new int[]{1, 1}, new int[]{-1, 1}, new int[]{1, -1}, new int[]{-1, -1}};
       bool IsPossible = false;
       foreach(int[] CheckPoint in CheckDiagonalPoints)
       {
           CheckPoint[1] += Point[1];
           CheckPoint[0] += Point[0];
           if (!Utils.IsPieceInBoard(CheckPoint))
               continue;
           if (BoardInfo[CheckPoint[0], CheckPoint[1]] == PlayerNum + 1)
               IsPossible = true;
       }
       return IsPossible;
   }

   //基本的なルールに則しているか確認
   //重なっていない、ボードからはみ出ていない、自分のピースの辺が接していない
   public bool CheckBaseRule(int[] Point, int PlayerNum)
   {
       if (!Utils.IsPieceInBoard(Point))
           return false;
       if (BoardInfo[Point[0], Point[1]] != 0)
           return false;
       if (!CheckAdjacentRule(Point, PlayerNum))
           return false;
       return true;
   }

   public bool CheckAdjacentRule(int[] Point, int PlayerNum)
   {
       int[][] CheckAdjacentPoints = new int[][]{new int[]{1, 0}, new int[]{-1, 0}, new int[]{0, 1}, new int[]{0, -1}};
       foreach(int[] CheckPoint in CheckAdjacentPoints)
       {
           CheckPoint[1] += Point[1];
           CheckPoint[0] += Point[0];
           if (!Utils.IsPieceInBoard(CheckPoint))
               continue;
           if (BoardInfo[CheckPoint[0], CheckPoint[1]] == PlayerNum + 1)
               return false;
       }
       return true;
   }

   //デバッグ用
   void PrintBoardInfo(int[,] Board)
   {
       int y = 0;
       string PrintList = "\n";
       for (; y < ConstList.BoardSize ;y++)
       {
           int x = 0;
           for (; x < ConstList.BoardSize ;x++)
           {
               PrintList += (Board[y, x]) + " ";
           }
           PrintList += "\n";
       }
       Debug.Log(PrintList);
   }
}

こんな感じです。

ピースの回転・反転を実装する

ピースのスクリプトを改良しました。

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

public class Piece : MonoBehaviour
{
   public List<int[]> Design{get; set;}
   public Vector3 WaitPoint{get; set;} //ピースの待機場所
   List<int[]> StartDesign;
   int RotateAngle;
   int ReverseAngle;
   public bool IsSet; //置かれた後のピースか判断 あまり必要ないかも
   void Start()
   {
       Design = PieceDesign.ReturnDesign(this.gameObject);
       StartDesign = Design;
   }

   void Update()
   {
   }

   //ボードで設定されているピース待機場所に戻る
   public void ReturnWaitPoint()
   {
       this.transform.position = WaitPoint;
   }

   //ピースの回転。反転中は回転方向を逆にしています。回転方向は引数の±で判断
   public void Rotate(int Dir)
   {
       foreach (int[] Piece in Design)
       {
           int[] Tmp = new int[]{Piece[0], Piece[1]};
           Piece[1] = -1 * Dir *Tmp[0];
           Piece[0] = 1 * Dir *Tmp[1];
       }
       if (ReverseAngle != 0)
           Dir *= -1;
       RotateAngle += 90 * Dir;
       Quaternion TmpQ;
       TmpQ = Quaternion.AngleAxis(RotateAngle, new Vector3(0, 1, 0));
       transform.rotation = Quaternion.AngleAxis(ReverseAngle, new Vector3(1, 0, 0)) * TmpQ;
       if (RotateAngle >= 360 || RotateAngle <= -360)
           RotateAngle = 0;
   }

   //ピースの反転
   public void Reverse()
   {
       foreach (int[] Piece in Design)
       {
           Piece[0] = -Piece[0];
       }
       ReverseAngle += 180;
       Quaternion TmpQ;
       TmpQ = Quaternion.AngleAxis(RotateAngle, new Vector3(0, 1, 0));
       transform.rotation = Quaternion.AngleAxis(ReverseAngle, new Vector3(1, 0, 0)) * TmpQ;
       if (ReverseAngle >= 360)
           ReverseAngle = 0;
   }

   //デバッグ用
   public void DebugLogPieceList()
   {
       Debug.Log("====");
       foreach (int[] A in Design)
       {
           Debug.Log(A[0] + " " + A[1]);
       }
   }
}

動かしてみる

画像1

良い感じですね。

次回

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