https://zerosik00.tistory.com/56
C++ 인벤토리, 제작시스템 구현해보기 1
목표객체 지향 설계방식과 SOLID원칙을 기반으로 하여.C++ CLI환경에서 간단한 게임 시스템을 구현해보기.그중에서도 아이템과 제작 시스템을 구현해보는것. SOLID 원칙이란?Single responsibity principle,
zerosik00.tistory.com
이전에 설계한 클래스들을 바탕으로 각 클래스의 기능을 만들어보자.
필자가 아직 const와 &키워드 사용방법이 익숙하지 않은점 양해바란다.

우선 기초가 될 Item 클래스는 값만 가지고있는 사실상 구조체의 역활을 한다.
//Item.h
#pragma once
#include <string>
class Item {
int id;
std::string name;
std::string description;
public:
Item() :id(-1), name(""), description("") {}
Item(int& _id, const std::string& _name, const std::string& _desc) :
id(_id), name(_name), description(_desc) {}
int getId() const { return id; }
std::string getName() const { return name; };
std::string getDescription() const { return description; };
};
값을 저장하고, 생성자에서 값을 초기화도록 구성하였다.
기본 생성자를 사용하면 id값이 -1이 되어 잘못된 값이라는 걸 인지하도록 한다.
다만 위의 방법은 확장을 생각하면 좋은 방법은 아니나 임시방편.
수정될 일이 없는 객체인만큼 getter만 구현하고 마무리.
다음으로 아이템의 정의가 담길 ItemDatabase 클래스.
이름에서 드러나듯 데이터베이스의 역할을 할 예정이다.
게임으로 치면 도감. 그런데 생각해보면 이 클래스는 하나만 필요할것이 자명하다.
완전히 동일한내용의 데이터베이스(도감)이 두개있어도 하나는 쓸모가 없을게 뻔하니까. 메모리도낭비고, 리소스도낭비고...
그래서 단 하나의 객체만 존재하도록 하는 디자인 패턴인 Singleton을 적용할 필요가 있다고 생각했다.
//ItemDatabase.h
#pragma once
#include <map>
#include "Item.h"
class ItemDatabase
{
private:
std::map<int, Item> itemDatabase;
static ItemDatabase* instance;
void InitializeItemDatabase();
ItemDatabase() { InitializeItemDatabase(); }
public:
ItemDatabase(const ItemDatabase&) = delete;
ItemDatabase& operator=(const ItemDatabase&) = delete;
static ItemDatabase* getInstance() {
if (instance == nullptr) {
instance = new ItemDatabase();
}
return instance;
}
//아이템 조회
Item getItemByID(int& id);
};
정적 멤버번수를 선언하고, 최초 실행시에 초기화. 이후 하나의 인스턴스를 공유하는 싱글톤 패턴을 통해
단 하나만의 객체를 이용하도록 만들었다.
데이터베이스로서의 단일역활을 가지고, Id를 통해 아이템의 정보를 조회하는 기능을 담당한다.
실제 DB라고 생각하면 플레이어가 도감을 수정하는건 말이안되니 오직 조회기능만을 제공한다
다음은 Inventory 클래스.
아이템의 입출력과 저장을 담당할것이다
//Inventory.h
#pragma once
#include <map>
class Inventory {
private:
int maxStack = 999;
std::map<int, int> stock;
public:
//전체 아이템 재고 읽기
const std::map<int, int> getStock();
//특정 아이템 개수 가져오기
int getCount(int itemId);
//개수가 maxStack넘기면 false
bool addItem(int itemId, int count);
//재고보다 소모량이 많으면 false
bool consumeItem(int itemID, int count);
//특정 아이템 재고 충분한지 확인
bool enoughItem(int itemID, int count);
};
아이템을 저장할 map의 타입을 어떻게 할 지 고민을 좀 했는데, map<int, int>로 사용하도록 하였다.
이유는 메모리와 연산을 최적화하면서 단일 책임원칙에 맞게 "재고관리"에만 목적을 두도록 하기 위해서이다.
우리는 이미 ItemDatabase를 통해 외부에서 아이템 데이터를 읽을 수 있는데,
이 책임을 또다시 Inventory에게 줄 이유가 없다고 판단하였다.
다음은 제작 시스템의 기반이 될 제작 레시피 CraftRecipe클래스.
Item 과 마찬가지로 구조체처럼 이용할 예정이다.
//CraftRecipe.h
#pragma once
#include <map>
class CraftRecipe {
//결과 id
int resultItemId;
//id, 수량
std::map<int, int> Ingredients;
public:
CraftRecipe() : resultItemId(-1), Ingredients({}) {}
CraftRecipe(int _result, std::map<int, int> _ids) :resultItemId(_result), Ingredients(_ids) {}
bool compareIngredient(std::map<int, int> comp) {
return Ingredients == comp;
}
int getItemid() const { return resultItemId; }
std::map<int, int> getIngredients() const { return Ingredients; }
};
조합결과 아이템의 id를 가지고, 제작에 필요한 재료를 인벤토리때처럼 map<int, int>로 정의하였다.
멤버 변수의 사예를 들면 id=1인 강철 검의 제작식이 id=2인 목재 1개와 id=3인 철괴 2개라고 한다면,
CraftRecipe의 resultItemId는 1, Ingredients는 [ [2, 1], [3, 2] ] 의 형태가 될것으로 생각된다.
또, map은 자동 Key기반의 자동 정렬을 지원해서, map끼리 비교할 때 넣은 순서에 상관없이 key-value가 동일하면
operator == 연산에서 같음을 반환하기에 사용이 편하다.
마지막으로 핵심 시스템이될 CraftManager클래스.
//CraftManager.h
#pragma once
#include "ItemDatabase.h"
#include "CraftRecipe.h"
#include "Inventory.h"
#include <map>
#include <vector>
class CraftingManager
{
private:
std::vector<CraftRecipe> craftRecipes;
ItemDatabase& db;
const void InitializeRecipes();
public:
CraftingManager(ItemDatabase& _db) :db(_db) {
InitializeRecipes();
}
//아이템 제작 시도, 실패시 -1
int Craft(const std::map<int, int>& items);
//특정 아이템 제작, 아이템id, 인벤토리 참조
int Craft(const int ItemID, Inventory& inventory);
//레시피 중 특정 재료를 사용하는 아이템 리스트
std::vector<int> getItemUsingIngredient(int ingredientId);
//인벤토리참조하여 가능한 모든 제작 가능한 아이템 반환
std::vector<int> getCraftableItems(Inventory& inventory);
//모든 레시피 출력
void showAllRecipes();
};
이때까지 만든 클래스들을 모두 활용하게될 코어.
(플레이어의)인벤토리 객체를 연결해서 가지고있도록 하고 초기 생성시에 모든 레시피를 초기화하도록 하고
클래스의 역활은 제작과 정보 제공
Craft함수를 통해 제작을,
getItemUsingIngredient() 함수에서는 주어진 재료로 만들 수 있는 목록을 반환
getCraftableItems()는 플레이어의 인벤토리에서 당장 만들 수 있는 목록을 반환한다.
showAllRecipes를 통해 모든 레시피에 대한 정보도 알아볼 수 있다.
초기 구상에 더하여 헤더파일 구현을 진행해보았다.
다음은 초기 아이템과 레시피를 정의해보고 메인의 루프 함수를 만들어 인벤토리 재고관리, 아이템 제작을 해보면서 문제점을 찾아보고 트러블슈팅을 목표로 한다.
사족.
아직 설계가 미숙하여 SOLID를 신경쓴다면서 SRP밖에 신경을 못쓰고있는 느낌이 든다.
아직까지 인터페이스로 추상화하지 않았기도 하고, 상속관계도 없어서 ISP와 DIP에 대해서 적용이 안되어 있기도 하다.
OCP:개방폐쇄원칙에 최소한의 필요한 기능만 정의하여 확장에 대해 열려있다고 생각하는데, 추후에 살을 붙여보면서 느끼는게 체득하기 더 좋을것같다.
OCP를 위해 당장 기능 구현을 위한 최소한의 기능만을 정의해두었는데, 이해한 바가 맞는지도 좀 햇갈린다.
여러 예시코드들을 찾아보면서 더 익숙해져야 할듯.
C++문법도 아직 익숙치가않아서 따로 정리해야겟다.(const나 &사용 등.)
'개발 > 내일배움캠프' 카테고리의 다른 글
| C++ 인벤토리, 제작시스템 구현해보기 4 / 실제 기능 구현 (0) | 2026.03.18 |
|---|---|
| C++ 인벤토리, 제작시스템 구현해보기 3 (0) | 2026.03.16 |
| C++ 인벤토리, 제작시스템 구현해보기 1 (0) | 2026.03.12 |
| 자주 볼거같은 Character Movement컴포넌트 속성들 기록 (0) | 2026.02.27 |
| C언어 conversion specification(서식or형식 지정자) (0) | 2026.02.25 |