테트리스 강좌

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

5단계: 입력 처리

🎯 이 단계에서 배울 것


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

구분 이번 단계 예시 설명
내가 만드는 것 InputHandler 클래스, MoveLeft/MoveRight/Rotate 호출, 쿨다운 로직 우리가 정의하는 “어떤 키에 어떤 동작을 붙일지”.입력을 게임 로직에 연결하는 건 우리 코드.
엔진(API)이 주는 것 Input.GetKey, KeyCode, FindObjectOfType, Update() Unity가 제공. FindObjectOfType은 씬에서 컴포넌트를 찾는 API.

왜 엔진 것을 쓰나?
키 입력은 Input이 감지하고, 우리는 “눌렸을 때 MoveLeft() 호출”만 연결합니다. FindObjectOfType으로 씬에서 블록을 찾습니다.


📝 실습 내용

📐 예시 구조
InputHandler 빈 오브젝트를 씬 루트에 두고 스크립트만 붙이면 됩니다.

씬 (Tetris)
├─ Main Camera
├─ GameBoard
├─ Spawner
│  └─ (Spawn Point 등)
├─ InputHandler       ← 여기, 루트 레벨에 추가. 빈 오브젝트 + Input Handler (Script). 연결할 칸 없음.
└─ (기타)

InputHandler가 FindObjectOfType<TetrisBlock>() 로 블록을 찾아 MoveLeft 등을 호출합니다. → 시스템 설계 개요


📌 작업 씬 Tetris인지 확인. 아니면 → 씬 이름 변경. 발음 → 영어 용어 표기

0. 5단계 시작 전 점검 (누락 방지)

키로 블록이 움직이려면 4단계까지 아래가 있어야 합니다. InputHandler 붙이기 전에 확인하세요.

확인 항목 필요한 것 없을 때
작업 씬 상단 씬 탭이Tetris 1단계 씬 이름 변경 방법대로 Tetris로 변경
GameBoard Hierarchy에GameBoard 오브젝트, Game Board (Script) 붙어 있음 (2단계) TetrisBlock이 gameBoard를 못 찾아 ValidMove()에서 에러 → 이동 실패
Spawner Hierarchy에Spawner 오브젝트, Spawner (Script) 붙어 있음. Spawn Point 연결, Block Prefab 연결 (4단계) Play 시 블록이 스폰되지 않음
블록 프리팹 사용하는 프리팹(예: I_Block)루트 오브젝트에 TetrisBlock 스크립트 붙어 있음. ValidMove, MoveLeft, MoveRight, MoveDown, Rotate 구현됨 (4단계) FindObjectOfType<TetrisBlock>()이 null → 키를 눌러도 이동 호출 안 됨

체크 방법

맞으면 테스트 전 필수 설정 확인 후 1. InputHandler 생성으로 가세요.


테스트 전 필수 설정 (카메라·게임 보드 표현)

Play 시 블록·보드가 보여야 합니다. 안 보이면 아래 설정.
카메라 이상 시 → 카메라 설정 가이드

<span id="테스트-전-필수-설정-카메라게임-보드-표현">

카메라 세팅

  1. [Hierarchy > Main Camera] 클릭
  2. [Inspector > Main Camera > Transform]
    • Position: (5, 10, -10) — 10×20 보드의 가로 중앙(5), 세로 중간(10) 을 바라보고, Z는 음수로 두어 2D 화면 앞에 오도록
    • Rotation: (0, 0, 0)
  3. [Inspector > Main Camera > Camera]
    • Projection: Orthographic(읽는 법: 오소그래픽) (2D 테트리스에 적합)
    • Size: 10 ~ 12 — 값이 작을수록 확대, 클수록 넓게 보입니다. 10이면 세로로 보드가 잘 보이고, 12면 여백이 더 보입니다.
  4. 상단 Game 탭에서 Free Aspect 또는 16:9 로 두고 Play → 보드 전체(스폰 위치 포함)가 보이는지 확인

게임 보드의 표현 (보드가 안 보일 때)

GameBoard는 빈 오브젝트라 화면에 안 보입니다. Position (0,0,0)이면 보드는 x 0~10, y 0~20. 카메라 (5, 10, -10), Size 10~12면 그 구간이 보입니다.
보드 시각화(선택) → 6단계 게임 보드 시각화

