게임 회사로 옮기고 처음 맡은 것이 Cocos2d-x 클라이언트의 아이템과 인벤토리였다. 아이템 목록이니 std::vector 면 될 줄 알았다.
Table of contents
Open Table of contents
처음에는 그냥 배열이었다
아이템을 얻으면 push_back 하고 쓰면 erase 하고 화면에는 순서대로 그렸다. 며칠은 잘 굴러갔다.
여기서 이미 한 번 걸렸다. Item* 을 담아 두고 erase 만 하면 delete 가 안 된다. Cocos2d-x 의 CCObject 는 참조 수를 세니 CCArray 에 넣으면 addObject 가 retain 하고 빼면 release 한다. 담는 통을 CCArray 로 바꿨다.
std::vector<Item*> items;
기획에서 포션 같은 것은 겹쳐서 보여 달라고 했다. 열 개를 가지고 있으면 칸 열 개가 아니라 한 칸에 개수로 표시하는 것이다. CCArray 에 객체가 열 개 들어 있으니 그릴 때마다 같은 것끼리 묶어야 했다. InventoryLayer 의 그리는 코드가 계속 커졌다.
그래서 ItemSlot 이 개수를 들고 있게 바꿨다. 한 칸이 슬롯 하나가 되니 그리기가 단순해졌다. 첫 번째로 구조를 갈아엎은 것이다.
겹치는 것과 안 겹치는 것이 섞였다
무기와 방어구는 겹치면 안 됐다. 강화 수치가 달라서 같은 아이템 번호라도 다른 물건이기 때문이다.
여기서 한참 막혔다. 개수를 들고 있는 구조인데 겹칠 수 있는 것과 없는 것이 한 통에 섞인다. 결국 아이템 정의에 겹칠 수 있는지를 넣고 겹칠 수 없는 것은 개수가 항상 1인 슬롯을 따로 만들게 했다.
struct ItemSlot {
int itemId;
int count;
int enhanceLevel; // 겹칠 수 없는 것만 의미가 있다
};
이 구조가 계속 마음에 걸렸다. 어떤 필드가 어떤 아이템에만 의미가 있는 형태이기 때문이다. 그때는 더 나은 방법을 몰라서 그대로 뒀다.
목록과 배치를 나눴다
정렬 기능이 들어오면서 문제가 터졌다. 사용자가 아이템을 원하는 칸으로 옮길 수 있게 해 달라는 것이었다.
CCArray 순서를 화면 순서로 쓰고 있어서 아이템을 옮기면 재배치해야 했다. 그런데 중간에 빈 칸이 있으면 표현할 방법이 없었다. 배열에는 빈 칸이라는 개념이 없다.
std::map<int, ItemSlot> 으로 바꿔서 칸 번호를 키로 삼았다. 빈 칸은 그 키가 없는 것으로 표현된다.
우리가 쓰는 판에는 CCDictionary 가 아직 없어서 STL 쪽을 썼다. Application.mk 에 APP_STL := gnustl_static 을 이미 넣어 둔 덕이었다.
목록과 배치는 다른 문제인데 CCArray 하나로 둘 다 하려고 해서 계속 부딪혔던 것이다.
최대치와 남은 문제
마지막으로 인벤토리 최대 칸 수가 정해졌다. 여기서 놓친 것이 있었는데 칸이 꽉 차 있어도 겹칠 수 있는 아이템은 기존 슬롯에 더할 수 있어야 한다는 점이었다. 칸 수만 세고 막아서 포션을 잔뜩 가진 사람이 포션 하나를 못 먹는 상황이 나왔다.
얻을 수 있는지 판정하는 함수를 따로 뺐다. 겹칠 수 있고 기존 슬롯에 여유가 있으면 가능하고 빈 칸이 있어도 가능하며 둘 다 아니면 불가다. 판정을 한 곳에 모으니 예외도 한 곳에서만 다루면 됐고 관련 버그가 눈에 띄게 줄었다.
끝까지 못 정한 것도 하나 있다. 아이템의 효과를 아이템이 알게 할지 캐릭터가 알게 할지다. 캐릭터 쪽에 두는 것이 당장 짧아서 그렇게 갔는데 종류가 늘면서 분기가 길어졌고 프로젝트가 끝날 때까지 그대로였다. 다시 한다면 어떻게 할지는 확신이 없지만 당장 짧다는 것이 근거가 아니었다는 것은 알겠다.
정리
- 아이템 목록은 배열이지만 인벤토리는 배열이 아니었다
- 겹치는 것과 겹치지 않는 것이 섞이면 한 구조로 담기 어렵다
- 어떤 필드가 어떤 것에만 의미가 있는 구조가 된다
- 목록과 배치는 다른 문제다
- 배열 순서를 화면 위치로 쓰면 옮기기와 빈 칸에서 막힌다
- 칸 번호를 키로 하는
std::map이면 빈 칸이 그냥 없는 키가 된다 - 판정을 한 곳에 모으니 예외 처리도 한 곳이 됐다
- 당장 짧아서 고른 설계는 종류가 늘면 그 자리에 그대로 남는다