3단계: 테트리스 블록 만들기
🎯 이 단계에서 배울 것
- Unity에서 프리팹 만들기
- 7가지 테트리스 블록 프리팹 제작
- TetrisBlock 클래스 기본 구조
- 자식 오브젝트 사용하기
🔷 구분하기: 내가 만드는 것 vs 엔진(API)이 주는 것
| 구분 | 이번 단계 예시 | 설명 |
|---|---|---|
| 내가 만드는 것 | TetrisBlock 클래스(코드), 프리팹 구조(I_Block, Block_0~3 등) | 우리가 정하는 “블록 모양·이름”. 어떤 자식이 어디에 올지 우리가 에디터에서 배치. |
| 엔진(API)이 주는 것 | GameObject, Transform, Cube, 프리팹 시스템 | Unity 에디터·엔진이 제공. 오브젝트 생성, 부모-자식 관계, 프리팹 저장/불러오기는 엔진 기능. |
왜 엔진 것을 쓰나?
오브젝트를 만들고, 부모-자식으로 묶고, 프리팹으로 저장하는 건 엔진이 하는 일입니다. 우리는 “어떤 모양으로, 어떤 이름으로 둘지”만 정하고, 나중에 TetrisBlock 클래스에서 그 구조를 엔진 API(transform.GetChild 등) 로 읽어 씁니다.
📝 실습 내용
📌 작업 씬 제차 확인 (매 단계 필수)
이번 단계 실습을 시작하기 전에 다시 한 번 확인하세요. 상단 씬 탭에서 지금 열린 씬이 Tetris (게임용)인지 봅니다. Tetris가 아니면 (SimpleScene, SampleScene, Untitled 등) 반드시 Tetris로 이름을 변경한 뒤 진행하세요. → 1단계 씬 이름 변경 방법
1. Unity에서 테트리스 블록 프리팹 만들기
각 테트리스 블록을 Unity 에디터에서 직접 프리팹으로 만듭니다.
코드에서 Dictionary나 List 같은 복잡한 자료구조를 사용하지 않고, Unity의 시각적 편집 기능을 활용합니다.
1-1. Prefabs(읽는 법: 프리팹스) 폴더 만들기
-
[Project > Assets] 로 이동 (Project(읽는 법: 프로젝트) 창에서 Assets(읽는 법: 에셋스) 폴더 클릭)
- 폴더 생성
- [Project > Assets] 선택 상태에서 우클릭 → “Create” → “Folder”
- 이름 변경
- 생성된 “New Folder”를 F2 또는 더블클릭으로 “Prefabs” 로 변경
- 결과: [Project > Assets > Prefabs]
1-2. I 모양 프리팹 만들기
📐 예시 구조 (가이드)
I_Block은 부모 1개 + 자식 4개(Block_0~3) 로 만듭니다. 만들 후 Project > Prefabs로 드래그해 저장합니다. 이 예시로 Hierarchy(위치)를 체크해 보세요.
Hierarchy (씬에서 만들 때)
└─ I_Block ← 루트 (나중에 TetrisBlock 스크립트 붙임)
├─ Block_0 ← 자식 Cube (Local Position 0,0,0)
├─ Block_1 ← (1,0,0)
├─ Block_2 ← (2,0,0)
└─ Block_3 ← (3,0,0)
Project (저장 후)
└─ Assets > Prefabs > I_Block ← 파란 아이콘 프리팹
1단계: 빈 GameObject(읽는 법: 게임 오브젝트)** 생성**
- [Hierarchy] (읽는 법: 하이어러키) 빈 공간 우클릭 → “Create Empty”(읽는 법: 크리에이트 엠프티)
- 또는 [메뉴 > GameObject > Create Empty]
- 이름 변경
- 생성된 “GameObject”를 F2 또는 [Inspector] (읽는 법: 인스펙터) 맨 위 이름 칸에서 “I_Block” 로 변경
- 결과: [Hierarchy > I_Block]
2단계: 첫 번째 큐브 추가 (Block_0)
-
[Hierarchy > I_Block] 클릭 (선택 상태 유지)
- 자식 큐브 생성
- [Hierarchy > I_Block] 가 선택된 상태에서 Hierarchy 빈 공간 우클릭
- “3D Object” → “Cube”(읽는 법: 큐브) 선택 (또는 [메뉴 > GameObject > 3D Object > Cube])
- 중요: I_Block이 선택된 상태에서 생성해야 자식으로 생성됨!
- 결과: [Hierarchy > I_Block > Cube]
- 이름 변경
- 생성된 “Cube”를 “Block_0” 로 변경 → [Hierarchy > I_Block > Block_0]
- Local Position(읽는 법: 로컬 포지션) 설정
- [Hierarchy > I_Block > Block_0] 클릭
- [Inspector > Block_0 > Transform] (읽는 법: 트랜스폼) > Position (읽는 법: 포지션): X 0, Y 0, Z 0 (자식은 Local Position으로 표시됨)
- Scale(읽는 법: 스케일) 조정
- [Inspector > Block_0 > Transform > Scale]: X 0.9, Y 0.9, Z 1
- 블록들 사이에 여백이 생겨 테트리스처럼 보임.
3단계: 나머지 큐브 추가 (Block_1, Block_2, Block_3)
- [Hierarchy > I_Block > Block_0] 클릭 → Ctrl + D (또는 Cmd + D) 또는 우클릭 → Duplicate
- 생성: [Hierarchy > I_Block > Block_0 (1)]
- Block_1: “Block_0 (1)” 이름을 “Block_1” 로 변경 → [Hierarchy > I_Block > Block_1]
- [Inspector > Block_1 > Transform > Position]: X 1, Y 0, Z 0 (Scale 0.9, 0.9, 1 유지)
-
Block_2: Block_1 Ctrl + D → 이름 “Block_2” → [Inspector > Block_2 > Transform > Position]: X 2, Y 0, Z 0
- Block_3: Block_2 Ctrl + D → 이름 “Block_3” → [Inspector > Block_3 > Transform > Position]: X 3, Y 0, Z 0
4단계: 프리팹으로 저장
-
Scene 뷰에서 4개 큐브가 일렬로 배치된 I 모양인지 확인
블록이 안 보이면 → 아래 “3단계에서 블록이 안 보일 때” 참고. - 프리팹 폴더로 드래그
- [Hierarchy > I_Block] 를 클릭한 채로 [Project > Assets > Prefabs] 폴더 안으로 드래그
- Prefabs 폴더에 I_Block 이 파란색 아이콘(프리팹)으로 저장됨
- 프리팹 확인
- [Project > Assets > Prefabs > I_Block] 에 파란색 아이콘으로 표시됨
- Hierarchy의 I_Block은 프리팹 인스턴스가 됨. (위치 표기 규칙: 1단계 - 위치 표기 규칙)
💡 팁:
- 모든 블록의 Scale을 동일하게 (0.9, 0.9, 1)로 설정하면 블록들 사이에 약간의 여백이 생깁니다
- Scale (1, 1, 1): 블록들이 딱 달라붙음 ❌
- Scale (0.9, 0.9, 1): 블록들 사이에 여백이 생김 ✅
3단계에서 블록이 안 보일 때 {#3stage-block-not-visible}
증상: I_Block을 만들었는데 화면에 블록이 안 보인다.
1. Scene 뷰에서 보기 (만드는 동안)
3단계에서는 Scene 뷰에서 블록을 편집합니다.
- [Hierarchy > I_Block] 클릭해서 선택한 뒤 F 키를 누르세요. Scene 뷰 카메라가 I_Block에 맞춰져서 블록이 화면 중앙에 보입니다.
- Scene 뷰 상단 2D 버튼이 켜져 있으면 끄고 3D로 두면 큐브가 입체로 보입니다. (2D 모드면 옆에서만 보이거나 작게 보일 수 있음.)
2. 루트 위치 확인
- [Hierarchy > I_Block] 클릭 → [Inspector > Transform > Position] 이 (0, 0, 0) 인지 확인.
- 값이 너무 크면 (예: 1000, 0, 0) 화면 밖에 있어서 안 보일 수 있습니다. (0, 0, 0) 으로 맞추세요.
3. 자식 Cube Scale 확인
- [Hierarchy > I_Block > Block_0] 클릭 → [Inspector > Transform > Scale] 이 (0.9, 0.9, 1) 인지 확인.
- 0 이 들어가 있으면 블록이 안 보입니다. (0.9, 0.9, 1) 로 맞추세요.
4. Game 뷰에서 안 보일 때
- Game 뷰는 Main Camera가 비추는 화면입니다. 카메라를 아직 안 맞췄으면 블록이 안 보이거나 아주 작게 보일 수 있습니다.
- 4·5단계에서 플레이할 때 보이게 하려면, 5단계 테스트 전 필수 설정 (카메라) 또는 카메라 설정 가이드 에서 Position (5, 10, -10), Orthographic, Size 10 으로 맞추세요.
- 지금 3단계에서는 Scene 뷰에서 I_Block 선택 후 F 로 블록이 보이면 다음 단계(프리팹 저장)로 진행하면 됩니다.
정리: Hierarchy에서 I_Block 선택 → F (Scene 뷰) · Position (0,0,0) · Scale (0.9, 0.9, 1) · Game 뷰에서 보려면 카메라 설정.
1-3. 나머지 모양들 만들기
I 모양과 동일한 방법으로 나머지 모양들을 만듭니다. 각 모양마다:
- 빈 GameObject 생성 → 이름 변경
- 자식 큐브 4개 추가 → 각각 이름 변경 (Block_0, Block_1, Block_2, Block_3)
- 각 블록의 Local Position 설정 (아래 표 참고)
- 각 블록의 Scale을 (0.9, 0.9, 1)로 설정
- 프리팹으로 저장 (Prefabs 폴더로 드래그)
O 모양 (O_Block):
- 빈 GameObject 생성 → 이름: “O_Block”
- Block_0: Local Position (0, 0, 0), Scale (0.9, 0.9, 1)
- Block_1: Local Position (1, 0, 0), Scale (0.9, 0.9, 1)
- Block_2: Local Position (0, 1, 0), Scale (0.9, 0.9, 1)
- Block_3: Local Position (1, 1, 0), Scale (0.9, 0.9, 1)
- Prefabs 폴더로 드래그하여 저장
T 모양 (T_Block):
- 빈 GameObject 생성 → 이름: “T_Block”
- Block_0: Local Position (0, 0, 0), Scale (0.9, 0.9, 1)
- Block_1: Local Position (1, 0, 0), Scale (0.9, 0.9, 1)
- Block_2: Local Position (2, 0, 0), Scale (0.9, 0.9, 1)
- Block_3: Local Position (1, 1, 0), Scale (0.9, 0.9, 1)
- Prefabs 폴더로 드래그하여 저장
S 모양 (S_Block):
- 빈 GameObject 생성 → 이름: “S_Block”
- Block_0: Local Position (1, 0, 0), Scale (0.9, 0.9, 1)
- Block_1: Local Position (2, 0, 0), Scale (0.9, 0.9, 1)
- Block_2: Local Position (0, 1, 0), Scale (0.9, 0.9, 1)
- Block_3: Local Position (1, 1, 0), Scale (0.9, 0.9, 1)
- Prefabs 폴더로 드래그하여 저장
Z 모양 (Z_Block):
- 빈 GameObject 생성 → 이름: “Z_Block”
- Block_0: Local Position (0, 0, 0), Scale (0.9, 0.9, 1)
- Block_1: Local Position (1, 0, 0), Scale (0.9, 0.9, 1)
- Block_2: Local Position (1, 1, 0), Scale (0.9, 0.9, 1)
- Block_3: Local Position (2, 1, 0), Scale (0.9, 0.9, 1)
- Prefabs 폴더로 드래그하여 저장
J 모양 (J_Block):
- 빈 GameObject 생성 → 이름: “J_Block”
- Block_0: Local Position (0, 0, 0), Scale (0.9, 0.9, 1)
- Block_1: Local Position (0, 1, 0), Scale (0.9, 0.9, 1)
- Block_2: Local Position (1, 1, 0), Scale (0.9, 0.9, 1)
- Block_3: Local Position (2, 1, 0), Scale (0.9, 0.9, 1)
- Prefabs 폴더로 드래그하여 저장
L 모양 (L_Block):
- 빈 GameObject 생성 → 이름: “L_Block”
- Block_0: Local Position (2, 0, 0), Scale (0.9, 0.9, 1)
- Block_1: Local Position (0, 1, 0), Scale (0.9, 0.9, 1)
- Block_2: Local Position (1, 1, 0), Scale (0.9, 0.9, 1)
- Block_3: Local Position (2, 1, 0), Scale (0.9, 0.9, 1)
- Prefabs 폴더로 드래그하여 저장
💡 팁:
- 각 모양을 만들 때마다 Scene 뷰에서 모양이 제대로 보이는지 확인하세요
- 모든 블록의 Scale을 (0.9, 0.9, 1)로 설정하는 것을 잊지 마세요
- 프리팹으로 저장하면 나중에 재사용할 수 있습니다
팁:
- 각 블록의 Local Position은 부모(TetrisBlock) 기준 상대 위치입니다.
- 부모가 (5, 17)에 있으면, Local Position (1, 0)인 블록은 실제로 (6, 17)에 표시됩니다.
- 스케일 조정: 모든 블록의 Scale을 (0.9, 0.9, 1)로 설정하면 블록들 사이에 여백이 생겨서 테트리스처럼 보입니다.
- Scale (1, 1, 1): 블록들이 딱 달라붙음 ❌
- Scale (0.9, 0.9, 1): 블록들 사이에 여백이 생김 ✅
2. TetrisBlock 클래스 기본 구조
2-1. TetrisBlock 스크립트 파일 생성
-
[Project > Assets > Scripts] 로 이동 (Project 창에서 Assets → Scripts 폴더 클릭)
- C# 스크립트 생성
- [Project > Assets > Scripts] 선택 상태에서 우클릭 → “Create” → “C# Script”
- 이름 변경
- 생성된 “NewBehaviourScript”를 F2 또는 더블클릭으로 “TetrisBlock” 로 변경
- 결과: [Project > Assets > Scripts > TetrisBlock.cs]
2-2. 스크립트 코드 작성
- TetrisBlock.cs 파일 열기
- [Project > Assets > Scripts > TetrisBlock.cs] 더블클릭
- 에디터가 열림
- 코드 작성
- 에디터에 기본 코드가 있음
- 다음 코드로 전체 교체:
📌 코드 주석 — [우리 멤버], [Unity가 호출], [Unity 프로퍼티] 등은 내가 만든 것 / 상속·Unity 것 구분용입니다. 자세한 표기 → 4단계 - 소스코드 구분 표기.
using UnityEngine;
public class TetrisBlock : MonoBehaviour // [MonoBehaviour 상속] Unity 부모 클래스
{
private GameBoard gameBoard; // [우리 멤버] 게임 보드 참조
void Start() // [Unity가 호출] 오브젝트가 씬에 올라온 뒤 엔진이 한 번 호출
{
gameBoard = FindObjectOfType<GameBoard>(); // [Unity API] 씬에서 GameBoard 찾기
if (gameBoard != null)
{
transform.position = new Vector3(5, 17, 0); // transform, position: [Unity 프로퍼티] 상속받은 것
}
}
}
중요: 이 방식에서는:
- ❌ Dictionary 사용 안 함
- ❌ List 사용 안 함
- ✅ 자식 오브젝트(Block_0, Block_1 등)를 직접 사용
- ✅ Unity의 Transform 시스템 활용
3. TetrisBlock 스크립트를 프리팹에 추가 (4·5단계에서 필수)
📌 연결 안내
4단계에서 Spawner로 블록을 스폰하고, 5단계에서 키보드로 블록을 조작하려면 사용하는 블록 프리팹의 루트 오브젝트에 TetrisBlock 스크립트가 반드시 붙어 있어야 합니다. Spawner가 Instantiate한 블록에 TetrisBlock이 없으면 FindObjectOfType<TetrisBlock>()이 찾지 못해 이동·회전이 동작하지 않습니다. 아래처럼 한 번만 붙여 두면 됩니다.
- 프리팹 선택
- [Project > Assets > Prefabs > I_Block] 클릭
- [Inspector] 에 프리팹 정보(루트 오브젝트 등)가 표시됨
- 스크립트 추가
- [Inspector > Add Component] 클릭 → 검색창에 “TetrisBlock” 입력 → “Tetris Block” 추가
- (TetrisBlock.cs는 [Project > Assets > Scripts > TetrisBlock.cs] 에 있어야 함)
- 다른 프리팹에도 추가
- [Project > Assets > Prefabs > O_Block] 등 Spawner에서 사용할 모든 블록 프리팹의 루트에 TetrisBlock 스크립트 추가
- 4단계 Spawner의 Block Prefab으로 넣는 프리팹은 전부 루트에 TetrisBlock이 있어야 함
4. 참고: 자식 블록 접근하기 (무슨 말인지만 보면 됨)
“자식 블록”이 뭐냐면 — 아까 만든 I_Block을 보면 부모(I_Block) 하나 아래에 Block_0, Block_1, Block_2, Block_3이 붙어 있습니다. 이 Block_0, Block_1 … 같은 걸 자식 블록이라고 부릅니다.
“접근하기”란 코드에서 그 자식들을 하나씩 읽는 것을 말합니다. 4단계에서 이동 가능한지(ValidMove), 회전(Rotate) 할 때 각 자식의 위치를 GetChild(0), GetChild(1) 처럼 읽어서 씁니다.
지금 3단계에서는 이 코드를 넣을 필요 없습니다. 4단계에서 ValidMove, Rotate 만들 때 나옵니다. 여기서는 “나중에 4단계에서 이렇게 자식들을 읽어 쓴다”만 보고 넘어가면 됩니다.
예시 (4단계에서 쓰는 방식):
// 자식 블록 개수 확인
int childCount = transform.childCount; // transform, childCount: [Unity 프로퍼티] 상속 — 우리가 선언한 게 아님
// 첫 번째 자식 블록 가져오기
Transform firstBlock = transform.GetChild(0); // GetChild(i): [Unity API]
// 모든 자식 블록 순회
for (int i = 0; i < transform.childCount; i++) // transform.childCount: [Unity 프로퍼티]
{
Transform block = transform.GetChild(i); // GetChild(i): [Unity API]
// block.position, block.localPosition: [Unity 프로퍼티]
}
참고:
transform.position: Unity 월드 좌표 (절대 위치)transform.localPosition: 부모 기준 상대 위치- 자식 블록들은
localPosition으로 배치되어 있으므로, 부모를 이동하면 자식도 함께 이동합니다
💡 이 방식의 장점
이 방식의 장점 (프리팹 기반):
- ✅ 간단함: Dictionary, List 같은 고급 자료구조 불필요
- ✅ 시각적: Unity 에디터에서 직접 블록 모양을 만들고 확인 가능
- ✅ 이해하기 쉬움: 2차원 배열만 알면 됨
- ✅ 직관적: 프리팹을 만들면 바로 사용 가능
다른 방식 (코드 기반):
- ❌ Dictionary, List 같은 자료구조 필요
- ❌ 코드로만 모양을 정의해야 함
- ❌ 초보자에게 어려움
📍 좌표 설명
📌 좌표는 4·5·6단계(이동·회전·행 제거)에서도 계속 사용됩니다. 이 단계에서 다루는 Local Position(상대 좌표), World Position(절대 좌표) 에 대한 보강 개념이 기초 개념 문서에 있으니, 헷갈리면 아래 문서에서 계속 복기하세요. → 좌표계 · Transform · Vector
Local Position (상대 좌표):
- 각 Block의 Local Position은 부모(TetrisBlock) 기준 상대 위치
- 예: Block_0의 Local Position이 (0, 0)이면, 부모 위치에서 오프셋 없음
- 예: Block_1의 Local Position이 (1, 0)이면, 부모에서 오른쪽으로 1칸
World Position (절대 좌표):
- 부모의
transform.position이 (5, 17)이고 - 자식의 Local Position이 (1, 0)이면
- 자식의 World Position은 (6, 17)입니다
초기 생성 위치:
- Spawner의 위치에 따라 달라질 수 있음
- 보통 (5, 17) 또는 (4, 19) 정도
- Unity 씬에서 Spawner GameObject의 Position을 설정
📌 맨 아래 줄이 반쯤 잘려 보일 때: 자식이 큐브면 한 칸이 -0.5~0.5 구간을 차지해서 바닥 줄이 반쯤 잘릴 수 있습니다. 카메라 Size를 10.5 정도로 키우면 화면에 다 보입니다. → 4단계 바닥이 좌표와 안 맞아 보일 때 참고.
💡 이번 단계에서 익히는 개념
- 클래스 = 관련된 변수와 함수를 한 덩어리로 묶은 설계도 (TetrisBlock 클래스 = 블록 관련 변수·함수)
- 멤버 변수 vs 로컬 변수 (3단계에서 첫 구분):
- 멤버 변수 = 클래스 안, 메소드 밖에 선언한 변수. 객체가 있는 동안 유지되고, 여러 메소드에서 쓸 수 있음. (예: TetrisBlock의 width, height처럼 클래스 상단에 두는 값)
- 로컬 변수 = 메소드 안에서만 선언한 변수. 그 메소드가 실행되는 동안만 존재. (예: 메소드 안에서 잠깐 쓰는 임시 값)
- 이름·위치 컨벤션과 단계별 확장은 멤버 변수와 로컬 변수 문서를 보세요. 멤버·메소드 용어는 4~7단계에서 조금씩 익혀도 됩니다.
📌 복기: 변수 = 저장소(위치, 자식 블록 등). 배열 = 같은 타입을 순서대로 (블록 모양 정의할 때 쓰임). 한 번에 이해 못했어도 변수 · 배열에서 다시 실습해 보세요.
📌 복기 (좌표): Local Position, World Position, Scale 은 이번 단계뿐 아니라 4~6단계에서도 계속 나옵니다. 좌표 관련 보강 개념은 좌표계·Transform·Vector에 있으니, 계속 복기하세요.
📖 기초 개념 학습 (복기 → 점진적 실습)
📌 복기로 위 개념을 떠올린 뒤, 아래 기초 개념 문서에서 점진적으로 확장된 실습을 진행하세요. 단계가 올라갈수록 같은 개념이 조금씩 확장되므로, 차근차근 따라 하면 됩니다.
🛠️ 환경: 아래 개념 실습은 각 문서에 안내된 “이 실습 전용 씬”에서 진행하세요. 각 문서 맨 앞의 “이 실습 전용 씬 만들기 (환경 설정)”을 따라 그 씬을 만들거나 열고, 같은 문서에서 수행 과정만 이어서 하면 됩니다. (다른 문서로 왔다 갔다 하지 않습니다. 개념 실습 테스트 환경 가이드 참고.)
| 개념 문서 | 이번 단계와의 연결 |
|---|---|
| 좌표계 · Transform · Vector | 좌표 보강 개념. Local Position, Scale, X/Y 축. 4~6단계에서도 쓰이므로 계속 복기 권장. |
| 멤버 변수와 로컬 변수 | 클래스 안 vs 메소드 안 변수, 이름·위치 컨벤션. 단계별 확장 |
| 클래스 | TetrisBlock처럼 변수·함수를 묶은 설계도. Player 클래스 실습 |
| GameObject | 프리팹·자식 큐브가 모두 GameObject. 생성·이름 변경 실습 |
📐 이 단계 확장 실습 (전용 씬에서 점진적 연습): 3단계 확장 실습 01 - GetChild과 자식 순회
→ 단계별 확장 실습 전체 목차: 단계별 확장 실습 (목차)
✅ 확인 사항
- TetrisBlock 클래스가 정상적으로 작동하는가?
- 블록이 화면에 표시되는가?
- 7가지 모양을 모두 정의할 수 있는가?
📖 기초 개념 학습으로 이어가기
위 표의 기초 개념 학습을 진행한 뒤, 리스트·딕셔너리나 전체 구조가 궁금하면 시스템 설계 개요에서 점진적으로 이어가세요.
✅ 다음 단계 전 점검: 작업 씬이 Tetris인지 재차 확인하세요. Tetris가 아니면 씬 이름 변경 방법을 따라 Tetris로 바꾼 뒤 다음 단계로 넘어가세요.
| ← 이전 단계: 2단계 - 게임 보드 만들기 | 다음 단계: 4단계 - 블록 이동 구현 → |