정리: 카메라 Position (5, 10, -10), Size 10~12로 맞추면 됩니다.
맨 아래 줄이 반쯤 잘리면 → 4단계 바닥 트러블슈팅 (Size 10.5)


1. InputHandler(읽는 법: 인풋 핸들러) 클래스 생성

Assets/Scripts/InputHandler.cs 생성:

  1. [Project > Assets > Scripts] 선택 → 우클릭 → Create → C# Script → 이름 InputHandler
    • 결과: [Project > Assets > Scripts > InputHandler.cs]
  2. [Project > Assets > Scripts > InputHandler.cs] 더블클릭 → 아래 코드로 전체 교체 후 저장 (Ctrl+S)
using UnityEngine;

public class InputHandler : MonoBehaviour   // [MonoBehaviour 상속]
{
    private float moveCooldown = 0.1f;   // [우리 멤버]
    private float moveTimer = 0f;         // [우리 멤버]

    void Update()   // [Unity가 호출] 상속받은 메소드 — 매 프레임 엔진이 호출
    {
        moveTimer += Time.deltaTime;   // Time.deltaTime: [Unity 프로퍼티]

        if (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A))   // Input.GetKey, KeyCode: [Unity API]
        {
            if (moveTimer >= moveCooldown) { MoveLeft(); moveTimer = 0f; }   // MoveLeft: [우리 메소드]
        }
        if (Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D))
        {
            if (moveTimer >= moveCooldown) { MoveRight(); moveTimer = 0f; }   // MoveRight: [우리 메소드]
        }
        if (Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S))
        {
            TetrisBlock current = FindObjectOfType<TetrisBlock>();   // [Unity API]
            if (current != null && moveTimer >= moveCooldown)
            {
                current.MoveDown();   // MoveDown: [우리 메소드] TetrisBlock 쪽
                moveTimer = 0f;
            }
        }
        if (Input.GetKeyDown(KeyCode.UpArrow) || Input.GetKeyDown(KeyCode.W))   // [Unity API]
        {
            Rotate();   // [우리 메소드]
        }
    }

    void MoveLeft()   // [우리 메소드]
    {
        TetrisBlock current = FindObjectOfType<TetrisBlock>();   // [Unity API]
        if (current != null)
        {
            current.MoveLeft();   // MoveLeft: [우리 메소드] TetrisBlock 쪽
        }
    }
    void MoveRight()   // [우리 메소드]
    {
        TetrisBlock current = FindObjectOfType<TetrisBlock>();
        if (current != null)
        {
            current.MoveRight();
        }
    }
    void Rotate()   // [우리 메소드]
    {
        TetrisBlock current = FindObjectOfType<TetrisBlock>();
        if (current != null)
        {
            current.Rotate();   // Rotate: [우리 메소드] TetrisBlock 쪽
        }
    }
}

씬에 InputHandler 연결하는 방법

개념: InputHandler는 씬의 “어디”에 넣는가

씬에서의 위치 (Hierarchy 구조 예)

예시 구조:

Hierarchy
├── Main Camera
├── GameBoard
├── Spawner
│   └── (Spawn Point 등)
├── InputHandler   ← 여기, 루트 레벨에 추가
└── (기타)

연결 과정 (순서대로)

  1. 빈 오브젝트 만들기
    • [Hierarchy] 빈 공간 우클릭 → Create Empty
    • 루트에 GameObject 생김 → [Hierarchy > GameObject]
  2. 이름 바꾸기
    • 오브젝트 선택 후 F2 또는 [Inspector] 맨 위 이름 칸에 InputHandler 입력 → Enter
    • 결과: [Hierarchy > InputHandler]
  3. InputHandler 스크립트 붙이기
    • [Project > Assets > Scripts > InputHandler.cs] 를 [Hierarchy > InputHandler] 위로 드래그
    • [Inspector]에 Input Handler (Script) 보이면 연결됨
  4. 확인
    • [Hierarchy > InputHandler] 클릭 시 [Inspector]에 Input Handler (Script) 있으면 OK
    • InputHandler는 Spawner처럼 연결 칸 없음. 스크립트만 붙이면 됨.
  5. 씬 저장
    • Ctrl+S 또는 File → Save
    • 안 하면 나중에 씬 열었을 때 InputHandler가 없을 수 있음

