테트리스 강좌

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

7단계: 점수 및 레벨 시스템

🎯 이 단계에서 배울 것


🔷 구분하기: 내가 만드는 것 vs 엔진(API)이 주는 것

구분 이번 단계 예시 설명
내가 만드는 것 GameController 클래스, score/level, AddScore(), UpdateUI() 우리가 정의하는 “몇 줄 지우면 몇 점”, “레벨이 오르면 낙하 속도 어떻게 바꿀지”. 점수·레벨 규칙은 전부 우리 로직.
엔진(API)이 주는 것 FindObjectOfType, Canvas/Text(UI), MonoBehaviour, .text 속성 Unity가 제공. 화면에 글자 띄우기는 엔진(UI 시스템)이 담당. 우리는 “무슨 값(score, level)을 어디(Text)에 넣을지”만 정함.

왜 엔진 것을 쓰나?
UI(Canvas, Text)는 엔진이 그리기 때문에, 우리는 Text 컴포넌트의 .text 에 문자열만 넣어 주면 됩니다. FindObjectOfType으로 GameController를 찾고, AddScore를 부르는 연결은 우리가 만들고, 실제 “점수 숫자 올리기”와 “화면에 반영”은 우리 로직 + 엔진 UI를 같이 씁니다.


📝 실습 내용

💡 7단계는 “점수·레벨·UI”만 추가하는 단계입니다.
6단계까지 행 제거가 되었다면, 여기서는 GameController 하나 만들고 점수/줄/레벨을 화면에 띄우면 됩니다. UI가 길어 보여도 “Canvas → Text 몇 개 → 스크립트에서 .text에 문자열 넣기”가 전부라서, 끊어서 해도 됩니다.

📌 실습 순서 (시점·맥락 맞추기)

  1. UI 만들기 (Canvas, ScoreText, LinesText, LevelText)
  2. GameController 스크립트 작성 → GameController 오브젝트 생성
  3. UI Text를 GameController Inspector에 연결
  4. TetrisBlock의 LockPiece 수정 — ClearFullRows() 반환값을 받아 GameController.AddScore(linesCleared) 호출 추가 (6단계 LockPiece에는 없으므로 이 단계에서 추가)
  5. (선택) 레벨에 따른 낙하 속도 조정
  6. (선택) 게임 보드 최종 완성 — 보드 에어리어·스프라이트를 씬에서 손으로 맞춤 (코드로 좌표 맞추지 않음)

GameController를 먼저 만든 뒤, 같은 7단계에서 LockPiece에 점수 연결을 넣으면 컴파일·흐름이 맞습니다.

📌 발음: Canvas, Rect Transform, TextMeshPro 등 읽는 법 → 영어 용어 발음·표기 (한글 발음만 보셔도 됩니다)

📐 이번 단계 예시 구조 (가이드)
7단계에서는 Canvas 아래에 Text 3개를 두고, GameController 오브젝트에서 Inspector로 연결합니다. UI 계층 구조는 아래와 같습니다. 이 예시로 Hierarchy(위치)를 체크해 보세요.

씬 (Tetris)
├─ Main Camera
├─ GameBoard
│  └─ BoardBackground   (선택)
├─ Spawner
│  └─ (Spawn Point 등)
├─ InputHandler         ← 루트 레벨
├─ Canvas               ← UI 부모
│  ├─ ScoreText        ← GameController의 Score Text에 연결
│  ├─ LinesText        ← Lines Text에 연결
│  └─ LevelText        ← Level Text에 연결
├─ EventSystem         (자동 생성)
└─ GameController      ← Game Controller (Script) + Score/Lines/Level Text 드래그 연결

자세한 UI 구조·배치 → UI 배치 구조 설계 · 시스템 설계 개요

📌 이 단계에서는 TextMeshPro(TMP)만 사용합니다
아래 GameController 코드는 TMP_Text 타입을 사용합니다. 따라서 UI 텍스트는 반드시 “UI > Text - TextMeshPro” 로 만들어야 합니다. Legacy > Text로 만들면 Inspector에서 Score Text / Lines Text / Level Text 칸에 드래그가 되지 않거나 타입이 맞지 않아 연결할 수 없습니다. 처음부터 Text - TextMeshPro로 만드세요.


