공룡이 달려가다 만나는 "선택 문(Select Doors)"을 구성하는 스크립트입니다. 각 문에는 덧셈·뺄셈·곱셈·나눗셈 중 하나의 연산 타입(type)과 숫자가 표시되며, 플레이어가 어느 문을 통과하느냐에 따라 공룡 수가 바뀝니다. SelectDoors는 두 문의 색상과 텍스트를 초기화하고, 충돌 시 연산 정보를 제공하는 역할을 합니다.
게임 구조
| 스크립트 | 역할 |
|---|---|
SelectDoors |
문 타입(enum) 설정, 색상·텍스트 초기화, 좌/우 연산 정보 제공 |
DinoController |
공룡 이동 및 문 충돌 감지 (튜토리얼 52) |
DinoPositionController |
문 통과 후 공룡 수 증감 처리 (튜토리얼 52) |
SelectDoors - 전체 코드
using UnityEngine;
using TMPro;
public enum DoorType { Plus, Minus, Times, Division }
public class SelectDoors : MonoBehaviour
{
public SpriteRenderer rightDoorSpriteRD;
public SpriteRenderer leftDoorSpriteRD;
public TextMeshPro rightDoorText;
public TextMeshPro leftDoorText;
[SerializeField] private DoorType rightDoorType;
public int rightDoorNumber;
[SerializeField] private DoorType leftDoorType;
public int leftDoorNumber;
public Color goodColor;
public Color badColor;
void Start() { SettingDoors(); }
public void SettingDoors()
{
if (rightDoorType.Equals(DoorType.Plus)) { rightDoorSpriteRD.color = goodColor; rightDoorText.text = "+" + rightDoorNumber; }
else if (rightDoorType.Equals(DoorType.Minus)) { rightDoorSpriteRD.color = badColor; rightDoorText.text = "-" + rightDoorNumber; }
else if (rightDoorType.Equals(DoorType.Times)) { rightDoorSpriteRD.color = goodColor; rightDoorText.text = "x" + rightDoorNumber; }
else if (rightDoorType.Equals(DoorType.Division)) { rightDoorSpriteRD.color = badColor; rightDoorText.text = "÷" + rightDoorNumber; }
if (leftDoorType.Equals(DoorType.Plus)) { leftDoorSpriteRD.color = goodColor; leftDoorText.text = "+" + leftDoorNumber; }
else if (leftDoorType.Equals(DoorType.Minus)) { leftDoorSpriteRD.color = badColor; leftDoorText.text = "-" + leftDoorNumber; }
else if (leftDoorType.Equals(DoorType.Times)) { leftDoorSpriteRD.color = goodColor; leftDoorText.text = "x" + leftDoorNumber; }
else if (leftDoorType.Equals(DoorType.Division)) { leftDoorSpriteRD.color = badColor; leftDoorText.text = "÷" + leftDoorNumber; }
}
public DoorType GetDoorType(float xPos) { return xPos > 0 ? rightDoorType : leftDoorType; }
public int GetDoorNumber(float xPos) { return xPos > 0 ? rightDoorNumber : leftDoorNumber; }
}
핵심 개념 설명
1. enum (열거형(enum)) - DoorType
public enum DoorType { Plus, Minus, Times, Division }
**열거형(enum)**은 관련된 상수(constant)들에 이름을 붙여 묶어 놓은 타입(type)입니다. 숫자 대신 의미 있는 이름을 사용할 수 있어 코드 가독성이 크게 높아집니다.
// enum 없이 숫자로 표현하는 경우 (의미를 알기 어려움)
int doorType = 0; // 0이 Plus인지 Minus인지 코드만 봐서는 모름
// enum으로 표현하는 경우 (의미가 명확)
DoorType doorType = DoorType.Plus; // 바로 이해 가능
enum의 내부 값: 기본적으로 0부터 시작하는 정수(integer)값을 가집니다.
| 이름 | 내부 정수(integer)값 |
|---|---|
DoorType.Plus |
0 |
DoorType.Minus |
1 |
DoorType.Times |
2 |
DoorType.Division |
3 |
언제 enum을 쓰는가?
- 정해진 몇 가지 선택지 중 하나를 나타낼 때
- 방향(상/하/좌/우), 상태(대기/이동/공격), 종류(불/물/풀 속성(property)) 등
- 조건문(conditional statement)에서 분기를 명확하게 구분하고 싶을 때
// enum 활용 예시
public enum GameState { Playing, Paused, GameOver }
public enum AttackType { Melee, Ranged, Magic }
.Equals vs == 비교
// .Equals 사용 (이 코드에서 사용한 방식)
if (rightDoorType.Equals(DoorType.Plus)) { ... }
// == 연산자 사용
if (rightDoorType == DoorType.Plus) { ... }
두 방식 모두 enum 비교에서 동일하게 동작합니다. enum은 값 타입(Value Type)이므로 참조 비교가 아닌 값 비교를 수행합니다.
| 비교 방식 | 특징 |
|---|---|
.Equals() |
메서드(method) 호출 방식, null 안전하지 않음 (enum은 null 불가이므로 실제로는 문제 없음) |
== 연산자(operator) |
더 간결한 표현, 가독성 좋음 |
2. SerializeField - private 필드(field)를 Inspector에서 편집
[SerializeField] private DoorType rightDoorType;
[SerializeField] private DoorType leftDoorType;
[SerializeField] 어트리뷰트(attribute)를 붙이면 private 필드(field)임에도 Unity Inspector에서 확인하고 편집할 수 있습니다.
왜 public 대신 SerializeField를 쓰는가?
public으로 선언하면 Inspector에서도 편집할 수 있지만, 다른 스크립트에서도 자유롭게 접근·수정이 가능해집니다. 문의 타입(type)은 이 오브젝트 내부에서만 관리해야 하므로 private으로 캡슐화(encapsulation)하되, 디자이너가 Inspector에서 설정할 수 있도록 [SerializeField]를 사용합니다.
| 선언 방식 | Inspector 편집 | 외부 스크립트 접근 | 사용 시기 |
|---|---|---|---|
public |
가능 | 가능 | 다른 스크립트에서도 읽거나 써야 할 때 |
[SerializeField] private |
가능 | 불가능 | Inspector에서만 설정하고 외부 접근은 막을 때 |
private |
불가능 | 불가능 | 완전히 내부 전용 데이터일 때 |
// 비교 예시
public int score; // Inspector ○, 외부 접근 ○
[SerializeField] private int score; // Inspector ○, 외부 접근 ✗
private int score; // Inspector ✗, 외부 접근 ✗
Inspector에서 DoorType enum 필드(field)는 드롭다운 메뉴로 표시되어 Plus / Minus / Times / Division 중 선택할 수 있습니다.
3. SpriteRenderer.color - 문 색상 설정
public SpriteRenderer rightDoorSpriteRD;
public Color goodColor;
public Color badColor;
rightDoorSpriteRD.color = goodColor; // 좋은 문: goodColor로 칠하기
rightDoorSpriteRD.color = badColor; // 나쁜 문: badColor로 칠하기
SpriteRenderer: 2D 스프라이트(이미지)를 렌더링하는 컴포넌트입니다. .color 프로퍼티로 스프라이트에 색상을 곱해서 적용합니다. 흰색 스프라이트에 색상을 곱하면 그 색상 그대로 표시됩니다.
// Color 사용 예시
spriteRenderer.color = Color.red; // 빨간색
spriteRenderer.color = Color.blue; // 파란색
spriteRenderer.color = new Color(1, 0.5f, 0, 1); // RGBA로 직접 지정 (주황색)
spriteRenderer.color = Color.white; // 원본 색상 (색상 곱셈의 항등원)
goodColor / badColor를 사용하는 이유: 어떤 색이 "좋은 문"인지 코드에 하드코딩하지 않고 Inspector에서 설정합니다. 게임 디자이너가 색상을 바꾸고 싶을 때 코드를 수정하지 않아도 됩니다.
| 문 타입(type) | 색상 | 이유 |
|---|---|---|
Plus (덧셈) |
goodColor |
공룡 수 증가 → 유리한 문 |
Times (곱셈) |
goodColor |
공룡 수 증가 → 유리한 문 |
Minus (뺄셈) |
badColor |
공룡 수 감소 → 불리한 문 |
Division (나눗셈) |
badColor |
공룡 수 감소 → 불리한 문 |
4. SettingDoors() - 조건문(conditional statement)으로 문 초기화
public void SettingDoors()
{
if (rightDoorType.Equals(DoorType.Plus))
{
rightDoorSpriteRD.color = goodColor;
rightDoorText.text = "+" + rightDoorNumber;
}
else if (rightDoorType.Equals(DoorType.Minus))
{
rightDoorSpriteRD.color = badColor;
rightDoorText.text = "-" + rightDoorNumber;
}
else if (rightDoorType.Equals(DoorType.Times))
{
rightDoorSpriteRD.color = goodColor;
rightDoorText.text = "x" + rightDoorNumber;
}
else if (rightDoorType.Equals(DoorType.Division))
{
rightDoorSpriteRD.color = badColor;
rightDoorText.text = "÷" + rightDoorNumber;
}
// leftDoorType도 동일한 구조로 처리
}
Start()에서SettingDoors()를 호출하므로, 게임 시작 시 각 문의 색상과 텍스트가 자동으로 초기화됩니다.public으로 선언되어 있어 외부 스크립트(예: 맵 생성 스크립트)에서 문 설정을 바꾼 뒤 다시 호출할 수도 있습니다.- 텍스트는 문자열(string) 연결로 구성됩니다:
"+" + 3→"+3","x" + 2→"x2"
TextMeshPro.text: TextMeshPro 컴포넌트에 표시할 문자열(string)을 설정합니다. .text 프로퍼티에 문자열(string)을 대입하면 즉시 화면에 반영됩니다.
5. GetDoorType / GetDoorNumber - 삼항 연산자(operator)로 좌/우 판단
public DoorType GetDoorType(float xPos) { return xPos > 0 ? rightDoorType : leftDoorType; }
public int GetDoorNumber(float xPos) { return xPos > 0 ? rightDoorNumber : leftDoorNumber; }
공룡이 충돌한 문이 오른쪽인지 왼쪽인지를 공룡의 X 좌표(xPos)로 판단합니다.
월드 좌표 X > 0 → 오른쪽 문
월드 좌표 X < 0 → 왼쪽 문
삼항 연산자(operator): 조건 ? 참일 때 값 : 거짓일 때 값 형태입니다. if-else를 한 줄로 표현할 때 사용합니다.
// if-else 버전
if (xPos > 0)
return rightDoorType;
else
return leftDoorType;
// 삼항 연산자 버전 (동일한 동작)
return xPos > 0 ? rightDoorType : leftDoorType;
이 두 메서드(method)는 DinoController의 충돌 처리 코드에서 호출됩니다(튜토리얼 52에서 학습합니다):
// 호출 예시 (튜토리얼 52에서 등장)
DoorType doorType = doors.GetComponent<SelectDoors>().GetDoorType(transform.position.x);
int doorNum = doors.GetComponent<SelectDoors>().GetDoorNumber(transform.position.x);
Unity 씬 설정 요약
| 항목 | 권장 설정 |
|---|---|
| 문 프리팹 | 빈 GameObject에 SelectDoors 부착, 좌/우 문 오브젝트를 자식으로 배치 |
rightDoorSpriteRD / leftDoorSpriteRD |
각 문 오브젝트의 SpriteRenderer 컴포넌트 할당 |
rightDoorText / leftDoorText |
각 문 오브젝트의 TextMeshPro 컴포넌트 할당 |
rightDoorType / leftDoorType |
Inspector 드롭다운에서 Plus / Minus / Times / Division 선택 |
rightDoorNumber / leftDoorNumber |
Inspector에서 연산에 사용할 숫자 입력 |
goodColor / badColor |
Inspector Color Picker에서 색상 지정 (예: 파랑/빨강) |
요약
| 개념 | 사용 위치 | 핵심 |
|---|---|---|
enum DoorType |
클래스(class) 외부 선언 | 문 종류를 의미 있는 이름으로 관리 |
[SerializeField] private |
필드(field) 선언 | Inspector 편집 허용 + 외부 접근 차단(캡슐화(encapsulation)) |
SpriteRenderer.color |
SettingDoors() |
문 타입(type)에 따라 색상 동적 변경 |
TextMeshPro.text |
SettingDoors() |
연산 기호와 숫자를 문에 표시 |
| 삼항 연산자(operator) | GetDoorType, GetDoorNumber |
X 좌표로 오른쪽/왼쪽 문 구분 |