정리: Hierarchy 루트에 InputHandler 빈 오브젝트 → InputHandler.cs 드래그로 붙이기 → Ctrl+S 저장. (위치 표기: 1단계)


언제 스폰된 블록이 움직이나요?

이동은 Play 모드에서 일어납니다.

  1. ▶ Play를 눌러야 함. 그때부터 Spawner가 블록 하나 스폰하고 동작 시작.
  2. 자동 낙하 (4단계)
    TetrisBlock Update() → fallTimer 쌓임 → fallSpeed마다 MoveDown() → 키 안 눌러도 한 칸씩 내려감.
  3. 키로 이동 (5단계)
    InputHandler Update() → 화살표/WASD 누르면 TetrisBlock 찾아서 MoveLeft(), MoveRight(), Rotate(), MoveDown() 호출.

정리: Play 누른 뒤 → 자동 낙하 + 키 입력으로 좌우·회전·빠른 낙하.

키를 눌러도 이동이 안 될 때

화살표/WASD 눌러도 블록이 안 움직이면 순서대로 확인.

  1. Game 뷰 한 번 클릭 (포커스). 다른 창에 포커스 있으면 키가 게임에 안 감.
  2. InputHandler
    [Hierarchy > InputHandler] 있고 [Inspector]에 Input Handler (Script) 붙어 있는지.
  3. 블록 프리팹 루트에 TetrisBlock
    프리팹 루트(예: I_Block)에 TetrisBlock 붙어 있어야 함. 자식에만 있으면 찾지 못함.
  4. GameBoard
    [Hierarchy]에 GameBoard + Game Board (Script). 없으면 ValidMove()에서 NullReferenceException.
  5. Console
    Window → General → Console에서 빨간 에러 확인 → 3·4 다시 점검.

정리: Game 뷰 포커스 → InputHandler·GameBoard 씬에 있는지 → 프리팹 루트에 TetrisBlock → Console. 맨 아래 줄 반쯤 잘리면 → 4단계 바닥/좌표 (카메라 Size 10.5).

회전은 2번 되는데, 그 다음엔 아무것도 안 될 때

증상: 위/ W 로 회전은 한두 번 되다가, 그 다음부터는 회전·좌우 이동·아래 모두 안 됨.

  1. Game 뷰 한 번 클릭 (포커스).
  2. 블록 한 개만: 수동 블록 제거, Spawner 블록만 두기.
  3. 회전 후 보드 밖: Spawn Point (5, 18, 0), GameBoard Width 10 / Height 20. Play 멈추고 위치 조정 후 다시 Play.
  4. Console 빨간 에러 → GameBoard 있는지, 씬 Tetris인지 확인.

나중에 블록이 여러 개 나오나요?

네. 나중에 나옵니다.

정리: 6단계에서 LockPiece() → SpawnNext() 로 이어지면 블록이 차례로 나오고, 8단계에서 여러 종류의 블록이 나오게 할 수 있습니다.


🔗 이전 단계와의 연결

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

이 단계에서는 4단계에서 만든 TetrisBlock의 이동 함수들을 사용합니다:

작동 방식:

  1. 사용자가 키를 누름
  2. InputHandler가 입력을 감지
  3. 현재 TetrisBlock를 찾아서 해당 함수 호출
  4. TetrisBlock가 이동/회전 수행

💡 참고: 현재 블록 찾기

📌 복기: 변수에 한 번 저장해 두고 쓰는 것 = 캐싱. 변수에서 다시 실습해 보세요.

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

📌 복기: 메소드 = 클래스 안의 함수. 한 번에 이해 못했어도 멤버와 메소드에서 다시 실습해 보세요.

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

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

개념 문서 이번 단계와의 연결
멤버와 메소드 메소드 = 클래스 안의 함수. 한눈에 정리 + AddScore 실습
함수 매개변수·반환값. AddNumbers, GetTen 실습
FindObjectOfType currentPiece 찾기

📐 이 단계 확장 실습 (전용 씬에서 점진적 연습): 5단계 확장 실습 01 - Input과 쿨다운 → 단계별 확장 실습 전체 목차: 단계별 확장 실습 (목차)

✅ 확인 사항

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


← 이전 단계: 4단계 - 블록 이동 구현 다음 단계: 6단계 - 행 제거 시스템 →