📌 작업 씬 제차 확인 (매 단계 필수)
이번 단계 실습을 시작하기 전에 다시 한 번 확인하세요. 상단 씬 탭에서 지금 열린 씬이 Tetris (게임용)인지 봅니다. Tetris가 아니면 (SimpleScene, SampleScene, Untitled 등) 반드시 Tetris로 이름을 변경한 뒤 진행하세요. → 1단계 씬 이름 변경 방법

📌 7단계 시작 전 점검 (6단계·입력시스템)
7단계는 점수·레벨·UI만 추가하는 단계라서, 아래가 되어 있어야 의미가 있습니다. 한 번만 확인해 보세요.

확인 항목 설명
5단계 입력 키보드로 블록 이동·회전이 되나요? → 5단계 - 입력 처리
6단계 행 제거 줄이 꽉 차면 줄이 사라지고, 다음 블록이 나오나요? → 6단계 - 행 제거 시스템
GameBoard / Spawner / TetrisBlock 6단계처럼 LockPiece에서 ClearFullRows, SpawnNext까지 연결되어 있나요? (AddScore는 7단계 4번에서 추가)

위가 대략 되었다면 7단계는 GameController 만들기 → UI 연결 → LockPiece에 AddScore 연결 순서로 하면 됩니다.


0. UI가 이미 만들어져 있다면?

4단계나 6단계에서 Canvas와 기본 Text를 미리 만들었다면:

처음부터 만드는 경우: 아래 1번부터 따라하세요.

1. UI 만들기 (Canvas와 Text)

게임 화면에 점수, 줄 수, 레벨을 표시하기 위해 UI를 만들어야 합니다.

1-1. Canvas(읽는 법: 캔버스) 생성

  1. Hierarchy(읽는 법: 하이어러키) 창 확인
    • 화면 왼쪽의 Hierarchy 창에서 현재 씬 오브젝트들이 트리로 보이는지 확인
  2. Canvas 생성
    • [Hierarchy] 창 빈 공간에서 우클릭
    • “UI” → “Canvas” 선택
    • 또는 [메뉴 > GameObject > UI > Canvas]
    • 생성 후 위치: [Hierarchy > Canvas]
  3. 자동 생성 확인
    • [Hierarchy > Canvas] (파란색 선택됨)
    • [Hierarchy > EventSystem] 도 함께 생성됨 (건드리지 않음)
  4. Canvas 확인
    • [Hierarchy > Canvas] 클릭
    • [Inspector > Canvas > Render Mode] 가 “Screen Space - Overlay” 인지 확인 (기본값)

1-2. 점수 표시 Text 생성

  1. Canvas 선택
    • [Hierarchy > Canvas] 클릭
    • 중요: Canvas가 선택된 상태여야 자식으로 생성됨!
  2. Text 생성
    • [Hierarchy > Canvas] 가 선택된 상태에서 Hierarchy 빈 공간 우클릭
    • “UI” → “Text - TextMeshPro”(읽는 법: 텍스트메시프로) 선택
    • Legacy > Text는 사용하지 마세요. 코드가 TMP_Text 타입이라 TMP 오브젝트만 연결됩니다.
    • 생성 후: [Hierarchy > Canvas > Text (TMP)]
  3. TextMeshPro Import (처음 사용 시)
    • Import 창이 뜨면 “Import TMP Essentials” 클릭 후 완료될 때까지 대기
  4. 이름 변경
    • [Hierarchy > Canvas > Text (TMP)] 선택
    • F2 또는 [Inspector] 맨 위 이름 칸에서 “ScoreText” 로 변경
    • 결과: [Hierarchy > Canvas > ScoreText]
  5. 텍스트 내용 설정
    • [Hierarchy > Canvas > ScoreText] 클릭
    • [Inspector > ScoreText > TextMeshPro - Text (UI) 컴포넌트]:
      • Text: “점수: 0” (한글이 □로 나오면 아래 한글 폰트 설정 참고)
      • Font Size: 24
      • Color: 흰색
  6. 위치 조정 (Rect Transform(읽는 법: 렉트 트랜스폼))
    • [Inspector > ScoreText > Rect Transform]:
      • Pos X: 400, Pos Y: 200
      • Width: 200, Height: 50
  7. Game 뷰에서 확인
    • 상단 Game 탭 클릭 → 화면 오른쪽 상단에 “점수: 0” (또는 “Score: 0”) 보이면 성공!
    • 한글이 □□□로 나오면 아래 “텍스트가 사각형으로 깨질 때” → TMP 한글 폰트 설정을 진행하세요.

