テキスト解説
準備中
コード
▼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);//ピースを出現位置に移動させる(ピースが盤面の上に現れる)
}
}

