テキスト解説

準備中

コード

▼BoardScript

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

public class BoardScript : MonoBehaviour
{
    //テトリス盤面のすべてを管理するクラス

    //1)マス(1つの四角)とグリッド(盤面)設定 | ヨコ×タテ(2次元配列[x, y])
    private int width = 10;   // 盤面の横幅(マス数)
    private int height = 20;  // 盤面の高さ(マス数)
    Transform[,] grid; //(2次元配列[x, y])ヨコ×タテ

    // 2)盤面を作る(2次元配列[x, y])
    void Awake() //Start() でもたぶん動く。
    {
        grid = new Transform[width, height];
    }

    //3)座標p(=1マス)がテトリス盤面の範囲内にあるかどうかをチェック
    public bool IsInside(Vector2Int p)
    {
        return p.x >= 0 // 左にはみ出してない?
           && p.x < width // 右にはみ出してない?
           && p.y >= 0 // 下にはみ出してない?
           && p.y < height; // 上にはみ出してない?
    }

    //4)4マスのテトリミノ(Tピース)が、その位置に置けるかどうか?
    //そのピースのすべてのマス(cells)が、盤面の中にあり、かつ既にそこにブロックがないなら true
    //4つのセルのうち、1つでもダメなら false(置けない)を返す。
    //(テトリミノの中心座標,各セルの相対座標)
    public bool CanPlace(Vector2Int cPos, Vector2Int[] cellOffsets)
    {
        //foreach=for文の簡易版。配列やリストを「全部」取り出すとき専用
        //foreach (var 変数名 in 集合体・配列)
        //cellOffsetsという配列の中から 1つずつ取り出して 、co という変数に入れて{}内を実行
        foreach (var co in cellOffsets)
        {
            //①セルの実際の位置を計算
            Vector2Int p = cPos + co; //中心座標(cPos)に相対座標(co)を足してそのマスが盤面上でどこになるかを求めている。

            //②盤面の外ならNG
            if(!IsInside(p)) return false;//範囲内でなければ false(置けない)

            //③そのマスに既にブロックがあるならNG
            if(grid[p.x, p.y] != null) return false; //grid[p.x, p.y] に何かブロックが入っている=置けない
        }
        return true; //④すべてOKなら true(置ける)
    }

    //5)盤面(grid)に、ブロックを固定する
    //(どこのマスに置くか(座標), 置くブロック(1マス分)そのもの)
    public void FixBlock(Vector2Int p, Transform cell)
    {
        grid[p.x, p.y] = cell;//「盤面の 点p の位置に、この cell(1マス)を置いたよ」
    }

    //6)盤面の枠線(見た目用の補助線。なくてもOK)
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.white * 0.5f;
        for (int x = 0; x <= width; x++)
            Gizmos.DrawLine(new Vector3(x, 0), new Vector3(x, height));
        for (int y = 0; y <= height; y++)
            Gizmos.DrawLine(new Vector3(0, y), new Vector3(width, y));
    }

}

▼Tetromino

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

public class Tetromino : MonoBehaviour
{
    //「落ちていくテトリミノをプレイヤーが操作(移動、回転、固定)するクラス
    //1)「BoardScript」クラスと「SpawnerScript」クラス
    [HideInInspector] public BoardScript myBoard;
    [HideInInspector] public SpawnerScript mySpawner;

    //2)変数等の用意
    Vector2Int centerPos; //Tetromino(ピース)の「中心座標」(整数座標)
    List<Transform> cells = new(); //テトリミノを構成する4つの小ブロック(cell)をいれるためのリスト
    Vector2Int[] offsets;//各ブロックの「中心からの相対座標」(配列)
    float nextFall; //次に落下する時間のタイミング(を管理)
    public float fallDelay = 0.5f; //テトリミノが自動で1マス落ちるまでの時間(0.5秒)