1-3. 줄 수 표시 Text 생성

  1. Canvas 선택
    • [Hierarchy > Canvas] 클릭
  2. LinesText 생성
    • [Hierarchy > Canvas] 선택 상태에서 Hierarchy 빈 공간 우클릭
    • “UI” → “Text - TextMeshPro” 선택
    • 위치: [Hierarchy > Canvas > Text (TMP)]
  3. 이름 변경
    • 해당 Text 선택 후 이름 “LinesText” 로 변경 → [Hierarchy > Canvas > LinesText]
  4. 설정
    • [Hierarchy > Canvas > LinesText] 클릭
    • [Inspector > LinesText > TextMeshPro - Text (UI)]: Text = “줄: 0”, Font Size = 24, Color = 흰색
    • [Inspector > Rect Transform]: Pos X 400, Pos Y 150, Width 200, Height 50
  5. Game 뷰에서 확인
    • Game 뷰에서 ScoreText 아래에 “줄: 0” 보이면 성공!

1-4. 레벨 표시 Text 생성

  1. Canvas 선택
    • [Hierarchy > Canvas] 클릭
  2. LevelText 생성
    • [Hierarchy > Canvas] 선택 상태에서 Hierarchy 빈 공간 우클릭
    • “UI” → “Text - TextMeshPro” 선택
    • 생성 후: [Hierarchy > Canvas > Text (TMP)]
  3. 이름 변경
    • 해당 Text 선택 후 이름 “LevelText” 로 변경 → [Hierarchy > Canvas > LevelText]
  4. 설정
    • [Hierarchy > Canvas > LevelText] 클릭
    • [Inspector > LevelText > TextMeshPro - Text (UI)]: Text = “레벨: 1”, Font Size = 24, Color = 흰색
    • [Inspector > LevelText > Rect Transform]: Pos X 400, Pos Y 100 (LinesText 아래), Width 200, Height 50
  5. Game 뷰에서 확인
    • Game 뷰에서 세 개의 텍스트가 세로로 정렬되어 보이면 성공!
    • 위에서 아래로: ScoreText, LinesText, LevelText

💡 팁:

⚠ 텍스트가 사각형(□□□)으로 깨질 때 (한글 폰트 지정)
TextMeshPro(TMP) 기본 폰트(LiberationSans)에는 한글이 없어서 “점수”, “줄”, “레벨” 등이 □□□로 나옵니다. 아래 TMP 한글 폰트 설정대로 하면, Font Asset을 지정해서 한글이 제대로 보이게 할 수 있습니다.

TMP 한글 폰트 설정 (필수)

1단계: 한글 폰트 파일 준비

2단계: TMP Font Asset 생성

점수줄레벨게임오버 0123456789:

💡 왜 전체 한글이 아닌가? — 전체 한글(11,172자)을 넣으면 생성에 오래 걸리고 Atlas가 커집니다. 우리 게임에서 쓰는 글자만 넣으면 빠르고 가볍습니다. 나중에 글자를 추가하고 싶으면 이 칸에 더 넣고 다시 Generate 하면 됩니다.

