본문 바로가기
언리얼_엔진_게임개발_공부/프로젝트

[C++/텍스트RPG 프로젝트] 벡터를 참조로 반환하는 함수...를 호출해 변수에 저장...하는 과정속 나의 불찰...그리고 배움...

by jaboy 2025. 1. 15.

나는 바보다! 그래서 즐겁다! 왜? 배울 게 많아서!

간단한(?) 게임 로직을 구현해보는 팀 프로젝트를 진행 중이다.

아이템, 인벤토리, 상점 기능의 구현을 맡았고, 열정 넘치게 아이템 강화 시스템을 데코레이터 패턴을 사용해 추가해보기로 하였다. 내일배움캠프 언리얼 과정에서 배운 내용을 그대로 적용하면 되었기에 해당 디자인 패턴을 사용하는 것이나 기능을 구현하는 것 자체는 전혀 어렵지 않았다. 그러나...

 

버그가 발생하는 시나리오

요약해서 말하자면...

1. 플레이어가 상점에 방문

2. (상점) 강화 메뉴 선택

 

[반복 1]

3. (상점) 인벤토리 접근, 아이템 목록 출력

4. (상점) 플레이어가 선택한 장비 아이템 강화

5. (상점) 강화 데코레이터로 장비를 감싸서 강화 구현

6. (상점) 인벤토리의 착용 함수로 넘김

7. (인벤토리) 강화된 아이템 착용 및 효과 적용

 

[반복 2]

3. (상점) 인벤토리 접근, 아이템 목록 출력 --> 아이템이 강화가 안 되어 있음!!!!

 

첫 반복문 진입 및 강화/착용까지는 잘 되었으나,

반복문을 다시 돌 때 인벤토리에 접근하고 출력하면 강화 적용이 안되어 있는 것을 확인하였다.

 

추측할 수 있는 점

아이템 객체, 아이템을 가리키는 포인터, 인벤토리에 포인터를 인자로 넘기는 과정 등

강화가 구현되고 인자로 전달하는 것에는 문제가 없다.

첫 iteration 에서 강화가 잘 되고 인벤토리의 착용 함수에 인자로 잘 넘어갔다는 것은,

오고가는 포인터가 강화된 객체를 가리키고 있다는 것입니다.

그렇다면... 뭔가 로컬 변수로 가져온 인벤토리가 실제로 객체가 저장된 인벤토리가 아닌 것 같단 말이죠?

강화하는 함수의 시그니처
void Shop::UpgradeEquipment(Equipment* equipment, int index)

상위 함수에서 아래와 같이 호출되었다.
인벤토리가 vector<Item*> 이기 때문에 Item 을 상속하는 Equipment 포인터로 dynamic_cast
UpgradeEquipment(dynamic_cast<Equipment*>(_Inventory[i]), i)

 

 

아래 _Inventory 로컬 벡터 내부의 아이템에는 강화가 적용되고, 위 PInventory 싱글톤 내부 인벤토리 벡터 안 아이템에는 강화가 반영되지 않았다.

 

디버거에서 위쪽 PInventory 는 싱글톤으로 구현된 Inventory 인스턴스이다.

// 싱글톤 인스턴스 가져오기
Inventory* PInventory = Inventory::GetInstance();

 

PInventory 내부의 _Inventory 는 Inventory 클래스의 프라이빗 멤버 변수인 벡터로 실제 아이템이 저장되어 있다.

내가 목적한 것은 해당 벡터 내의 아이템에 강화 데코레이터를 추가?하는 것.

 

아래쪽에 있는 _Inventory 로컬 변수에 PInventory 의 GetInventory() 함수를 호출하여 반환된 벡터 (실제 아이템 객체가 생성되어 저장되는 컨테이너) 를 대입하고자 했다.

// Inventory 클래스의 public 함수 - private 멤버인 _Inventory를 반환하는데... 얼마나 멍청한 일인가?
vector<Item*>& Inventory::GetInventory()
{
	return _Inventory;
}

// 아래와 같이 호출해 로컬 변수에 저장하였다.
vector<Item*> _Inventory = PInventory->GetInventory();

//...

// 강화를 실행하는 데코레이터 생성자 호출
_Inventory[index] = new SwordUpgrade(equipment);

//...

// 강화한 아이템을 착용시키는 함수 호출
PInventory->AutoEquip(_Inventory[index]);


여기서 문제!! 과연 제가 잘못한 부분은 어디일까요?!?!?

(이렇게 모아서 보니까 바로 보이네요 ... 한참 고생했는데....)

 

정답은....

.,....

.,.,.

,.

,.ㅠㅠ,..,,..

 

바로 _Inventory 로컬 변수를 참조로 선언하지 않은 것입니다 ㅠㅠ

함수는 벡터의 참조 vector<Item*>& 를 반환하는데, vector<Item*> 인 변수에 대입했던 것입니다 ㅠㅠ

아무리 인벤토리에 강화된 아이템을 대입시키려고 해도 실제 벡터는 멀리서 지켜만 볼뿐......

 

그런데 여기서 내가 멍청한 점 하나 더!!

인벤토리 싱글톤 내부의 private 벡터를 외부에서 직접적으로 수정할 거면...

그러니까 private 멤버 변수인 벡터를 참조로 반환하는 public 함수를 만들고

외부에서 이 참조를 받아서 벡터의 내부 객체를 대입 연산자 등을 이용해 직접적으로 수정할 거면....

이 벡터가 private 변수인 의미가 없어지죠...

하하하.

 

결정할 것 : 인벤토리 클래스 내부 벡터를 private 으로 놔둘 것인지?

1. private 멤버 변수로 놔둔다면

내부 벡터의 객체를 교체시키는 public 함수를 만든다.

이렇게 하면 벡터에 가하는 수정을 '교체'로 제한할 수 있게 된다. (삭제 등 다른 행동은 불가)

 

2. public 멤버 변수로 교체한다면

GetInventory 함수는 없어도 되고, _Inventory 에 직접 접근하여 수정을 가한다.

 

아무래도 1번 방향이 안전할 것 같네요. 다만 메모리 관리에 주의해야 하겠지요.

 

앞으로는 클래스를 설계할 때 프라이빗 변수에 취해야 하는 행동을 미리 고려하고,

그에 맞게 접근 제한을 계획하고 함수를 만들 것!