🎮 PlayerCondition 시스템 구현 및 디버깅
🔧 핵심 흐름 정리
- Condition.cs: 상태 수치 (current, max, passive) 를 관리하는 공통 컴포넌트
- PlayerCondition.cs: 헝거(Hunger), 체력(Health), 정신력(Sanity) 간 상호작용 로직 작성
- UICondition.cs: 상태 값을 UI 슬라이더와 연결하여 실시간 반영
🧨 주요 문제 및 해결 과정
- 현상: 시간이 지나도 Hunger가 감소하지 않고 오히려 증가
- 원인: passiveValue가 음수값(-20) → Subtract(-x) = Add(x) 효과
- 해결: Mathf.Abs()로 양수 처리하여 항상 감소 방향 보장
hunger.Subtract(Mathf.Abs(hunger.passiveValue) * Time.deltaTime);
- 헝거가 0에 도달하면 체력이 감소하도록 연동 (의도한 리스크 감정 설계 구현)
- 콘솔 로그 디버깅 습관 형성 → 실시간 수치 변화를 추적해 원인 빠르게 파악 가능
🧱 UI 자동 정렬 시스템: Vertical Layout Group 활용 체험
🎯 실습 내용
- Vertical Layout Group 컴포넌트를 활용해 버튼, 상태창 등의 UI를 자동으로 수직 정렬
- Content Size Fitter를 함께 적용해 자식 오브젝트 크기에 따라 부모 영역 자동 조절
- 불필요한 수동 위치 조정 없이 정렬 가능해짐
✨ 느낀 점
- UI를 코드로 움직이지 않아도 정렬이 자동으로 해결되는 경험은 처음이었음
- 다양한 해상도, 콘텐츠 길이 변화에도 유연하게 대응됨 → 유지보수 용이
- Scroll View의 내부 콘텐츠 영역에 필수적 요소임을 체감
“정렬은 코딩이 아니라 컴포넌트 설정으로 끝낼 수 있다.” → UI에 대한 생각 전환
🔒 코드 안정성 확보: 방어 코드와 싱글턴 보호 구조 적용
✅ Null 방지 체크 습관화
if (target != null)
{
target.DoSomething();
}
- 오브젝트 참조가 사라졌을 때 발생하는 NullReferenceException 방지 습관 확립
✅ DontDestroyOnLoad 적용 실습
- 씬이 전환되어도 오브젝트를 유지하기 위한 구조 구현
- 게임 매니저, BGM 컨트롤러 등은 DontDestroyOnLoad()로 생존 보장
🚨 실습 중 실수로 싱글턴 오브젝트가 중복 생성되는 문제도 겪음 → 조건 검사 필요함
🛠️ 생산성 도구 활용 습관 정착
✅ 불필요한 using 정리 습관화
작업 단축키 (Visual Studio 기준)
| using 제거 | Ctrl + R, G |
| 코드 자동 정렬 | Ctrl + K, D |
| 선택 영역만 정렬 | Ctrl + K, F |
✅ 코드 탐색 효율화
- Ctrl + 클릭: 클래스/메서드 정의로 즉시 이동
- Alt + ← / →: 앞뒤 탐색 히스토리 이동 (실전에서 탐색 피로도 극감함)
Ctrl + 클릭은 오늘 처음 썼지만 앞으로 중독될 예정. "코드의 순간이동" 체감 완료.
⚠️ 메서드 시그니처 충돌 경험 및 조치
🧨 발생 오류
'PlayerCondition' 형식은 동일한 매개 변수 형식을 가진 'Heal' 멤버를 미리 정의합니다.
- 원인: 같은 이름과 매개변수(float)를 가진 메서드가 중복 정의됨
- 조치: 메서드 이름을 RestoreHealth, RecoverHunger 등으로 명확히 변경
public void RestoreHealth(float amount) { ... }
public void RecoverHunger(float amount) { ... }
✍️ 코딩 시 메서드 명명은 시그니처와 함께 유일성을 유지해야 한다는 원칙 체득
🎯 오늘의 키워드
- 상태 시스템 디버깅
- UI 자동 정렬 레이아웃
- 코드 안정화와 예외 방지 습관
- 생산성 도구 (정렬, 탐색, 정리) 체득
- 메서드 충돌 실습 통한 시그니처 관리 능력 향상
🧠 2025 Unity 3D 입문 기술노트 (이론 중심)
🌌 Skybox (스카이박스)
✔️ 개념 요약
- 3D 씬의 배경을 구성하는 큐브 형태 또는 구형의 텍스처 맵핑 기법
- 플레이어 시점에서 주변 환경 전체를 감싸며 하늘, 구름, 별, 안개 등 자연적 요소를 표현
- 주로 큐브맵(6면) 또는 프로시저럴(Procedural) 방식으로 구성
✔️ 종류
종류 설명
| Skybox/6 Sided | 전통적인 큐브 형태. 6개의 텍스처로 구성된 정육면체 맵 |
| Skybox/Cubemap | 하나의 큐브맵 텍스처를 이용하는 포맷 (모바일에서 효율적) |
| Skybox/Procedural | 시간, 태양 위치 등을 반영해 자동 계산되는 동적 스카이박스 (Day/Night 효과에 유리) |
✔️ 설정 위치
- Window > Rendering > Lighting > Environment > Skybox Material
- 또는 코드로 설정:
RenderSettings.skybox = yourSkyboxMaterial;
✔️ 특징 및 팁
- 카메라가 이동해도 Skybox는 고정되어 시점을 유지 → 몰입감 ↑
- 환경광 조명, 반사 광에도 간접적으로 영향 (특히 Reflection Probe와 연계 시)
- 고해상도 Skybox는 퍼포먼스에 영향을 줄 수 있어, 모바일 플랫폼에서는 간단한 Cubemap 추천
🧲 Rigidbody & ForceMode
✔️ Rigidbody 개요
- Unity의 물리 엔진을 사용하는 모든 물리 기반 오브젝트의 핵심 컴포넌트
- 중력, 힘, 토크, 질량, 속도 등 다양한 물리 속성 보유
- 게임 내 물리 충돌, 중력 낙하, 발사체, 점프 등 구현의 중심
✔️ Rigidbody 주요 설정 항목
속성 설명
| Mass | 질량. 가속도 및 충돌 반응에 영향 |
| Drag / Angular Drag | 선형/회전 감속 (마찰과 비슷한 역할) |
| Use Gravity | 중력 사용 여부 (떨어지는 물체인지 여부 설정) |
| Is Kinematic | 물리 엔진에 영향을 받지 않게 함 (스크립트 이동 전용 객체에 적합) |
| Interpolate | 카메라 떨림 방지를 위한 부드러운 위치 보간 방식 |
✔️ ForceMode 종류 (AddForce의 두 번째 인자)
ForceMode 효과 사용 예
| Force | 질량 기반 지속적인 힘 | 풍압, 지속 가속 |
| Acceleration | 질량 무시. 순수 가속도 | 무중력 상태 추진 |
| Impulse | 질량 기반 짧고 강한 힘 | 점프, 피격 반동 |
| VelocityChange | 질량 무시. 속도 변경 | 순간 회피, 대쉬 |
rb.AddForce(Vector3.forward * 10f, ForceMode.Impulse);
✔️ 참고 사항
- Force와 Impulse는 질량에 영향을 받음
- VelocityChange와 Acceleration은 질량을 무시함
- 타격감이 중요한 경우 → Impulse 추천
- 대쉬, 텔레포트 느낌 → VelocityChange
🔦 Ray & Raycast
✔️ Ray란?
- **시작점(origin)**과 **방향(direction)**을 가진 보이지 않는 선 (일종의 레이저 포인터)
- Unity에서 광선처럼 사용하여 충돌 대상을 탐색하거나 위치를 지정하는 데 사용
✔️ Raycast란?
- Ray를 발사하여 일정 거리 내에서 오브젝트와 충돌하는지를 검사하는 방식
- Physics.Raycast()로 구현하며, UI를 제외한 물리 오브젝트 검사에 사용
✔️ 사용 예시
Ray ray = new Ray(transform.position, transform.forward);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100f))
{
Debug.Log("Hit object: " + hit.transform.name);
}
✔️ 활용 사례
- 총알 맞은 오브젝트 체크
- 마우스 클릭 대상 확인 (카메라.ScreenPointToRay 사용)
- NPC 시야, 감지 시스템
- 조준 보조 시스템 (중심선 기반 타겟팅)
✔️ RaycastHit 주요 정보
속성 설명
| .point | 충돌 지점의 위치 |
| .transform | 맞은 대상의 Transform |
| .distance | 충돌까지의 거리 |
⚠️ Raycast는 기본적으로 Collider가 붙은 물체에만 반응함. 레이어 마스크를 사용해 검사 범위 조절 가능.
⌨️ Unity Input System (신 입력 시스템)
✔️ 개요
- Unity의 최신 입력 시스템 (Input System Package)
- 기존 Input.GetKey 또는 Input.GetAxis 방식보다 구조화되어 있음
- 키보드, 마우스, 터치, 게임패드 등 다양한 입력 장치를 Action 기반으로 통합 관리
✔️ 주요 구성 요소
구성 설명
| Input Actions Asset | .inputactions 파일로, 모든 입력을 구조화하여 정의 |
| InputActionMap | 특정 상태/상황별로 묶은 입력 세트 (예: Player, UI) |
| InputAction | 실제 키 동작을 정의하는 단위 (Jump, Move 등) |
| Binding | 어떤 키/버튼이 InputAction과 연결되는지 설정 |
✔️ 주요 이벤트 흐름
- started : 키를 누르기 시작할 때
- performed : 입력이 인식되었을 때 (ex. 누르고 있음)
- canceled : 입력이 해제될 때 (ex. 키를 뗐을 때)
public void OnJump(InputAction.CallbackContext context)
{
if (context.performed)
{
Jump();
}
}
✔️ 처리 방식 3종 비교
방식 개요 장점 단점
| SendMessage | "On+이름" 메서드를 자동 호출 | 간단함, 자동 연결 | 느리고 디버깅 어려움 |
| Invoke UnityEvent | 인스펙터에서 연결 | 디자이너 친화적 | 실수 시 런타임 에러, 성능 낮음 |
| C# Event 바인딩 | 코드에서 직접 연결 | 성능 좋고 명확 | 구현 구조를 정확히 이해해야 함 |
✔️ PlayerInput 컴포넌트 설정
- Input Actions를 연결하면 자동으로 이벤트 방식 선택 가능 (SendMessage / InvokeUnityEvent / C# Events)
- Behavior 설정에서 선택 가능:
- Invoke Unity Events
- Send Messages
- Invoke C# Events
✔️ Input System의 장점
- 여러 플랫폼 입력을 하나의 인터페이스로 처리
- 입력 방식 간 전환이 쉬움 (키보드 → 패드 → 모바일)
- 멀티 키 바인딩, 디바이스 인식 자동 처리 가능
- 런타임 중 입력 스킴 전환 가능
🖱️ Scroll View & UI 자동 배치 시스템
✔️ Scroll View란?
- Unity UI 시스템에서 스크롤 가능한 영역을 제공하는 컨테이너 컴포넌트
- 내부 콘텐츠가 뷰포트를 넘어설 경우 자동으로 스크롤바 생성
- 주로 리스트, 인벤토리, 로그창, 대화창 등에 사용됨
✔️ 구성 요소 구조
Scroll View (스크롤 전체)
├── Viewport (보여지는 화면 영역)
│ └── Content (실제 콘텐츠들)
└── Scrollbar (세로/가로 선택적)
- Viewport: 마스크 역할. 이 영역 바깥의 콘텐츠는 보이지 않음
- Content: 이 안에 Vertical Layout Group 등으로 버튼/텍스트 정렬
- Scrollbar: 자동 활성화되거나 수동으로 배치 가능
✔️ 필수 조합 컴포넌트
컴포넌트 역할
| Vertical/Horizontal Layout Group | 자식 UI 요소들을 자동으로 세로/가로 정렬 |
| Content Size Fitter | 콘텐츠의 크기를 자식 요소에 맞게 자동 조절 |
| Layout Element | 자식 요소 각각의 사이즈/우선순위 세부 조정 가능 |
✔️ 일반적인 설정 흐름
- Scroll View 생성 → 내부 구조 그대로 유지
- Content에 Vertical Layout Group + Content Size Fitter 추가
- 버튼, 텍스트, 슬롯 등 요소들을 Content의 자식으로 추가
🧠 왜 중요한가?
- 콘텐츠 개수가 달라져도 자동으로 배치됨 → UI 제작 시간 감소
- 디바이스 해상도가 달라져도 대응 가능 → 반응형 디자인
- 런타임 중 버튼 추가/삭제가 빈번한 UI에서 유지보수성이 매우 뛰어남
✔️ 예제: 동적으로 버튼 추가
GameObject btn = Instantiate(buttonPrefab, contentTransform);
btn.GetComponentInChildren<Text>().text = "New Option";
이때도 Vertical Layout Group이 잡혀 있으면 자동으로 위치가 맞춰짐
⚠️ 주의 사항
- Content Size Fitter를 사용할 경우 스크롤뷰와 충돌하는 경우가 있음
→ Layout Group과 함께 쓰되, Content의 RectTransform anchor/pivot 조정 필수 - 모바일 UI에서는 스크롤바보다 터치 스크롤 감도 조절이 중요
🖱️ UI Event System & Graphic Raycaster
✔️ UI Event System이란?
- Unity에서 **UI 상호작용(클릭, 드래그, 포커스 등)**을 처리하기 위한 이벤트 처리 시스템
- UI뿐 아니라 모든 입력 기반 트리거의 핵심 관리자 역할을 함
✔️ 구성 요소
구성 요소 설명
| EventSystem | 씬에 하나만 존재. 모든 UI 입력 이벤트를 처리하는 중심 객체 |
| Standalone Input Module | 마우스/키보드 입력 처리용. 기본 모듈 |
| Input System UI Module | 신 Input System 기반의 UI 입력 처리용 모듈 (입력 시스템을 설정한 경우 자동 사용됨) |
| Graphic Raycaster | Canvas 상에서 어떤 UI 요소가 클릭되었는지 감지하는 컴포넌트 |
✔️ 작동 흐름 요약
- 사용자가 마우스 클릭 or 터치 입력
- EventSystem이 입력을 감지
- Canvas 상에 붙은 Graphic Raycaster가 UI 요소에 Raycast를 쏴서 클릭된 오브젝트 탐색
- 클릭된 오브젝트에 연결된 인터페이스 함수 실행 (예: IPointerClickHandler)
🎮 자주 사용하는 인터페이스
인터페이스 설명
| IPointerClickHandler | 클릭 시 동작 |
| IPointerEnterHandler | 마우스를 올렸을 때 동작 |
| IPointerExitHandler | 마우스가 벗어날 때 동작 |
| IDragHandler | 드래그 중 동작 |
| IDropHandler | 드래그 놓기 동작 |
using UnityEngine.EventSystems;
public class MyButton : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("버튼 클릭됨!");
}
}
💡 유니티의 UI 버튼은 실제로 Button 클래스 내부에서 이 인터페이스들을 구현하고 있음
✅ Graphic Raycaster의 역할
- Canvas에 부착되며, UI 요소에 Ray를 쏘아 감지
- LayerMask를 통해 감지할 UI의 레이어를 제한할 수 있음
- Physics Raycaster와 달리, UI 전용 Raycast임 (3D 물체 탐지는 해당 안 됨)
⚠️ 주의 사항
- UI가 Canvas의 하위에 있어야 Raycast 감지가 가능
- Canvas가 World Space일 경우, 카메라 설정이 정확해야 Ray 감지가 동작
- UI에 Raycast Target이 꺼져 있으면 클릭 불가 (예: 투명 이미지 등)
💡 팁: Physics.Raycast와의 차이
항목 UI Raycast (Graphic Raycaster) 물리 Raycast (Physics.Raycast)
| 대상 | UI 요소 (Image, Button 등) | Collider가 붙은 3D/2D 오브젝트 |
| 감지 방식 | Canvas 기준 UI Ray 투사 | 실제 공간에 직선 광선 투사 |
| 필요 조건 | Canvas + GraphicRaycaster + EventSystem | Collider + Rigidbody 등 |
⏱️ Time.deltaTime과 Update 루프 개념
✔️ Time.deltaTime이란?
- **이전 프레임과 현재 프레임 사이에 걸린 시간(초)**을 의미
- 매 프레임마다 값이 변하며, 프레임 속도에 따라 일정하지 않음
- 주로 이동, 회전, 상태 감소 등 프레임 기반 계산을 보정하기 위해 사용
transform.Translate(Vector3.forward * speed * Time.deltaTime);
→ 매초 speed만큼 이동하게 보정됨
✔️ 왜 중요한가?
- Unity는 기본적으로 프레임 단위로 Update()를 실행함
- 하지만 프레임이 60FPS일 때와 30FPS일 때는 같은 코드라도 속도가 달라짐
- deltaTime을 곱해주면 프레임에 관계없이 일정한 속도로 움직이거나 변화함
🧠 쉽게 말하면: “초당 얼마나 움직일지를 프레임 수에 따라 자동 보정해주는 값”
🔁 Unity의 주요 루프 구조
✅ Update()
- 매 프레임마다 호출되는 함수 (1초에 60프레임이면, 60번 호출됨)
- 입력 처리, 상태 변화, 실시간 연산 등 대부분의 로직은 여기에 작성
void Update()
{
MovePlayer();
RotateEnemy();
}
✅ FixedUpdate()
- 물리 계산용 전용 루프 (기본적으로 0.02초마다 실행 = 50FPS 고정)
- Rigidbody 움직임이나 충돌 처리 등은 여기에서 실행해야 예측 가능함
void FixedUpdate()
{
rb.AddForce(force);
}
✅ LateUpdate()
- 모든 Update() 호출 후 실행
- 주로 카메라 이동, UI 동기화 등 오브젝트가 이동된 후 처리할 때 사용
💡 Update 루프 선택 기준
함수 사용 목적 비고
| Update() | 키 입력, 상태 처리, UI 갱신 등 | Time.deltaTime 사용 필요 |
| FixedUpdate() | 물리 힘 적용, Rigidbody 제어 | deltaTime 대신 fixedDeltaTime 내장됨 |
| LateUpdate() | 카메라, 따라가기, 정렬 등 | 순서상 마지막 실행됨 |
🧠 요약 비유
- Update() → 실시간 사용자 입력 & 상태 변화
- FixedUpdate() → 일정한 틱으로 움직이는 기계
- LateUpdate() → 마지막에 따라붙는 카메라맨
🧩 Component vs GameObject vs MonoBehaviour 구조
✔️ GameObject란?
- Unity의 씬 상에 존재하는 모든 오브젝트의 기본 단위
- GameObject 그 자체는 아무런 기능이 없음 → Component들을 조합해 기능 부여
- 트랜스폼(위치/회전/크기), 메쉬, 콜라이더, 스크립트 등은 모두 컴포넌트로 붙음
GameObject player = new GameObject("Player");
✔️ Component란?
- GameObject에 붙는 기능 단위 조각
- Transform, Rigidbody, Collider, AudioSource, Script 등 전부 Component
- GameObject에 여러 Component를 붙여서 완성된 기능의 오브젝트를 만듦
player.AddComponent<Rigidbody>();
Unity 구조의 핵심 철학: 모든 동작은 Component의 조합이다.
✔️ MonoBehaviour란?
- Unity의 모든 사용자 정의 스크립트가 상속받는 기본 클래스
- Update(), Start(), OnTriggerEnter() 등 Unity 생명주기를 사용할 수 있게 해줌
- 반드시 GameObject에 붙어야 동작함
public class PlayerController : MonoBehaviour
{
void Update()
{
Move();
}
}
- MonoBehaviour는 자체적으로 new 생성이 불가능하고, 반드시 GameObject에 AddComponent로 추가되어야 함
🔄 관계 정리
GameObject
├── Transform (항상 존재)
├── Rigidbody (Component)
├── BoxCollider (Component)
└── PlayerController (MonoBehaviour Script → Component)
- GameObject: 껍데기
- Component: 기능 조각
- MonoBehaviour: 사용자 정의 기능을 만들기 위한 Component 기반 클래스
🎯 실전 예시
GameObject enemy = GameObject.Find("Enemy");
Rigidbody rb = enemy.GetComponent<Rigidbody>();
- Find()로 GameObject를 찾고
- GetComponent<T>()로 붙어있는 기능을 찾아 가져옴
🧠 요약 정리
개념 설명
| GameObject | 씬 위의 존재 단위 (비어있는 틀) |
| Component | GameObject에 부착되는 기능 모듈 (조각) |
| MonoBehaviour | Unity의 사용자 스크립트 기반 클래스 (컴포넌트로 동작) |
🎓 “GameObject는 컨테이너, Component는 기능, MonoBehaviour는 우리가 직접 만드는 기능 컴포넌트”
🎞️ Animation과 Animator Controller 구조
✔️ Animation과 Animator 차이
용어 역할
| Animation Clip | 실제 애니메이션 데이터 (ex: 달리기, 점프, 피격 등) |
| Animator Controller | 여러 애니메이션 클립을 상태(State)로 구성하고 전환(Transition) 제어 |
| Animator | GameObject에 붙는 컴포넌트. Animator Controller를 받아 실행 담당 |
✅ 구성 요소 예시
Player (GameObject)
├── Animator (Component)
└── Animator Controller (에셋)
├── Idle (Animation Clip)
├── Run (Animation Clip)
└── Jump (Animation Clip)
- Animator → 씬에서 동작
- Animator Controller → 클립을 묶어 로직 제어
- Animation Clip → 실제 움직임 데이터
🎮 Animator Controller의 핵심 구조
- State Machine 기반
- 각 상태(State)에 Animation Clip이 연결됨
- 상태 간 전환(Transition)은 **조건(Condition)**을 기반으로 이루어짐
예: 상태 전환 흐름
Idle → [조건: Speed > 0.1] → Run
Run → [조건: IsJump == true] → Jump
Jump → [조건: Grounded == true] → Idle
✔️ Transition 조건 설정
- Animator Controller에서 파라미터(Parameter)를 정의하고
- 상태 전환에 조건으로 사용함
파라미터 종류
유형 설명
| Float | 이동 속도 등 연속적인 값 |
| Bool | 점프 중 여부, 피격 여부 등 |
| Int | 상태 구분용 정수 값 |
| Trigger | 한 번만 발동되는 이벤트 (ex: 공격) |
✍️ 스크립트에서 Animator 제어
Animator anim = GetComponent<Animator>();
// bool 타입 파라미터 설정
anim.SetBool("IsJump", true);
// Trigger 발동
anim.SetTrigger("Attack");
- Animator 파라미터의 이름은 정확히 일치해야 함 (오타 주의)
🧠 애니메이션 동기화 팁
- Root Motion 사용 시 → 애니메이션이 이동/회전을 포함함
- Root Motion 비활성화 시 → Transform 이동은 스크립트로 제어
📌 캐릭터 조작이 중심이면 Root Motion Off + 코드 기반 이동
📌 Cutscene/전용 연출이면 Root Motion On 사용 가능
🏷️ Unity Tag vs Layer 차이 정리
✔️ Tag란?
- 오브젝트에 **의미 있는 이름표(Label)**를 붙이는 기능
- 오브젝트 간의 구분, 조건 탐색, 충돌 식별 등에 사용됨
- 예시: Player, Enemy, Item, Checkpoint
✅ Tag 특징
- 문자열 기반
- 태그는 여러 오브젝트가 공유할 수 있음
- CompareTag() 또는 tag 속성으로 코드에서 판별
if (other.CompareTag("Enemy"))
{
TakeDamage();
}
✅ Tag 설정 방법
- 오브젝트 선택 > Inspector > Tag > Add Tag... 에서 사용자 정의 가능
🧱 Layer란?
- 카메라/물리 시스템이 인식하는 필터 레이어
- 충돌 감지, 카메라 렌더링, 레이캐스트 대상 설정 등에 사용됨
- 숫자 기반 인덱스를 가지며, 총 32개까지 설정 가능 (0~31)
✅ Layer 특징
용도 설명
| 카메라 Culling Mask | 특정 Layer만 렌더링 여부 설정 |
| Raycast | 특정 Layer에만 반응할 수 있도록 설정 |
| Physics 충돌 매트릭스 | Edit > Project Settings > Physics 에서 충돌 여부 설정 |
int layerMask = 1 << LayerMask.NameToLayer("Enemy");
if (Physics.Raycast(ray, out hit, 100f, layerMask))
{
Debug.Log("Enemy 감지됨");
}
✅ Layer 설정 방법
- Inspector > Layer > Add Layer... 에서 사용자 정의
- 이후 오브젝트에 해당 Layer를 할당
🔄 Tag vs Layer 요약 비교
구분 Tag Layer
| 목적 | 구분/식별 | 충돌/렌더링 필터링 |
| 데이터 형태 | 문자열 | 숫자 인덱스 (최대 32개) |
| 설정 위치 | Inspector > Tag | Inspector > Layer |
| 코드 사용 | CompareTag("Player") | LayerMask, gameObject.layer 등 |
| 중복 사용 | 가능 (여러 오브젝트에 동일 태그 가능) | 단일 오브젝트당 하나의 레이어 |
🧠 Tag는 ‘이름표’, Layer는 ‘필터링 도구’라고 생각하면 이해 쉬움
🔗 Unity 오브젝트 참조 방식 요약
Unity에서는 코드에서 특정 오브젝트나 컴포넌트를 참조하기 위해 여러 가지 방법이 존재합니다.
상황에 따라 적절한 방법을 선택하면 코드 유지보수성과 성능이 크게 향상됩니다.
✅ 1. GetComponent<T>()
- 현재 GameObject 또는 자식 오브젝트에서 컴포넌트를 가져옴
- 가장 기본적이며 자주 쓰이는 방식
Rigidbody rb = GetComponent<Rigidbody>();
⚠️ 성능을 고려해 Start()나 Awake()에서 캐싱하는 것이 좋음
✅ 2. GameObject.Find()
- 씬 안의 GameObject를 이름(String)으로 찾아줌
- 사용은 간단하지만, 느리고 의존성이 높기 때문에 자주 사용하지 말 것
GameObject player = GameObject.Find("Player");
❌ 오타 발생 시 런타임 오류, 비효율적인 탐색 구조
✅ 3. transform.Find()
- 현재 오브젝트의 자식 중에서 이름으로 탐색
- 구조가 정해진 경우에는 유용함
Transform hand = transform.Find("Hand_R");
✅ 4. FindObjectOfType<T>()
- 씬 내에서 특정 타입의 첫 번째 오브젝트를 찾아 반환
- 단일 매니저, Singleton 참조에 많이 사용됨
GameManager gm = FindObjectOfType<GameManager>();
⚠️ 실행 중 호출하면 성능 저하 가능 → Start나 Awake에서 1회만 호출
✅ 5. public 필드 + 인스펙터 할당
- 가장 안전하고 직관적인 방법
- 오브젝트나 컴포넌트를 Drag & Drop으로 할당
public GameObject weapon; // 인스펙터에서 직접 연결
✅ 가장 권장되는 방식 (수동 설정이 가능할 때)
✅ 6. SerializeField + private
- 외부 접근은 막으면서 인스펙터 노출 가능
[SerializeField] private PlayerController controller;
✅ 캡슐화와 인스펙터 편집을 모두 만족
✅ 7. Singleton 패턴으로 접근
- 전역에서 접근이 필요한 매니저 계열 객체에 사용
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
private void Awake()
{
Instance = this;
}
}
// 접근 예시
GameManager.Instance.DoSomething();
🧠 요약 정리
방식 용도 특징
| GetComponent<T>() | 내 컴포넌트 참조 | 가장 기본적. 캐싱 권장 |
| GameObject.Find() | 이름으로 찾기 | 비효율적. 지양 |
| FindObjectOfType<T>() | 타입으로 찾기 | 매니저 참조에 용이 |
| Inspector 연결 | 수동 연결 | 가장 안전하고 직관적 |
| Singleton | 전역 접근 | 매니저, UI 컨트롤에 적합 |
🔄 Unity Script 실행 순서 및 초기화 흐름
Unity에서는 스크립트의 생명주기(Lifecycle)가 정해져 있으며, 특정 함수들은 정해진 순서와 타이밍에 따라 호출됩니다.
이를 이해하면 객체 초기화, 물리 처리, 사용자 입력 반영 등을 정확한 타이밍에 구현할 수 있습니다.
✔️ 주요 실행 순서 요약
순서 함수 설명
| ① | Awake() | 스크립트가 활성화될 때 가장 먼저 실행. 오브젝트 간 참조 설정에 적합 |
| ② | OnEnable() | 오브젝트가 활성화될 때 호출. 매번 Enable 시마다 실행됨 |
| ③ | Start() | 첫 프레임 직전 한 번만 호출. 초기화 작업에 주로 사용 |
| ④ | Update() | 매 프레임마다 실행. 입력 처리, 상태 갱신 등 |
| ⑤ | LateUpdate() | 모든 Update 후 실행. 카메라 추적 등에 사용 |
| ⑥ | FixedUpdate() | 고정된 시간 간격으로 실행. 물리 연산용 |
| ⑦ | OnDisable() | 오브젝트 비활성화될 때 호출 |
| ⑧ | OnDestroy() | 오브젝트가 파괴되기 직전 호출 |
✅ 실행 순서 흐름 예시
Awake()
→ OnEnable()
→ Start()
→ Update() / FixedUpdate() (반복)
→ OnDisable()
→ OnDestroy()
🧠 함수별 주요 역할
함수 주로 사용하는 목적
| Awake() | 스크립트 내부 변수 초기화, 싱글턴 설정 등 |
| Start() | 외부 참조 초기화 (다른 오브젝트가 Awake한 뒤 실행됨) |
| Update() | 실시간 입력 처리, 상태 변화 등 |
| FixedUpdate() | Rigidbody 이동/힘 적용 등 물리 처리 |
| LateUpdate() | 카메라 따라가기, 위치 동기화 등 |
| OnEnable() | 씬 재시작, UI 리셋 등에 활용 가능 |
| OnDestroy() | 데이터 저장, 종료 처리 등 정리 로직에 사용 |
📝 실습 팁
- 싱글턴은 대부분 Awake()에서 Instance = this로 설정
- 오브젝트 간 의존성이 있을 때는 Start()에서 참조 연결
- FixedUpdate()는 프레임률에 관계없이 일정 간격 → Time.deltaTime 사용 ❌
⚠️ 주의 사항
- 비활성화된 오브젝트는 Start(), Update()가 호출되지 않음
- Awake()는 오브젝트가 비활성화여도 호출됨 (단, 비활성 상태에서 Instantiate한 경우)
📦 ScriptableObject란? 구조와 활용
✔️ ScriptableObject 개념
- Unity에서 제공하는 데이터 중심 객체 생성용 클래스
- 일반 MonoBehaviour와 다르게 씬에 존재하지 않고, Hierarchy에 붙지 않음
- 프로젝트 내에 에셋으로 존재하며, 공유 가능한 데이터 템플릿 역할
[CreateAssetMenu(fileName = "NewStat", menuName = "Custom/StatData")]
public class StatData : ScriptableObject
{
public string characterName;
public int maxHealth;
public float moveSpeed;
}
✅ 생성 방법
- ScriptableObject를 상속받는 클래스를 정의
- [CreateAssetMenu] 어트리뷰트로 생성 메뉴에 노출
- Project 창에서 우클릭 > Create > Custom/StatData로 생성
🎯 주요 특징
항목 설명
| 독립성 | 씬에 존재하지 않음. Hierarchy에 붙지 않음 |
| 참조 가능성 | 여러 오브젝트가 하나의 ScriptableObject를 참조 가능 |
| 사용 예 | 스탯 템플릿, 아이템 정보, 설정값, 테이블 데이터 등 |
| 변경 반영 | ScriptableObject를 수정하면 이를 참조한 모든 오브젝트가 반영됨 |
🎮 캐릭터의 스탯, 무기 속성, 대사 데이터 등 반복되는 구조를 공유할 때 매우 유리
🧪 예제: 무기 데이터 활용
public class Weapon : MonoBehaviour
{
public WeaponData weaponData;
void Start()
{
Debug.Log(weaponData.damage);
}
}
[CreateAssetMenu(menuName = "Data/Weapon")]
public class WeaponData : ScriptableObject
{
public string weaponName;
public int damage;
public float cooldown;
}
- ScriptableObject는 Project 창에서 직접 만들고, MonoBehaviour에 드래그로 할당
💡 MonoBehaviour vs ScriptableObject
구분 MonoBehaviour ScriptableObject
| 목적 | 씬 내 동작, 컴포넌트 | 데이터 템플릿, 공유 리소스 |
| 저장 위치 | Hierarchy | Project 창 (Asset) |
| 인스턴스화 | Instantiate 필요 | CreateInstance or Asset 생성 |
| 실행 흐름 | Update 등 생명주기 포함 | Update 없음 (데이터 전용) |
🧠 활용 팁
- 변하지 않는 설정값, 수치 데이터, 반복 구조에는 ScriptableObject가 훨씬 효율적
- 씬을 넘어서는 데이터 유지가 필요할 때 적합
- 단, 런타임 변경사항은 저장되지 않음 → 저장 기능이 필요하면 JSON이나 PlayerPrefs와 병행
📓 Unity 입문 백지 학생 메모장
공식 튜터님도 알려주셨고, 강의도 많지만... 지금 머릿속은 혼란 그 자체.
🤯 지금까지 배운 게 너무 많아요
- Skybox가 뭐였는지 기억 안 남
- Rigidbody ForceMode 4개 중 뭐가 뭔지 헷갈림
- Scroll View가 자동 정렬된다는 건 알겠는데, 설정법은 벌써 까먹음
- Animation은 상태 머신이라는데… 내 머리는 스택 머신 상태
😵 헷갈리는 것들 예시
개념나의 혼란
| deltaTime | 왜 곱하는지 알 것 같으면서도 모르겠음 |
| Tag vs Layer | 아직도 충돌에 뭐 쓰는지 햇갈림 |
| GetComponent | 이거 자주 쓰는데 캐싱 안 하면 왜 느린지도 감 못 잡음 |
🧠 하지만 정리는 된다
- 지금 당장은 이해보다는 반복이 필요하다
- 하나씩 직접 해보면 몸이 기억함
- “몰라도 일단 써본다”는 태도로 접근 중
✍️ 나의 전략
- 💡 하나하나 예제로 정리해서 실습 코드 옆에 붙이기
- 🧩 모르는 개념은 바로바로 주석으로 메모
- 🐌 진도는 느려도, 실전 적용되면 오래 간다
👣 지금은 아직 백지 상태지만, 이 백지는 곧 설계도로 바뀐다. 그 첫 벽돌은 “포기하지 않고 하나씩 쌓는 습관”
'팀스파르타 내일배움캠프' 카테고리의 다른 글
| 🎮 Unity 개인 과제 – 점프킹 3D 기능 구현 완료 (1) | 2025.05.23 |
|---|---|
| 📌 Unity에서 인스펙터 드래그 앤 드롭보다 코드 구독이 선호되는 이유 정리 (6) | 2025.05.22 |
| 📅 2025.05.09 / TIL - 팀 작업: StartScene & IntroScene 개발 (0) | 2025.05.09 |
| 2025.05.08 / 아이템 시스템 개발 회고 및 트러블슈팅 (0) | 2025.05.08 |
| 🧠 Unity 부트캠프 5주차 TIL & 트러블슈팅 회고록 (0) | 2025.05.07 |