3단계: 텍스트 오브젝트에 한글 폰트 지정 (Font Asset)
한글 폰트를 쓰이게 하려면, 각 TMP 텍스트 오브젝트의 Font Asset 칸에 방금 만든 SDF를 넣어야 합니다.

한글 폰트 지정 요약
| 단계 | 할 일 | | —- | ———————————————————————————————————————————————- | | 1 | Assets/Fonts에 한글 .ttf 넣기 | | 2 | [Window > TextMeshPro > Font Asset Creator] → Source Font 선택, Custom Characters에 점수줄레벨게임오버 0123456789: 등 입력 → Generate → Save | | 3 | 각 ScoreText / LinesText / LevelText의 Inspector > TextMeshPro - Text (UI) > Font Asset에 저장한 SDF 지정 |

2. GameController 클래스 생성

📌 코드에서 쓰는 것 구분

📌 TMP와 코드가 맞아야 연결됩니다
아래 GameController 코드는 TMP_Text 타입(using TMPro)을 사용합니다. 따라서 Score Text / Lines Text / Level Text 칸에는 Text - TextMeshPro로 만든 오브젝트만 드래그할 수 있습니다.


Assets/Scripts/GameController.cs 생성:

using UnityEngine;
using TMPro;   // TextMeshPro — TMP 오브젝트 드래그하려면 이 타입 사용

public class GameController : MonoBehaviour   // [MonoBehaviour 상속]
{
    public int score = 0;   // [우리 멤버]
    public int lines = 0;   // [우리 멤버]
    public int level = 1;   // [우리 멤버]

    public TMP_Text scoreText;   // [우리 멤버] Inspector에 Text - TextMeshPro 오브젝트 드래그
    public TMP_Text linesText;
    public TMP_Text levelText;

    void Start()   // [Unity가 호출] 상속받은 메소드 — 엔진이 한 번 호출
    {
        UpdateUI();   // [우리 메소드]
    }

    public void AddScore(int linesCleared)   // [우리 메소드] LockPiece가 호출
    {
        int points = 0;
        switch (linesCleared)
        {
            case 1: points = 100; break;
            case 2: points = 300; break;
            case 3: points = 500; break;
            case 4: points = 800; break;
        }
        score += points * level;   // score, level: [우리 멤버]
        lines += linesCleared;
        level = (lines / 10) + 1;
        UpdateUI();   // [우리 메소드]
    }

    void UpdateUI()   // [우리 메소드]
    {
        scoreText.text = $"점수: {score}";   // scoreText: [우리 멤버], .text: [Unity 프로퍼티] 상속
        linesText.text = $"줄: {lines}";
        levelText.text = $"레벨: {level}";
    }
}

3. GameController와 UI 연결하기

GameController 스크립트를 GameObject에 추가하고 UI Text를 연결해야 합니다.

3-1. GameController 스크립트 파일 생성

  1. [Project > Assets > Scripts] 로 이동 (Project 창에서 Assets 클릭 → Scripts 폴더 클릭)

  2. C# 스크립트 생성
    • [Project > Assets > Scripts] 선택 상태에서 우클릭 → “Create” → “C# Script”
  3. 이름 변경
    • 생성된 “NewBehaviourScript”를 F2 또는 더블클릭으로 “GameController” 로 변경
    • 결과: [Project > Assets > Scripts > GameController.cs]
  4. 스크립트 코드 작성
    • [Project > Assets > Scripts > GameController.cs] 더블클릭
    • 에디터가 열리면 위의 코드로 전체 교체 후 Ctrl + S (또는 Cmd + S) 저장

3-2. GameController GameObject 생성

  1. 빈 GameObject 생성
    • [Hierarchy] 빈 공간 우클릭 → “Create Empty”
    • 또는 [메뉴 > GameObject > Create Empty]
  2. 이름 변경
    • 생성된 “GameObject”를 F2 또는 [Inspector] 맨 위 이름 칸에서 “GameController” 로 변경
    • 결과: [Hierarchy > GameController]
  3. GameController 스크립트 추가
    • [Hierarchy > GameController] 클릭
    • [Inspector > Add Component] 클릭 → 검색창에 “GameController” 입력 → “Game Controller” 추가
    • 또는 [Project > Assets > Scripts > GameController.cs] 를 [Hierarchy > GameController] 위로 드래그하여 추가

