목차
유니티 숙련 챕터 시작
지난 유니티 입문 챕터를 뒤로하고 새로운 챕터가 시작되었다.
새로운 팀원들과 만나 인사도 하고 팀장도 정하고 (내가 아니다! 예이!) 각자 지급 받은 강의를 시작으로 개인 공부를 진행했다.
중간에 챌린지반 특강도 이어졌다.
스카이박스 (Skybox)
게임 씬을 둘러싸는 환경 배경을 표현하는 기술이다.
하늘, 구름, 산 등 자연 배경을 확장된 월드처럼 보여주기 위해서 사용된다.
실제로는 육면체(Cube Map) 또는 구형(Sphere Map)에 텍스처를 입혀 구현한다.
주요 프로퍼티
- Tint Color: 스카이박스에 색조를 더함
- Exposure: 밝기 조절
- Rotation: Y축 기준 회전
- Front, etc: 큐브맵의 각 면에 들어갈 텍스처 슬롯 지정
동작 및 적용
스카이박스는 모든 불투명 오브젝트 뒤에 렌더링된다.
내부적으로는 큐브 또는 테셀레이트된 스피어메시를 사용한다.
- Skybox Material 생성
- Window > Rendering > Lighting Setting -> Scene 탭에서 스카이박스 설정
- 카메라 단위 오버라이드 필요시에는 카메라 선택 -> Component > Renderting > Skybox 추가 한 뒤 카메라의 Clear Mode를 Skybox로 설정한다.
추가 팁으로는
Fog 컬러는 Skybox 컬러와 유사하게 맞추는 것이 자연스럽고 좋다.
스카이박스는 실시간 교체를 통해 낮/밤 전환이나 이벤트 연출에 사용할 수도 있다.
과도한 해상도/복잡도는 성능 이슈를 유발할 수도 있으니 최적화에 유의해야한다.
Rigidbody ForceMode
ForceMode는 Rigidbody.AddForce() 호출 시 힘의 적용 방식을 결정하는 파라미터이다.
적용 목적(지속적 힘/순간적 힘)에 따라 다른 옵션을 선택해야한다.
ForceMode 종류
| 모드 | 설명 | 사용 |
| Force | 지속적인 힘을 가한다. (물리 시간마다 힘이 누적됨) | 일반적인 중력/가속 표현에 사용 |
| Acceleration | 질량과 무관하게 가속도 적용. | 질량에 영향받지 않게 일정한 가속을 줄 때 사용 |
| Impulse | 순간적인 충격을 줌. | 점프, 총알 피격 등 짧은 시간에 빠른 반응이 필요할 때 |
| VelocityChange | 속도를 직접 바꿈. (질량 무시) | 순간적인 속도 조절에 사용. 제어용 입력에 유용. |
Rigidbody rb = GetComponent<Rigidbody>();
rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
참고
유니티 숙련 주차 1-2 강의 PDF
Raycast
Ray
시작점(origin) + 방향(direction)으로 구성된 가상의 직선을 말한다.
주로 충돌 감지용 광선으로 사용한다.
// 오브젝트 기준 직선
Ray ray = new Ray(transform.position, transform.forward);
// 카메라 중심 기준 Ray
Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
// 마우스 위치 기준 Ray
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Raycast
Ray를 씬에 쏴서 콜라이더 충돌 여부를 확인한다.
물체를 직접 탐지하거나 마우스 클릭, 시선 교차, 총알 판정 등에서 활용할 수 있다.
if (Physics.Raycast(ray, out RaycastHit hit, 100f)) {
Debug.Log("Hit: " + hit.transform.name);
}
Raycast 주요 파라미터
| 파라미터 | 설명 |
| origin | Ray 시작 지점 (월드 좌표) |
| direction | Ray가 나아갈 발향 |
| maxDistance | 최대 충돌 거리 (기본값: 무한대) |
| layerMask | 충돌할 레이어 필터링 |
| queryTriggerInteraction | 트리거 포함 여부 설정 |
RaycastHit 주요 속성
| 속성 | 설명 |
| hit.point | 충돌 위치 (월드 좌표) |
| hit.distance | Ray 시작점에서 충돌점까지의 거리 |
| hit.transfrom | 충돌한 오브젝트의 트랜스폼 참조 |
참고사항
- Ray origin이 Collider 내부에 있으면 감지가 되지 않는다.
- Collider 이동 직후 바로 Raycast하면 인식 안될 수도 있다. -> 최소 한 프레임 (FixedUpdate) 대기 필요.
- Trigger 감지 여부는 QueryTriggerInteraction으로 제어한다.
- Debug.DrawLine(ray.origin, hit.point)를 활용하면 Ray를 시각적으로 확인할 수도 있다.
Input System: SendMessage vs InvokeEvent
SendMessage
- "On<ActionName>" 형태의 메서드를 런타임에 찾아서 호출한다.
- 스크립트에 메서드가 없으면 아무 일도 일어나지 않는다.
- 리플렉션 방식(문자열로 이름을 보고 코드의 구조를 탐색하고 실행하는 방식)으로 성능 비용이 존재한다.
따라서 빠르게 테스트 가능하여 프로토타입에는 유용하지만 느리고 위험(오타 등)하여 실무에서는 권장되지 않는다.
Invoke Unity Event
- InputAction에서 직접 UnityEvent를 연결한다.
- 인스펙터에서 버튼 클릭하듯 메서드를 연결하면 된다.
- 시각적으로 관리하기 쉽고 코드와 분리된다.
인스펙터에서 연결만 하면 되므로 초보자 친화적이다.
Invoke C# Event
- 스크립트 상에서 직접 inputAction.performed += ... 방식으로 등록한다.
- 입력 발생 전/후/해제 등 상황별로 세분화할 수 있다.
- 가장 유연하고 성능이 좋다.
- 명확한 타이밍을 제어할 수 있다.
챌린지반 특강: 디자인패턴
필기는 접은글!
건축서적 A Pattern Language: Towns Buildings, Constructions, written by 크리스토퍼 알렉산더
사람들이 직관적으로 좋다고 느끼는 건축/도시 공간에는 고통된 패턴이 있다.
이 패턴들은 반복적 문제에 대한 해결책으로 재사용 가능하다.
GOF (Gang Of Four) 책 추천.
디자인 패턴을 왜 써야 하는가?
협업 중 다른 사람이 작성한 기존 코드를 파악하거나 수정하고, 여기에 새로운 기능을 더하는 일이 자주 발생.
이 과정에서 의도하지 않은 버그가 생기거나, 성능이 저하되는 경우가 생김.
특히 복잡한 코드일수록 이러한 문제는 심해지고 이로 인해 개발 시간이 늘어난다.
이때 디자인 단지 구현 방법이 아니라, 공통 언어로서도 사용되는 효율적인 커뮤니케이션 수단이다.
-> 패턴 이름만으로도 핵심을 공유할 수 있다. "팩토리로 만들어주세요" 등.
개발 중 마주치는 문제 대부분은 이미 수많은 개발자들이 경험하고 해결책을 만들어낸 문제다.
디자인 패턴은 이러한 축적된 경험의 산물로, 다양한 상황에서 검증된 설계 전략을 제공한다.
다만, 무조건적인 적용은 오히려 해가 될 수 있다.
디자인 패턴은 필요할 때 쓰는 도구일 뿐, 목적이 아님.
패턴 도입시에는 이 상황에서 정말로 필요한가?를 먼저 고민하고 도입해야한다.
싱글톤(Singleton)
프로그램 전체에서 단 하나의 인스턴스만 존재하도록 보장하는 디자인 패턴
장점
전역 접근: 어디서든 접근 가능 (마치 전역 변수처럼)
메모리 절약: 인스턴스를 한 번만 만들고 계속 재사용하니까 낭비가 없음
설치와 초기화가 간단: 처음 한 번 만들고 나면 계속 쓰기만 하면 됨
단점
테스트 어려움: 항상 하나뿐이라서 테스트용으로 교체하기 어려움
사용처 추적이 어려움: 코드 곳곳에서 가져다 쓰면, 의존관계까 꼬이고 추적이 어려움
전역 상태 오염: 여러 곳에서 같은 인스턴스를 쓰다보면 의도치 않게 값이 바뀜
싱글턴에 적합한 클래스들 GameManager, AudioManager, SceneLoader, DataManager, PoolManager 등
공통 특징
오직 1개만 존재.
여러 시스템에서 공통적으로 사용됨.
상태를 계속 유지해야함.
게임 전체 수명주기 동안 살아있음.
정리하자면, 싱글톤은 강력하고 편리하지만 위험한 도구
반드시 하나만 존재하는 이유가 명확할 때만 사용한다.
팩토리(Factory)
객체를 직접 만들지 않고 공장에서 대신 만들어주는 구조
객체를 만들어 반환하는 함수를 제공하여 초기화 과정을 외부에서 보지 못하게 숨기고 반환 타입을 제어하는 방법.
객체를 new 또는 Instantiate를 이용해 직접 만들지 않고 팩토리에게 객체 생성 전담.
왜 사용할까?
직접 new를 하지 않고 Factory 내부에서 생성하니 확장이 쉬움.
조건 분기: 이름, 상태 등 여러 조건에 따라 적절한 객체 생성
결합도 감소: 생성 방식이 바뀌어도 외부 코드는 그대로. 테스트/유지보수 쉬움 -> SOLID 원칙 중 OCP
오브젝트 풀링과 팩토리를 결합할 수도 있다.
Q. 이제까지 클래스마다 들어가서 생성자 생성했었는데 이제 팩토리에서 관리하지 쉽겠다. A. 그 개념이 맞다.
언제 사용할까?
어떤 객체를 생성할지 상위 클래스(매니저 같은)는 모르고, 하위 클래스에서 결정해야 할 때.
객체 종류가 자주 바뀌거나 추가될 수 있을 때.
Q. 팩토리 패턴 네이밍 컨벤션은 클래스 이름 마지막에 Factory 붙이는 걸까요? A. 리드 마음대로다.
옵저버(Observer)
누군가 상태가 바뀌는 걸 관측하고, 관련된 객체들이 자동으로 알림을 받는 구조
왜 사용할까?
어떤 객체가 바뀌면, 그것을 지켜보는 애들이 자동으로 알 수 있게 하려고.
체력, 날씨, 설정 등 공통 데이터를 여러 객체가 바라볼 때.
옵저버 패턴이 없다면 데이터를 바꿀 때마다 수동으로 연결된 모든 객체를 일일히 고쳐야한다.
주로 UI 시스템에서 옵저버 패턴을 진짜 많이 쓰고, 개조해서 업그레이드된 버전이 많다.
Q. 지켜보는 자가 많을수록 필요한 옵저버 A. 좋은 말이다...
언제 사용할까?
상태변경이 여러 객체에 영향을 줄 때.
UI 이벤트 시스템, 게임 내 알림 시스템, 데이터 바인딩, 이벤트 리스터 등등에 활용
Q. 디자인 패턴은 여러 종류를 달아주기 위해서 인터페이스 위주로 개발하는 건가요? A. 종류가 다양해질 것을 대비해서 인터페이스로 구현하는 경우가 많다.
Q. delegate랑 비슷하다고 보면 될까요? A. 주로 delegate를 응용하여 구현하는 것이 옵저버 패턴이다.
중재자(Mediator)
모든 객체가 서로 말하는 대신, 중앙 관리자를 통해 소통
핵심: 객체들끼리 서로 모르게 만들고, 여러 컴포넌트 간의 직접적인 참조를 피하고, 중앙에서 상호작용을 조정하도록 만드는 구조
왜 사용할까?
객체들끼리 직접 연결되면 복잡해지니까 그 소통을 중간 관리자 하나가 대신 처리하게 만들기 위해.
중재자가 없다면 여러 객체들이 서로 직접 참조하면서 소통하며 시간이 지날수록 관계가 거미줄처럼 얽히고 복잡해짐.
객체간 결합도가 매우 높아지며 테스트가 복잡해지고 요지보수가 어려워진다.
MVP가 그런 구조. -> MVC를 알고 있으면 이따 찾아보면 좋을 듯.
중재자는 string이랑 Action으로 이루어진 Dictionary....
Q. 플레이어<->적 이것보다 플레이어<-중재자->적 이게 더 효율적일까요? A. 상황에 따라 다르다. 결합도를 낮추고 싶다면 쓸 수 있다. 나 같은 경우 결합도를 낮추고 싶으면 썼을 것 같다.
응용 (옵저버+중재자: EventBus)
Q. 1. 사건 발생 2. 옵저버 발견하고 버스한테 전달 3. 버스가 이제 구독한 subscriber한테 알려줌. 4. subscruber가 행동함.
Q. 코드 받아서 테스트할때 Debug.Log를 어디에 찍으면 좋나요. A. UI에 찍는 게 좋다..?
상태(State)
객체의 상태에 따라 동작이 다르게 만들고 그 상태 자체를 객체로 만들어 분리하는 패턴
캐릭터가 걷기 상태일 때는 걷고, 점프 상태일 때는 점프하는 것처럼 행동을 상태 객체로 분리해서 관리하는 구조.
언제 사용할까?
캐릭터 상태, 버튼 상태, 애니메이션 전환 등 상태가 자주 바뀌고 행동이 달라지는 경우
응용 (FSM)
하나의 시점에 하나의 상태만을 갖는 시스템.
입력이나 조건에 따라 상태 간 전이가 발생함.
구성요소
상태(State): 현재 시스템의 동작/상황을 나타냄
전이(Transition): 특정 조건 또는 이벤트가 발생하면 상태를 바꿈
이벤트(Event/Input): 전이 조건이 되는 입력
초기 상태: 시스템이 시작될 때 머무는 상태
상태 패턴과 FSM의 차이
FSM (switch방식): switch-case로 상태 분기 & 간단하고 빠르게 구현 가능 & 상태 많아지면 관리 어려움
상태 패턴: 상태별 클래스를 따로 만듬 & 확장성, 테스트 용이성 상승 & OOP 방식으로 더 구조적 관리
언제 FSM 쓸까?
플레이어/몬스터 행동 제어, 게임 상태 관리, 애니메이션 상태 관리
FSM + 상태패턴: 합치면 좋다!
커맨드(Command)
명령을 객체로 만들어, 실행을 나중에 하거나 저장할 수 있게 하는 구조
왜 사용할까?
행동을 객체로 저장하거나, 나중에 실행하고 싶을 때
실행 취소, 반복 실행 등 기록 가능한 명령이 필요할 때
구성요소
Command 인터페이스: 실행될 명령 정의(Execute(), Undo() 등)
ConcreteCommand: 실제 명령을 구현한 클래스
Invoker: 명령을 실행하는 주체 (보통 UI, 버튼 등)
Receiver: 명령의 실제 기능을 수행하는 대상 객체
전략(Strategy)
알고리즘(행동 방식)을 객체로 만들어서, 필요에 따라 교체할 수 있게 하는 구조
예를 들어 캐릭터가 근접 공격, 원거리 공격을 상황에 따라 바꿀 수 있다면, 공격 방식을 전략 객체로 분리해서 쉽게 교체할 수 있도록 합니다.
언제 사용할까?
어떤 행동이 상황에 따라 자주 달라져야 할때
if/switch로 동작 분기하는 코드가 많을 때
게임에서 공격방식, 이동 방식 등을 유연하게 교체하고 싶을 때
Q. FSM도 상태를 바꿔서 낀다고 했는데 전략도 상태를 교체할 수 있는 거면 차이가 있나요?
A. FSM은 자판기에서 음료수를 뽑는다, 마신다, 이렇게 나누는 거고, 전략은 그래서 그 음료수 맛이 어떻게 나는가?
커맨드와 전략 비교
커맨드는 무엇을 할지(What)를 객체로 만드는 것
전략은 어떻게 할지(How)를 객체로 만드는 것
Q. 디자인 패턴이 이것 말고도 다양할 것 같은데 어떻게 찾을 수 있을까요. A. 요즘은 AI 많이 쓰시는 것 같다.
Q2. 다른 사람들 TIL 열심히 훔쳐보기. A2. ㅋㅋㅋ그것도 좋은 것 같다.
디자인 패턴 개론
패턴이란? 반복되는 문제에 대한 재사용 가능한 해결책
이유: 복잡한 코드 유지보수, 협업 시 핵심 의도를 공유하는 공통 언어 역할
주의: 무조건 사용하는 것은 지양. 상황에 맞게 도입해야 효과적임
싱글턴 (Singleton)
하나의 인스턴스만 존재
장점: 전역 접근 가능, 메모리 절약, 간단한 초기화
단점: 테스트 어려움, 전역 오염, 의존 관계 꼬임
팩토리 (Factory)
객체 생성을 캡슐화
new 대신 Create() 같은 메서드로 생성 위임
장점: 확장 용이, 조건 분기 가능, 결합도 감소
사용 예시: 유닛 타입에 따라 객체를 다르게 생성할 때
옵저버 (Observer)
상태 변화 발생 시 관련 객체들에 알림 전달
장점: UI, 설정, 체력 등 데이터 변화 자동 반영
구현: delegate/event 사용
사용 예시: Unity UI 이벤트 시스템, SceneManager.sceneLoaded 등
중재자 (Mediator)
객체 간 직접 연결을 피하고 중재자(Mediator)를 통해 통신
장점: 결합도 낮춤, 확장성/유지보수성 증가
사용 예시: 플레이어<->적 간 상호작용을 Mediator가 중재
EventBus 패턴은 옵저버와 중재자의 조합
상태 (State)
객체 상태에 따라 다른 행동 수행
상태를 객체로 분리해 관리 -> 클래스 기반 분기 처리 가능
사용 예시: 걷기, 점프, 대기 등 캐릭터 상태
FSM (Finite State Machine)
한 번에 하나의 상태만 유지하며 조건에 따라 전이
장점: 직관적인 흐름 제어
단점: 상태가 많아지면 복잡해짐
상태 패턴과 조합 시 구조적 유연성 증가
사용 예시: 플레이어 행동, 애니메이션 전환, 게임 흐름 제어
커맨드 (Command)
명령을 객체로 캡슐화 -> 실행, 취소, 저장 가능
구성요소: Command 인터페이스, ConcreteCommand, Invoker (UI), Receiver
사용 예시: Undo/Redo 기능, UI 버튼 동작, 입력 기록
전략 (Strategy)
알고리즘을 객체로 분리해서 동적으로 교체 가능
장점: 다양한 동작 방식 유연하게 변경
사용 예시: 근접 공격 <-> 원거리 공격 전략 선택
Command vs Strategy
Command: 무엇(What)을 할까?에 초점 (요청 자체를 캡슐화)
Strategy: 어떻게(How) 할까?에 초점 (행동 방식을 캡슐화)
싱글톤 방어코드
이제까지 무조건 씬에 빈 GameObject를 생성해주고 그 곳에 스크립트를 컴포넌트로 넣어줘야 되는 줄 알았는데,
강의를 보니까 아래와 같이 진행하셨다.
private static CharacterManager _instance;
public static CharacterManager Instance
{
get
{
if(_instance == null)
{
_instance = new GameObject("CharacterManager").AddComponent<CharacterManager>();
}
return _instance;
}
}
그동안은 Title 씬에서 게임을 시작하게 되면 DontDestroyOnLoad 상태니까 씬 전환이 되어도 문제가 없었지만,
Game 씬이나, 암튼 해당 오브젝트 및 싱글톤 객체가 준비되지 않은 씬에서 테스트를 하려고 하면 늘 문제가 생겼었다.
이러한 방어 코드가 있다면 일일히 씬마다 GameObject를 만들어주지 않아도 되지 않을까 싶다.
느낀점
기존 팀원들과 헤어지고.. 붙잡고 있던 프로젝트도 이제 놓아줘야하고... 뭔가 허탈한 느낌이 든다.
그치만! 역시나!
개인 과제 주차는 지급 받은 강의 학습도 해야하고.
어제 발표하느라 밀린 분반 수업도 들어야하고, OOP 특강도 들어야해서 기운 빠져있을 시간이 없다!
(오늘 TIL만 봐도 분량이 어마어마하다!)
게다가 새로 배정 받은 팀은 챌린지 반이 4명이나 있어서, 폐를 끼치지 않으려면 진짜 열심히 해야할 것 같다.
내일 학습 할 것은 무엇인지
강의를 전부 끝낼 수 있으면 좋을 것 같다.
조금 질릴 때쯤, 과제 instruction을 체크하고 구상을 하면 좋을 것 같다.
'부트캠프 > 본캠프' 카테고리의 다른 글
| [내일배움캠프_2025AUG08] 유니티 숙련 챕터 마무리 (0) | 2025.08.08 |
|---|---|
| [내일배움캠프_2025AUG07] 유니티 숙련 챕터 (0) | 2025.08.07 |
| [내일배움캠프_2025AUG05] 팀 프로젝트 발표! (0) | 2025.08.05 |
| [내일배움캠프_2025AUG04] 팀 프로젝트 5일차, 발표 준비 (0) | 2025.08.04 |
| [내일배움캠프_2025AUG1]팀 프로젝트 4일차, 커스터마이징 (0) | 2025.08.01 |