테트리스 강좌

초등학생도 쉽게 따라할 수 있는 Unity 테트리스 게임 개발 강좌 자료

코드 리뷰 5단계: TetrisBlock

8단계 이후 최종 TetrisBlock.cs 전체 코드와 설명·복습입니다.

역할: 한 블록의 이동·회전·낙하·고정을 담당합니다. ValidMove로 이동 가능 여부를 검사하고, LockPiece에서 GameBoard·GameController·Spawner와 연결합니다.

관련 단계 복기: 4단계 - 블록 이동 구현(ValidMove, MoveLeft/Right/Down, Rotate, 자동 낙하) → 6단계 - 행 제거 시스템(LockPiece, AddToGrid, ClearFullRows, SpawnNext) → 7단계 - 점수·레벨 시스템(AddScore 연동) → 8단계 - 게임 완성(HardDrop).


전체 코드

using UnityEngine;

/// <summary>
/// 한 블록의 이동·회전·낙하·고정을 담당.
/// ValidMove로 이동 가능 여부 검사, LockPiece에서 GameBoard·GameController·Spawner와 연동.
/// </summary>
public class TetrisBlock : MonoBehaviour
{
    private GameBoard gameBoard;
    public float fallSpeed = 1f;   // 기본 낙하 간격(초). 레벨로 나눠 사용해 레벨 올라갈수록 빨라짐
    private float fallTimer = 0f;  // 자동 낙하용 타이머

    /// <summary>Start에서 GameBoard 참조를 한 번 찾아 둠.</summary>
    void Start()
    {
        gameBoard = FindObjectOfType<GameBoard>();
    }

    /// <summary>
    /// 이 블록이 position + offset으로 이동했을 때 유효한지 검사.
    /// 각 자식의 월드 좌표를 칸 좌표로 바꿔 보드 범위·빈 칸 여부 확인.
    /// </summary>
    public bool ValidMove(Vector3 offset)
    {
        Vector3 parentNew = transform.position + offset;
        for (int i = 0; i < transform.childCount; i++)
        {
            Transform child = transform.GetChild(i);
            Vector3 childWorld = parentNew + transform.TransformDirection(child.localPosition);
            Vector3Int cell = Vector3Int.RoundToInt(childWorld);
            if (cell.x < 0 || cell.x >= gameBoard.width || cell.y < 0 || cell.y >= gameBoard.height)
                return false;
            if (!gameBoard.IsCellEmpty(cell.x, cell.y))
                return false;
        }
        return true;
    }

    public void MoveLeft()
    {
        if (ValidMove(Vector3.left))
            transform.position += Vector3.left;
    }
    public void MoveRight()
    {
        if (ValidMove(Vector3.right))
            transform.position += Vector3.right;
    }
    /// <summary>한 칸 아래로 이동. 이동 불가면 바닥/블록 도달이므로 LockPiece 호출.</summary>
    public void MoveDown()
    {
        if (ValidMove(Vector3.down))
            transform.position += Vector3.down;
        else
            LockPiece();
    }
    /// <summary>바닥까지 한 칸씩 내린 뒤 고정. 스페이스 키로 호출.</summary>
    public void HardDrop()
    {
        while (ValidMove(Vector3.down))
            transform.position += Vector3.down;
        LockPiece();
    }

    /// <summary>자동 낙하. 레벨이 높을수록 fallSpeed/level로 간격이 짧아져 빨라짐.</summary>
    void Update()
    {
        GameController gameController = FindObjectOfType<GameController>();
        float currentFallSpeed = fallSpeed / gameController.level;
        fallTimer += Time.deltaTime;
        if (fallTimer >= currentFallSpeed)
        {
            MoveDown();
            fallTimer = 0f;
        }
    }

    /// <summary>Z축 -90도 회전. 회전 후 위치가 유효하지 않으면 원래대로 되돌림.</summary>
    public void Rotate()
    {
        transform.Rotate(0f, 0f, -90f);
        if (!ValidMove(Vector3.zero))
            transform.Rotate(0f, 0f, 90f);
    }

    /// <summary>
    /// 블록 고정 → 가득 찬 행 제거 → 점수 갱신 → 다음 블록 스폰 → 이 오브젝트 Destroy.
    /// 호출 순서 변경 시 동작이 깨질 수 있으므로 유지.
    /// </summary>
    public void LockPiece()
    {
        gameBoard.AddToGrid(this);
        int linesCleared = gameBoard.ClearFullRows();

        FindObjectOfType<GameController>().AddScore(linesCleared);
        FindObjectOfType<Spawner>().SpawnNext();

        Destroy(gameObject);
    }
}

코드 설명

멤버 변수

Start()

ValidMove(Vector3 offset)

MoveLeft / MoveRight

MoveDown

HardDrop

Update()

Rotate()

LockPiece()


복습

아래는 4·6·7·8단계에서 배운 내용을 정리한 복기 표입니다.

구분 내용 관련 단계
ValidMove 이동/회전 전에 “그 위치가 보드 안이고, 칸이 비어 있는지” 검사. 범위 밖이거나 이미 쌓인 칸이면 false. 4·6단계
자식 좌표 부모 position + TransformDirection(자식 localPosition)으로 자식의 월드 좌표를 구하고, RoundToInt로 칸 인덱스로 씀. 4단계
MoveDown과 LockPiece MoveDown에서 ValidMove(down)가 false면 “바닥 또는 블록에 닿음”이므로 LockPiece()로 고정. 6단계
레벨과 낙하 Update에서 fallSpeed / gameController.level로 간격을 줄여, 레벨이 높을수록 빨리 낙하. 7단계
LockPiece 흐름 AddToGrid → ClearFullRows → AddScore(linesCleared) → SpawnNext() → Destroy. 순서가 바뀌면 안 됨. 6·7·8단계

← 이전: 코드 리뷰 4단계 (Spawner) 목차