3-3. UI Text 연결

  1. [Hierarchy > GameController] 클릭 → [Inspector > Game Controller (Script)] 가 보이는지 확인

  2. Score Text 연결
    • [Inspector > Game Controller (Script) > Score Text] 필드 확인
    • [Hierarchy > Canvas > ScoreText] 를 드래그하여 Score Text 필드에 놓기
    • 또는 Score Text 필드 옆 원형 버튼 클릭 → ScoreText 선택 → Select
  3. Lines Text 연결
    • [Inspector > Game Controller (Script) > Lines Text] 에 [Hierarchy > Canvas > LinesText] 드래그 (또는 원형 버튼)
  4. Level Text 연결
    • [Inspector > Game Controller (Script) > Level Text] 에 [Hierarchy > Canvas > LevelText] 드래그 (또는 원형 버튼)
  5. 연결 확인
    • [Inspector > GameController > Score Text / Lines Text / Level Text] 에 각각 ScoreText, LinesText, LevelText가 표시되면 성공.
    • 중요: 연결되지 않으면 UI에 점수가 표시되지 않습니다!

💡 중요:

4. TetrisBlock의 LockPiece에 AddScore 연결하기

📌 시점
6단계 LockPiece에는 AddToGrid, ClearFullRows, SpawnNext만 들어 있습니다. GameController는 7단계에서 만드므로, 점수 연결도 이 단계(7단계) 에서 추가합니다. 위 1~3번에서 GameController를 만든 뒤 아래처럼 수정하면 됩니다.

TetrisBlock.cs의 LockPiece() 를 아래처럼 수정합니다.
(변경점: ClearFullRows() 반환값을 변수에 받고, 그다음 GameController를 찾아 AddScore 호출 추가.)

public void LockPiece()
{
    gameBoard.AddToGrid(this);
    int linesCleared = gameBoard.ClearFullRows();   // 반환값 받기 (7단계 추가)

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

    Destroy(gameObject);
}

변경점 정리:

순서는 GameController 생성(위 2~3번) 다음에 이 4번을 하면 됩니다.

5. 레벨에 따른 낙하 속도 조정

레벨이 올라갈수록 블록이 빨리 떨어지도록 합니다:

TetrisBlock.cs의 Update() 함수 수정:

void Update()
{
    GameController gameController = FindObjectOfType<GameController>();
    float currentFallSpeed = fallSpeed / gameController.level;   // 레벨이 높을수록 빠름
    fallTimer += Time.deltaTime;
    if (fallTimer >= currentFallSpeed)
    {
        MoveDown();
        fallTimer = 0f;
    }
}

6. 게임 보드 최종 완성 (씬에서 손으로 맞추기)

보드 에어리어·보드 스프라이트가 블록과 어긋나 있으면 7단계에서 여기서 최종 맞춥니다. 코드로 좌표 맞추지 않고, 씬에서 손으로만 맞춥니다.

💡 손으로 맞추는 이유 — 해상도·카메라·화면 비율이 바뀌면 코드로 넣은 고정 좌표는 어긋나기 쉽습니다. 씬에서 눈으로 맞추면 단순하고, 나중에 설정을 바꿔도 다시 조절하기 쉽습니다.

  1. Play를 눌러 블록이 어디에 떨어지는지 봅니다.
  2. [Hierarchy > GameBoard] 클릭 → [Inspector > Transform]
    • Position을 조절해서 보드(블록이 쌓이는 영역)가 화면에서 보기 좋은 위치에 오게 합니다.
  3. BoardBackground가 있다면 [Hierarchy > GameBoard > BoardBackground] 클릭 → Transform
    • Position, Scale을 조절해서 보드 스프라이트가 블록이 움직이는 영역을 대충 덮도록 맞춥니다.
  4. Spawn Point (Spawner 자식) 클릭 → Transform > Position
    • 블록이 보드 위쪽 중앙 근처에서 나오게 Y(와 X)를 조절합니다.
  5. 다시 Play 해 보면서, 블록이 보드 안에 들어오고 배경과 맞아 보일 때까지 2~4를 반복합니다.