    //3)テトリミノ(Tetromino)の「操作」と「自動落下」を処理する関数
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.LeftArrow)) Move(Vector2Int.left);//←で左に1マス移動 ※Move() 内で衝突判定
        if (Input.GetKeyDown(KeyCode.RightArrow)) Move(Vector2Int.right);//→で右に1マス移動
        if (Input.GetKeyDown(KeyCode.UpArrow)) Rotate();//↑で回転
        if (Input.GetKeyDown(KeyCode.DownArrow)) Step();//↓で1マス落下
        if (Input.GetKeyDown(KeyCode.Space)) HardDrop();//スペースを押したら一気に落とす(ハードドロップ)

        //自然落下(自動落下)
        if (Time.time >= nextFall) Step();//自然落下(自動落下)
    }

    //4)テトリミノ(ブロック T)を出現させて、動ける状態にセット
    //(テトリミノの初期化(出現準備)を全部まとめた関数)
    //形状づくり・座標セット・衝突チェック・描画・落下タイマー設定・置く
    public void SpawnAt(Vector2Int spawnCpos)//引数はテトリミノを出現させたい場所(中心座標)
    {
        //①テトリミノが画面上に現れる(中心座標をセット)
        centerPos = spawnCpos;//(SpawnerScriptから渡ってくる)中心座標をセット
        transform.position = new Vector3(centerPos.x, centerPos.y, 0); //テトリミノを座標 (x, y) の位置に出す

        //②4つのセルを集める
        //Tetromino プレハブの中にはBlock1~4まで4つの子オブジェクトがあります。
        //その4つを cells リストに入れる処理。
        cells.Clear();//まずリストを空っぽにする(新しいテトリミノのために リセットしている)
        //foreach (要素型 変数名 in 集合体・配列)
        foreach (Transform c in transform)
        {
            if(c.GetComponent<SpriteRenderer>() != null)//図形のコンポーネントが空ではない=ある
            {
                cells.Add(c);//オブジェクト(=ブロック)があったら、c を cells リストに入れる
            }
        }

        //③ 相対座標(offsets)を作成(テトリスの形状データ)
        //例(Tミノなら):offsets = {(0, 1),(-1, 0),(0, 0), (1, 0)};
        offsets = new Vector2Int[cells.Count];
        //空っぽの箱(配列)に、実際の値(座標)を入れる
        for (int i = 0 ; i < cells.Count ; i++)
        {
            //i番目のブロックの「相対座標(localPosition)」を整数の座標にして、offsets配列に保存
            offsets[i] = Vector2Int.RoundToInt(cells[i].localPosition);
        }

        //④ 出現位置が有効かチェック(ゲームオーバー判定)
        //つまり:「出現した瞬間に置けない?」「既にブロックが積まれている場所?」
        //なら ゲームオーバー と同じ状態にする。
        if (!myBoard.CanPlace(centerPos, offsets))
        {
            enabled = false;// このスクリプト(Tetromino)を無効化する
            return;//処理を終了
        }

        //⑤ テトリミノ描画
        // centerPos(中心の座標)とoffsets(中心からの距離) をもとに4つのセルの実際の位置を画面に置く。
        Redraw();//中身は「5」へ
        nextFall = Time.time + fallDelay;//以降、fallDelay(0.5秒)ごとに落下
    }

    //5)テトリミノ再配置(ピースが移動・回転・落下したとき)
    void Redraw()
    {
        //●具体的には中心位置(centerPos)+ 相対位置(offsets)から、
        //●全てのセル(cells)の座標を計算し直して画面に反映する処理。

        //forループで4つのセルを順番に処理
        for (int i = 0; i < offsets.Length; i++)
        {
            Vector2Int p = centerPos + offsets[i];//セルの実際の座標を算出/position(中心)+offsets(相対位置)
            //実際の Unity オブジェクトを動かす
            cells[i].position = new Vector3(p.x, p.y, 0);
        }
    }

    //6)テトリミノが着地したら固定処理
    //4つのセルを盤面に固定し、ピース本体を消し、次のピースを出す
    //つまり:テトリミノを完全に停止 → 盤面へ組み込み → 新しいピースへ切り替える
    void Lock()
    {
        //① 4つのセルを1つずつ処理
        for (int i = 0; i < cells.Count; i++)
        {
            //② セルの「実座標」を算出
            Vector2Int p = centerPos + offsets[i];//中心座標+相対座標

            //③ 4つのセルのi番目のブロック(オブジェクト)を取り出す
            Transform cell = cells[i];
            cell.SetParent(myBoard.transform);//④セルをTetromino所属からBoard所属に移籍
            //↑つまり:動く Tetromino から切り離されて盤面の一部に変わる

            //⑤ 盤面グリッドに記録
            cell.position = new Vector3(p.x, p.y, 0); //cellの最終的な座標をUnityにセット
            //盤面データに登録
            myBoard.FixBlock(p, cell);
        }
        Destroy(gameObject);//⑥このオブジェクト(動くテトリミノ)を消す
        mySpawner.SpawnT();//⑦ 次のピースを出す
    }

    //以下Move()、Rotate()、Step()、HardDrop()を定義
    //7)テトリミノを左右に1マス動かす処理
    void Move(Vector2Int dir)//右か左かの情報を受け取る
    {
        //① 新しい中心位置(移動後のNext Position)を算出
        Vector2Int np = centerPos + dir;//現在の中心位置 + 移動方向 | 右(+1,0)左(-1,0)

        //②その位置に置けるかチェック
        if(myBoard.CanPlace(np, offsets))
        {
            //③ 移動可能なら 中心座標 を更新
            centerPos = np;//中心位置を np に更新
            Redraw();//④ 見た目を更新(4セルを新しい位置に動かす)
        }
    }

    //8)1マス落下(自然落下・下キー押し時の「一段下がる」動き)
    void Step()
    {
        //① 新しい中心位置(下に1マス)を算出
        Vector2Int np = centerPos + Vector2Int.down;//現在の中心位置 + 下方向 (0, -1)

        //② その位置に落ちれるかチェック
        if (myBoard.CanPlace(np, offsets))
        {
            //③ 落下できる場合(自然落下成功)
            centerPos = np;//中心位置を np に更新
            Redraw();//4つのブロックを再配置
            nextFall = Time.time + fallDelay;//次の落下タイミング(0.5秒後)をセット
        }
        else
        {
            Lock();//落ちれない = 着地した と判断して固定処理へ。
        }
    }

    //9)↑キーで回転
    void Rotate()
    {
        //中心を軸に offsets を90度回転 → 有効なら適用 → 描画処理。
        //① 回転後の座標を入れる配列を用意
        Vector2Int[] rot = new Vector2Int[offsets.Length];

        //② 各ブロックの offset を90度回転する
        for (int i = 0; i < offsets.Length; i++)
        {
            Vector2Int o = offsets[i];
            rot[i] = new Vector2Int(-o.y, o.x);
            //● 回転前offset = (x, y)→● 回転後rot = (-y, x)
            //これは数学の 90度回転行列 を簡略化した式です。
            //例:offsets[i] = (1, 0)(右に1マス)
            //90度回転すると…( x,  y) = ( 1, 0)→(-y,  x) = ( 0, 1)
            //右にあるブロック → 上へ移動
        }

        //③ 回転後の形が置けるか判定する
        if (myBoard.CanPlace(centerPos, rot))
        {
            //④ 置けるなら 相対座標 を rot に更新
            offsets = rot;//offsetsを書き換えることでピース全体の見た目が回転した状態になる。
            Redraw();//⑤ 再描画
        }
    }

    //10)スペースキーで一気に落下(ハードドロップ)
    void HardDrop()
    {
        //① 下に行ける間ずっと落ちる
        //for文 → “回数が決まっている繰り返し”
        //while文 → “条件が真の間ずっと続く繰り返し”
        //終わりのタイミングが「条件」で決まるとき、“できる限り繰り返す” という処理はwhile文
        while (myBoard.CanPlace(centerPos + Vector2Int.down, offsets))//下に1マス行っても大丈夫?
        {
            centerPos += Vector2Int.down;//OK(true)なら 連続で 中心座標 を下へ更新
        }
        Redraw();//② 落ちきった位置で描画更新
        Lock();//③ ピースを固定する
    }
}

