1
Q
근데 자바에서는 체력바가 있고, 적에게 피격되면 체력바 줄어들게 하려면, 복잡한 계산이 있어야햇는데
유니티에서는(c#)
.value가 내장 메소드인가
슬라이더(게임오브젝트)의?
A
ㅇㅇ
Q
그러니깐 현재의 비율만 .value에 주면 되는건가
그러면 그 비율대로, 해당 슬라이더의 너비를 조정해주는건가
A
현재 상태의 비율(0.0 ~ 1.0)을 계산해서 slider.value에 넣어주기만 하면 됩니다.
예를 들어,
- 체력바: healthBar.value = (float)현재체력 / 최대체력;
- 경험치바: expBar.value = (float)현재경험치 / 최대경험치;
이렇게 비율 값만 넘겨주면, 유니티의 Slider 컴포넌트가 알아서 채워지는 정도를 시각적으로 업데이트해줍니다. 복잡한 그래픽 처리나 계산은 유니티가 내부적으로 담당합니다.
2
근데 여기는 틱함수가 없나?
아 틱함수는 필요없고
사건이 발생하면 이벤트로 뭔가 트리거를 처리하는건가
아니면 틱은 게임 매니저에서 ??
3
UIManager.Instance.UpdateLevelText(level);
UIManager.Instance.UpdateHealthBar(currentHealth, maxHealth);
Q
이 코드 뭔소리지,'
A
핵심은 static(정적) 키워드입니다.
- static이 붙은 변수나 메소드는 객체를 생성(new UIManager())하지 않아도 클래스 이름에 점(.)을 찍어 바로 접근할 수 있습니다.
- 이 Instance 변수는 '싱글톤 패턴(Singleton Pattern)' 이라는 디자인 패턴을 위해 사용되었습니다. 싱글톤 패턴은 "이 클래스의 객체는 게임 전체에 딱 하나만 존재하도록 보장하고, 누구나 쉽게 그 하나에 접근할 수 있게 하자!" 라는 약속입니다.
Q
아니 근데 static붙은건 객체 생성하지않고도 호출할수있다는건 알겟는데
중간에 .instance 는 왜 있는거지
A
| 구분 | static 메소드 (개념적 기능) | static 필드/변수를 통한 객체 접근 (지금 방식) |
| 정의 | public static void MyMethod() {} | public static MyClass Instance;<br>public void MyMethod() {} |
| 호출 방법 | MyClass.MyMethod() | MyClass.Instance.MyMethod() |
| 핵심 | 객체 없이, 클래스 이름만으로 실행 | 클래스 이름으로 유일한 객체를 찾아온 뒤, 그 객체의 기능을 실행 |
| 장점/단점 | 객체의 멤버 변수(예: healthBar)를 쓸 수 없음 | 객체의 모든 멤버 변수와 기능을 사용할 수 있음 |
Q
그니깐 정적인 메소드가 아님에도?
근데 ㅇ지금 updateExp는 정적이 아닌데
static이 없어
A
1단계: UIManager.Instance (여기까지가 static 영역입니다)
- UIManager: 클래스의 이름입니다.
- . : "클래스 내부에 있는 무언가에 접근하겠다."
- Instance: UIManager 클래스 내부에 public static UIManager Instance; 라고 선언된 정적(static) 변수입니다.
static 변수는 객체 생성 없이 클래스이름.변수이름으로 접근할 수 있습니다. 자바의 public static String schoolName; 을 School.schoolName 으로 접근하는 것과 똑같습니다.
여기까지 실행했을 때의 결과물은 무엇일까요?
바로 Awake() 함수에서 Instance = this; 코드를 통해 저장해 두었던 실제 UIManager 객체(인스턴스) 그 자체입니다.
즉, UIManager.Instance 라는 코드는 "유일한 UIManager 객체를 찾아내라" 라는 명령이고, 그 결과로 실제 객체가 손에 쥐어집니다.
2단계: [찾아낸 객체].UpdateExpBar(...) (이제부터는 non-static 영역입니다)
1단계에서 UIManager 객체를 성공적으로 찾아냈습니다. 이제 그 뒤에 붙는 코드를 보겠습니다.
- .: "방금 찾아낸 그 객체 내부에 있는 무언가에 접근하겠다."
- UpdateExpBar(...): 그 객체가 가지고 있는 일반(non-static) 메소드입니다.
이것은 자바에서 Car myCar = new Car(); 를 한 뒤 myCar.drive(); 를 하는 것과 문법적으로, 개념적으로 100% 동일합니다. myCar 라는 객체를 통해 그 객체의 기능인 drive()를 호출하는 것입니다.
최종 정리 (가장 중요한 부분)
| 코드 부분 | 이것은 무엇인가? | static 인가? | 결과물 | 비유 |
| UIManager.Instance | 정적(static) 변수 | 예 (Yes) | 실제 UIManager 객체 | <방송실> 안내판에서 실제 방송부원 찾기 |
| .UpdateExpBar() | 일반(non-static) 메소드 | 아니오 (No) | 메소드 실행 | 그 방송부원에게 "방송해줘!"라고 요청하기 |
Q
public static UIManager Instance { get; private set; }
뭔 의미지
이게 그렇다면 핵심같은데
A
아,이건 마치, 자바 실습시간에 외계인 언어
스태틱으로 선언하면, 객체 여러번 생성해도, count되는 숫자 지속되는것과 같나
가령
static int total=0으로 선언하면
"모든 스크립트가 자유롭게 읽어갈 수 있지만(public get), 오직 UIManager 클래스 자기 자신만이 값을 쓸 수 있는(private set) UIManager 타입의 정적(static) 보관함, Instance를 만든다."
이것이 왜 싱글톤 패턴의 핵심인가?
- 안전성 (Safety): PlayerStats나 다른 어떤 스크립트가 실수로 UIManager.Instance = null; 같은 코드를 실행해서 싱글톤을 망가뜨리는 것을 원천적으로 차단합니다. private set 때문에 컴파일 에러가 발생합니다.
비교: static 키워드의 동일한 원리
| 구분 | 자바의 카운터 예시 (static int total) | C#의 싱글톤 예시 (static UIManager Instance) |
| 선언 | class Alien { static int total = 0; } | class UIManager { public static UIManager Instance; } |
| 핵심 원리 | total이라는 int 변수는 Alien 클래스에 딱 하나만 존재하는 공용 게시판 숫자입니다. | Instance라는 UIManager 변수는 UIManager 클래스에 딱 하나만 존재하는 공용 게시판 주소록입니다. |
| 객체 생성 시 | new Alien() 할 때마다 이 공용 숫자를 1씩 증가시킵니다. (total++) | UIManager 객체가 Awake() 될 때, 이 공용 주소록에 자기 자신의 주소(this)를 딱 한 번 기록합니다. (Instance = this;) |
| 메모리 상태 | Alien 객체를 100개 만들어도, total이라는 메모리 공간은 단 하나이며, 그 값은 100이 됩니다. | UIManager 객체는 게임 내에 단 하나만 존재하며, Instance라는 메모리 공간도 단 하나이며, 그 안에는 유일한 UIManager 객체의 주소가 담겨 있습니다. |
| 결과 | 모든 Alien 객체들이 하나의 total 값을 공유합니다. | 게임 내 모든 스크립트들이 하나의 Instance 값을 공유합니다. |
Q
또한 레벨업할때, 경험치바도 업뎃해줘야하는거 아닌가
A
ㅇㅇ
UIManager.Instance.UpdateExpBar(currentExperience, experienceToNextLevel);
4

시작은 체력바의 Rect Transform 설정을 경험치바에도 적용시키는것이엇음
그러다가, 체력바를 채우기 위해서, 가득채움을 건들인것이 기억남
젤 하단, 젤 오른쪽에 있는걸로 하니깐, 해결됨
이 9가지 설정이 왜 중요한거지

"젤 하단, 젤 오른쪽에 있는 것", 즉 가로와 세로 모두 'Stretch' 로 설정하는 앵커 프리셋이 왜 그렇게 중요한지, 그리고 왜 이 설정으로 문제가 해결되었는지 쉽게 설명해 드리겠습니다.
앵커(Anchor)는 "부모-자식 관계의 규칙"입니다
유니티의 UI 시스템(UGUI)에서 모든 UI 요소(Image, Text, Slider 등)는 부모(Parent)와 자식(Child) 관계를 가집니다. 예를 들어 Fill은 Slider의 자식입니다.
앵커는 "부모의 크기가 변할 때, 자식의 위치와 크기를 어떻게 조절할 것인가?"에 대한 규칙을 정하는 4개의 압정(pin)이라고 생각하시면 됩니다.
이 규칙이 중요한 이유는 다양한 해상도(화면 크기)의 기기에서 UI가 깨지지 않고 일관되게 보이게 하기 위함입니다.
앵커 프리셋의 핵심 개념: 점(Point) vs 선(Stretch)
앵커 프리셋은 크게 두 가지 방식으로 작동합니다.
1. 앵커가 한 점에 모여 있을 때 (Point Anchor)
- 예시: 좌측 상단, 중앙, 우측 하단 등
- 역할: "부모의 특정 지점(앵커 위치)으로부터 고정된 거리를 유지해!"
- 특징: 부모의 크기가 변해도 자식의 크기는 변하지 않습니다. 위치만 앵커에 따라 유지됩니다.
- 예시: 게임 화면의 미니맵이나 설정 버튼. 화면이 커지든 작아지든 항상 구석에 같은 크기로 있어야 합니다.
2. 앵커가 양쪽으로 벌어져 있을 때 (Stretch Anchor)
- 예시: 가로로 Stretch, 세로로 Stretch, 그리고 님이 선택한 가로/세로 모두 Stretch
- 역할: "부모의 특정 가장자리(앵커 위치)로부터 고정된 여백을 유지하면서, 그 사이를 꽉 채워!"
- 특징: 부모의 크기가 변하면 자식의 크기도 함께 늘어나거나 줄어듭니다.
- 예시: 팝업창의 배경 이미지. 팝업창 내용이 많아져서 커지면 배경도 함께 커져야 합니다.
그래서 왜 Stretch-Stretch 설정이 문제를 해결했는가?
이것이 슬라이더 작동 방식의 핵심입니다.
Slider 컴포넌트는 value 값이 바뀔 때, 자식인 Fill 오브젝트의 Rect Transform을 직접 제어하여 크기를 조절합니다.
- 잘못된 설정 (아마도 '가운데 정렬' 같은 Point Anchor):
- Fill의 앵커들이 한 점(예: 중앙)에 모여 있었습니다.
- 이 규칙에 따르면 Fill은 자신의 크기를 바꾸지 않고, 부모의 중앙에 위치하려고만 합니다.
- 슬라이더가 value를 0으로 만들고 "너비(width)를 0으로 줄여!" 라고 명령해도, Fill은 "내 규칙은 크기 고정이라서 못 바꿔!" 라고 버티는 상황이 됩니다.
- 결국 Fill은 인스펙터에 설정된 처음 크기(꽉 찬 크기)를 계속 유지하게 됩니다.
- 올바른 설정 (님이 찾은 Stretch-Stretch Anchor):
- Fill의 앵커 4개가 각각 부모(슬라이더)의 네 귀퉁이에 달라붙었습니다.
- 이 규칙은 Fill의 너비와 높이가 부모를 따라가게 만듭니다.
- 이제 슬라이더가 value에 따라 Fill의 오른쪽 경계선을 조작합니다.
- value = 0 일 때: 슬라이더가 Fill의 오른쪽 경계선을 왼쪽 경계선까지 끌어당깁니다. Fill의 너비는 0이 되어 보이지 않게 됩니다.
- value = 0.5 일 때: 슬라이더가 Fill의 오른쪽 경계선을 부모의 절반 위치까지 밀어냅니다.
- value = 1 일 때: 슬라이더가 Fill의 오른쪽 경계선을 부모의 오른쪽 끝까지 밀어냅니다. Fill이 꽉 차게 됩니다.
결론
| 설정 | 의미 | 슬라이더 Fill에 적용 시 |
| Point Anchor (앵커 한 점) | "내 크기는 고정할래!" | 슬라이더의 값 조절 명령을 무시하고 원래 크기를 유지함 (문제의 원인) |
| Stretch Anchor (앵커 분리) | "부모 크기에 맞춰 내 크기도 바꿀게!" | 슬라이더의 값 조절 명령에 따라 크기가 유연하게 변함 (정상 작동) |
Slider의 Fill이 제대로 작동하게 하려면, Slider가 Fill의 크기를 마음대로 제어할 수 있도록 Fill이 Stretch Anchor 설정을 가져야만 합니다.
5
근데 경험치 구슬을 먹어도, 경험치 바가 안차는 문제

경험치 구슬 prefab에서 스크립트 연결을 안해주엇네
6

저장해야하는 파일은 동그라미가 뜨네
7
Q
근데 몬스터마다 주는 경험치를 다르게 하고 싶은데(
현재 적 스크립트에 experirncamount가 있긴하지만, 이건 사용안되고있는거 아닌가)
현재는 경험치 구슬이 하나만 있단 말이지 ?
그냥 경험치를 얼마나 주는지를, 몬스터 prefab에서 관리하게 하면 안되나
경험치 구슬은 그 죽은 몬스터.getExperienceAmount호출해서
근데 이렇게가 되나
몬스터가 죽으면, 다시 객체 풀로 들어갈텐데
A
몬스터가 죽거나 비활성화된 후에는 GetComponent 등으로 그 몬스터의 정보를 가져올 수 없습니다. 따라서 정보는 몬스터가 죽는 바로 그 순간에 전달되어야 합니다.
// 총알과 부딪혔을 때
if (other.CompareTag("PlayerShot"))
{
// ... 생략 ...
gameObject.SetActive(false); // 몬스터가 사라짐 (오브젝트 풀로 돌아감)
// --- ★★★ 여기가 바로 마법이 일어나는 곳입니다 ★★★ ---
if (experienceOrbPrefab != null)
{
// 1. 구슬 생성: 몬스터는 죽기 직전, 자신의 위치에 '경험치 구슬' 프리팹을 생성합니다.
GameObject orbObject = Instantiate(experienceOrbPrefab, transform.position, Quaternion.identity);
// 2. 구슬 정보 접근: 생성된 구슬 오브젝트에서 ExperienceOrb 스크립트를 찾아옵니다.
ExperienceOrb orbScript = orbObject.GetComponent<ExperienceOrb>();
// 3. 정보 복사 (가장 중요!): 만약 스크립트를 찾았다면,
// "내(this) 경험치 값(experienceValue)"을 "구슬의 경험치 값(orbScript.experienceValue)"에 복사해준다.
if (orbScript != null)
{
orbScript.experienceValue = this.experienceValue;
}
}
}
Q
아니 근데 현재 몹을 잡으면 경험치를 1만 주고 있는것같은데
출력은 다음과 같음
1 경험치 획득!
UnityEngine.Debug:Log (object)
PlayerStats:GainExperience (int) (at Assets/Scripts/PlayerStats.cs:46)
PlayerStats:OnTriggerEnter2D (UnityEngine.Collider2D) (at Assets/Scripts/PlayerStats.cs:91)
만약 복사가 제대로 되면 10이 출력되어야하는거 아닌가
그냥 경험치 구슬의 experiencevalue인 1이 출력되는거 같은데
A

여기의 수치를 바꾸니깐, 해결됫음
Q
이 스크립트의 경험치 변수는 왜 있는거지?
그냥 임의로 1로 잡은건가?
값 복사헤ㅐ오ㅓㅏ야하니깐?
A
oo
8
Q
근데, 하나의 레벨에서 출연하는 몬스터를 여러개로 설정하고싶을때
그냥 몬스터 프리팹을 늘리기만 하면 되나
자바에서는 각 레벨에 해당하는 몬스터들을 리스트에 담아서 처리햇엇는데
아, 유니티에서도 prefab탭안에 하나의 폴더를 더 만들어서 관리할수있나
또한
근데 현재 객체풀 구조도 같이 사용하고 있는데
즉, 지금은 적이 한 종류라서, 미리 적을 50마리 만들어두면 되지만
만약에 적의 종류가 많아지면, 미리만들어두어야하는 적들이 너무 많아지는데
자바에서는 그래서, 객체풀로, 적들의 갯수를 미리 정해두고
적이 생성될때 그 위에 이미지만 바꾸는 방식으로 여러종류의 적들을 구현햇는데
공격력과 체력을 다르게 하기 위해서는
그 이미지가 사자이미지인가(예를들어) yes이면 공격력10으로
근데 유니티는 그런 트릭보단, 진짜
적들의 prefab을 활용하는데 어케[ 해야하지
A
- 가장 추천하는 방법: 몬스터 종류별로 별도의 객체 풀 만들기 (다중 풀링)
- 차선책: 자바 방식과 유사하게, 데이터만 바꿔 끼우는 방식 (ScriptableObject 활용)
9
근데 만약에 특수한 공격이나,
원거리 공격을 시전하는 적이 있다면, 이건 enemy스크립트가 아니라,
그 몹의 이름으로 된 새로운 스크립트를 짠 다음,
해당 몹의 prefab에 연결하는게 좋겟지?
10


근데 현재 이미 gameManager 게임 오브젝트가 있는데(예전 방식에서 gameManager스크립트 없엇는데도 오브젝트는 있엇네)
여기에 objectpooler 스크립트가 부착되어있는데
새로운 객체풀 게임 오브젝트를 만들어서, 여기다가 스크립트 추가하는게 낫나
왜 별도로 분리하는 것이 더 좋은가? (SRP: 단일 책임 원칙)
소프트웨어 설계에는 단일 책임 원칙(Single Responsibility Principle) 이라는 매우 중요한 원칙이 있습니다. "하나의 클래스(또는 게임 오브젝트)는 하나의 기능(책임)만 가져야 한다"는 뜻입니다.
- GameManager의 책임: 게임의 규칙, 레벨, 시간, 승패 조건 등 게임 플레이의 흐름을 관리하는 것입니다.
- ObjectPooler의 책임: 총알, 몬스터, 이펙트 등 게임 오브젝트를 미리 생성하고 재활용하는 메모리 관리 시스템입니다.
11

근데 적들을 추가해야하는데 리스트 형태로 되어있는것같지않은데
수정해야하는 부분이 있나?
하이아키 창에서?
ㅇㅇ
+ 버튼을 누르고 정보를 입력한 후:```
Pools ▼ Size [ 3 ]
├─ Element 0 (Enemy)
├─ Element 1 (PlayerShot)
└─ Element 2 (Bat) <-- 새로 추가된 부분
Q
근데 이렇게 하면, 몬스터들의 종류와 난이도가 다양해졌을때 관리하기가 어렵잖아
그니깐, 난이도 별로 또 묶어주면 안되나
즉, 몬스터 별로 객체 풀 만들거긴한데
난이도별로 일종의 리스트 만들어서 관리 못하나
A
그건 gameManager에서 관리하고있네


12
새로운 연결 및 설정 방법 (유니티 에디터)
코드를 모두 수정했다면, 이제 유니티 에디터에서 이들을 연결하여 생명을 불어넣어야 합니다.
1. 새로운 몬스터 프리팹 준비
- 프로젝트 창에 Bat.prefab 같은 새로운 몬스터 프리팹을 만듭니다.
- 이 새로운 프리팹에도 Enemy.cs 스크립트를 붙이고, Experience Value, Damage 등을 원하는 값으로 설정합니다.
2. ObjectPooler 오브젝트 설정 (가장 중요!)
- 씬(Hierarchy)에서 ObjectPooler 게임 오브젝트를 선택합니다.
- 인스펙터 창을 보면 Pools 라는 리스트가 있습니다.
- + 버튼을 눌러 몬스터 종류별로 풀을 추가해줍니다.
- Element 0:
- Tag: "Enemy" (기존 프리팹 이름. 만약 Slime으로 바꿨다면 Slime으로)
- Prefab: 기존 몬스터 프리팹을 드래그앤드롭
- Size: 50
- Element 1 (새로 추가):
- Tag: "Bat" (새로운 프리팹의 이름과 정확히 일치해야 함)
- Prefab: 새로 만든 Bat.prefab을 드래그앤드롭
- Size: 30 (박쥐는 좀 더 약하니 30마리만)
- Element 0:
3. GameManager 오브젝트 생성 및 설정
- 씬에 빈 게임 오브젝트를 하나 만들고 이름을 GameManager 로 지정합니다.
- 위에서 작성한 GameManager.cs 스크립트를 이 오브젝트에 붙여줍니다.
- 인스펙터 창에 보이는 Level Progression 리스트를 설정합니다. 이것이 바로 레벨 디자인의 핵심입니다.
- 리스트의 Size를 2로 설정하여 두 개의 레벨을 만듭니다.
- Element 0 (레벨 1):
- Level Name: Level 1
- Time To Reach This Level: 0 (게임 시작부터 이 레벨)
- Spawnable Monsters 리스트의 Size를 1로 하고, 기존 몬스터 프리팹을 드래그앤드롭.
- Element 1 (레벨 2):
- Level Name: Level 2
- Time To Reach This Level: 30 (게임 시작 후 30초가 되면 이 레벨로 진입)
- Spawnable Monsters 리스트의 Size를 2로 하고, 기존 몬스터 프리팹과 Bat.prefab 을 각각 드래그앤드롭.
이제 게임을 실행하면, GameManager가 시간을 재다가 30초가 되면 currentLevel을 1로 올립니다. EnemySpawner는 매번 GameManager에게 "현재 스폰할 몬스터 목록 주세요!" 라고 요청하여, 30초 이후부터는 Bat도 함께 스폰하기 시작할 것입니다.
13

14
Q
또한 경험치 구슬은 이 객체풀 스크립트에 연결이 안된거 같은데ㅐ 연결해줘야하는거 아닌가
현재 적 스크립트를 보면, 그떄마다 경험치 구슬instatiate하고 있는데 그럼 이건 객체 풀이 아니라 그떄마다 객체 생성하고 있다는 뜻 아닌가
if 적이 충돌
if 그 대상의 tag==playerSot{경험치 구슬 생성}
A

15
Q
게임시작했지만, 몹이 안나옴
Pool with tag otapos doesn't exist.
UnityEngine.Debug:LogWarning (object)
ObjectPooler:SpawnFromPool (string,UnityEngine.Vector3,UnityEngine.Quaternion) (at Assets/Scripts/ObjectPooler.cs:64)
EnemySpawner/<SpawnEnemyRoutine>d__4:MoveNext () (at Assets/Scripts/EnemySpawner.cs:46)
UnityEngine.MonoBehaviour:StartCoroutine (System.Collections.IEnumerator)
EnemySpawner:Start () (at Assets/Scripts/EnemySpawner.cs:16)
아 맞다 오류있으면 게임 시작안됫던거같기도


난 문어의 tag도 enemy로 햇는데 뭐가 문제인거지
난 태그를 문어로 한적이 없어
A
// EnemySpawner.cs 의 일부
// ...
// 1. GameManager로부터 otapos.prefab을 가져옵니다.
GameObject monsterPrefab = monstersToSpawn[randomIndex];
// 2. ObjectPooler에게 "monsterPrefab의 파일 이름"을 태그로 사용하여 스폰을 요청합니다.
ObjectPooler.Instance.SpawnFromPool(monsterPrefab.name, spawnPosition, Quaternion.identity);
코드가 monsterPrefab.name을 태그로 사용하고 있습니다.
otapos 프리팹의 **파일 이름(name)**은 무엇인가요? 바로 "otapos" 입니다.
해결 방법 (코드 수정 없음)
이 문제를 해결하기 위해 코드를 수정할 필요는 없습니다. @ObjectPooler의 설정을 업데이트해주기만 하면 됩니다.
1. @ObjectPooler 게임 오브젝트 선택
- Hierarchy(계층) 창에서 @ObjectPooler를 선택합니다.
2. 새로운 몬스터를 위한 풀(선반) 추가
- 인스펙터 창의 Pools 리스트 아래에 있는 + 버튼을 클릭하여 새로운 칸을 추가합니다. Size가 3이 될 것입니다.
- 새로 생긴 Element 2 칸에 otapos 몬스터의 정보를 입력합니다.
* Tag: 여기에 반드시 otapos 라고 입력해야 합니다. (프리팹 파일 이름과 똑같이!)
* Prefab: 프로젝트 창에 있는 otapos.prefab 파일을 마우스로 끌어다 놓습니다.
* Size: 20 처럼 적당한 숫자를 입력합니다.
3. blueSnail 몬스터도 똑같이 추가
- GameManager 스크린샷을 보니 blueSnail이라는 몬스터도 사용하고 있습니다. 이 몬스터도 똑같은 문제를 일으킬 것입니다.
- + 버튼을 한 번 더 눌러 Size를 4로 만듭니다.
- 새로 생긴 Element 3 칸에 blueSnail의 정보를 입력합니다.
* Tag: blueSnail 이라고 입력합니다. (프리팹 이름과 똑같이!)
* Prefab: blueSnail.prefab 파일을 끌어다 놓습니다.
* Size: 30 처럼 숫자를 입력합니다.
수정 후 인스펙터 예상 모습:
Generated code
Pools ▼ Size [ 4 ]
├─ Element 0 (Enemy)
├─ Element 1 (PlayerShot)
├─ Element 2 (otapos) <-- 새로 추가된 부분
│ ├─ Tag: "otapos"
│ ├─ Prefab: (otapos 프리팹)
│ └─ Size: 20
│
└─ Element 3 (blueSnail) <-- 새로 추가된 부분
├─ Tag: "blueSnail"
├─ Prefab: (blueSnail 프리팹)
└─ Size: 30
이 설정을 완료하면, EnemySpawner가 "otapos 선반에서 꺼내줘!" 라고 요청했을 때 ObjectPooler가 정상적으로 otapos 몬스터를 꺼내줄 수 있게 되어 에러가 사라질 것입니다.
Q
근데 tag를 문어로 하면 충돌처리할때, 안되잖아, 현재 충돌처리는 Enemy가 tage인 얘들하고 처리하고있는데
그냥 코드를 수정해서 이름으로부터 가져오는게 아니라 tag Enemy 인게 중요한거 아닌가
A
문제 해결: 코드를 수정할 필요가 없는 이유
EnemySpawner가 monsterPrefab.name ("otapos")을 사용하는 것은 아주 올바른 행동입니다. 스포너의 역할은 "어떤 '종류'의 몬스터를 스폰할지" 결정해서 ObjectPooler에게 알려주는 것이기 때문입니다. ObjectPooler는 이 고유 이름표("otapos")를 보고 정확한 선반에서 몬스터를 꺼내옵니다.
이제 남은 일은, 그 꺼내온 otapos 몬스터가 자기 자신을 "Enemy" 카테고리에 속한다고 알고 있게 만들어주면 됩니다.
최종 해결 방법 (코드 수정 없이 설정만 변경)
모든 몬스터 프리팹들이 두 개의 ID를 모두 가지도록 설정하면 됩니다.
1. otapos.prefab 설정하기
- 프로젝트 창에서 otapos.prefab 파일을 선택합니다.
- 인스펙터 창을 봅니다.
- 설정 1 (스폰 구별용): 이 프리팹의 **파일 이름(name)**은 그대로 "otapos" 입니다. (이미 되어 있음)
- 설정 2 (충돌 처리용): 인스펙터 창 맨 위에 있는 Tag 드롭다운 메뉴를 클릭하고, 여기서 Enemy 를 선택해줍니다.
2. blueSnail.prefab 설정하기
- 프로젝트 창에서 blueSnail.prefab 파일을 선택합니다.
- 인스펙터 창을 봅니다.
- 설정 1 (스폰 구별용): 이 프리팹의 **파일 이름(name)**은 "blueSnail" 입니다. (이미 되어 있음)
- 설정 2 (충돌 처리용): 맨 위 Tag 드롭다운 메뉴에서 똑같이 Enemy 를 선택해줍니다.
3. @ObjectPooler 설정 유지하기
- @ObjectPooler의 인스펙터 창 설정은 이전 답변처럼 그대로 유지합니다. 각 풀의 Tag는 "otapos", "blueSnail" 등 프리팹의 고유한 이름이어야 합니다.
Q
근데 몬스터 종류를 추가할때마다,
풀에도 항상 추가해줘야하는 번거로움이 있네
어쩔수없나
A
결론부터 말씀드리면, 그 번거로움은 어쩔 수 없는 것이 아니며, 충분히 자동화할 수 있습니다.
현재 워크플로우의 문제점은 다음과 같습니다.
현재 워크플로우 (번거로움)
- 새로운 몬스터 프리팹(Dragon.prefab)을 만든다.
- GameManager의 Level Progression 리스트에 Dragon.prefab을 추가한다.
- **@ObjectPooler**의 Pools 리스트에도 +를 눌러 Dragon 풀을 수동으로 추가하고 태그와 프리팹을 연결한다. (실수하기 쉽고 귀찮은 부분)
이것을 아래와 같이 바꿀 수 있습니다.
개선된 워크플로우 (자동화)
- 새로운 몬스터 프리팹(Dragon.prefab)을 만든다.
- GameManager의 Level Progression 리스트에 Dragon.prefab을 추가한다.
- 끝. (ObjectPooler가 게임 시작 시 GameManager를 보고 알아서 필요한 모든 풀을 자동으로 생성함)
16
근데 겜 시작하자마자, 문어 나오는데 왜지


자동화된 워크 플로우는 나중에 개선해보자
'개발 > 유니티' 카테고리의 다른 글
| [유니티] 레벨업할때 한글 선택지 오류남(미완) (8) | 2025.08.05 |
|---|---|
| [유니티] 자바에서 만든 프로젝트 유니티로 옮기기(3) (17) | 2025.07.25 |
| [유니티] 자바에서 만든 프로젝트 유니티로 옮기기(2) (14) | 2025.07.24 |
| [유니티] 자바에서 만든 프로젝트 유니티로 옮기기(1) (13) | 2025.07.22 |
| [유니티] 옵저버 (Observer) 패턴 (13) | 2025.07.20 |