좌·우·하단이 어긋날 때 — 보드 배경(BoardBackground) 에 쓰는 스프라이트의 피벗(Pivot) 때문입니다. 블록은 큐브(3D/Quad)라서 피벗 이야기는 보드 배경 스프라이트에만 해당합니다. Project에서 그 스프라이트(Square 등)를 선택해 Pivot을 확인한 뒤 아래처럼 맞추면 됩니다.

스프라이트 피벗 BoardBackground Transform (10×20 보드) 설명
Bottom-Left (0, 0) Position 0, 0, 0 / Scale 10, 20, 1 왼쪽·아래가 논리 (0,0)에 딱 맞음. 좌·우·하단 일치할 때 이걸 쓰면 됨.
Center (0.5, 0.5) Position 5, 10, 0 / Scale 10, 20, 1 가운데가 (5, 10). 피벗이 Center면 이렇게.

피벗 바꾸는 법: [Project] 에서 사용 중인 스프라이트(Square 등) 클릭 → [Inspector] 맨 아래 Pivot → Bottom Left 또는 Custom (0, 0)으로 바꾼 뒤, 위 표에서 Bottom-Left 행처럼 Position (0,0,0), Scale (10, 20, 1) 넣으면 됩니다.

숫자 안 맞추고 눈으로만 맞춰도 되고, 위 표대로 넣으면 좌·우·하단이 논리 좌표와 일치합니다.


🔗 이전 단계와의 연결

이 섹션은 실습이 아닙니다. 따로 할 일이나 체크할 항목은 없습니다. “이 단계가 이전 단계와 어떻게 이어지는지”만 참고로 정리한 것이므로, 궁금할 때만 읽으면 됩니다.

이 단계에서는:

💡 이번 단계에서 익히는 개념

📌 복기: 변수(score, level) = 멤버, 메소드(AddScore, UpdateUI) = 그걸 다루는 함수. 프로퍼티(.text) = 괄호 없이 쓰는 값. 한 번에 이해 못했어도 변수 · 멤버와 메소드에서 다시 실습해 보세요.

📖 기초 개념 학습 (복기 → 점진적 실습)

📌 복기로 위 개념을 떠올린 뒤, 아래 기초 개념 문서에서 점진적으로 확장된 실습을 진행하세요. 단계가 올라갈수록 같은 개념이 조금씩 확장되므로, 차근차근 따라 하면 됩니다.

개념 문서 이번 단계와의 연결
멤버와 메소드 GameController = score·level(멤버) + AddScore(메소드). 한눈에 정리
클래스 Player 클래스. name·score + AddScore 실습
상태 관리 score, level 변수로 상태 추적. 점수·레벨 실습
이벤트 “행 제거 시” 점수 추가 같은 반응. 100점 달성 시 메시지 실습

📐 이 단계 확장 실습 (전용 씬에서 점진적 연습): 7단계 확장 실습 01 - 점수 변수와 UI 텍스트
→ 단계별 확장 실습 전체 목차: 단계별 확장 실습 (목차)

✅ 확인 사항

📖 기초 개념 학습으로 이어가기

위 표의 기초 개념 학습을 진행한 뒤, UI 표시·배치가 궁금하면 스코어 UI 표시 가이드, UI 배치 구조 설계에서 점진적으로 이어가세요.

✅ 다음 단계 전 점검: 작업 씬이 Tetris인지 재차 확인하세요. Tetris가 아니면 씬 이름 변경 방법을 따라 Tetris로 바꾼 뒤 다음 단계로 넘어가세요.


← 이전 단계: 6단계 - 행 제거 시스템 다음 단계: 8단계 - 게임 완성 →