▼SpawnerScript

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

public class SpawnerScript : MonoBehaviour
{
    //テトリミノ(Tピース)を出現させるクラス

    //1)変数を用意&アタッチ
    public BoardScript board; //ボードのスクリプト(が付いてるオブジェクトをドラッグ)
    public Tetromino TPrefab; //Block_T プレハブをドラッグ
    public Vector2Int spawnPos = new Vector2Int(5, 18); //出現させる位置

    // 2)ゲーム開始時にピース(T)を出す
    void Start()
    {
        SpawnT();//中身は3へ
    }

    //3)ピース(T)を生成
    public void SpawnT()
    {
        //生成の呪文 Instantiate(何を, 位置, 回転);
        Tetromino t = Instantiate(TPrefab, board.transform);
        //↓今生まれたテトリミノに「この盤面(Board)はこれだよ」と教えてあげている処理
        //新しいテトリミノはまだ「自分がどの盤面で動くのか」を知りません。
        //だから卵から生まれた瞬間に情報を渡している。
        t.myBoard = board;//出現したピースへ必要な情報を渡す
        t.mySpawner = this;//生まれたテトリミノに、自分(SpawnerScript)を渡す
        t.SpawnAt(spawnPos);//ピースを出現位置に移動させる(ピースが盤面の上に現れる)
    }
}

コメントを残す