Unreal 개발 본 캠프 25일차

HappyCircle·2026년 1월 2일

Unreal 개발

목록 보기
42/163

오늘 진행 내용

[CH2: 팀 프로젝트] 텍스트 콘솔 RPG

.dat 기반 아이템 로딩 + Effect 중심 설계 & Shop/Inventory 연동 아이디어 정리

오늘은 콘솔 RPG 프로젝트에서 아이템 시스템 전반을 구조적으로 정리하는 데 집중했다.
실제 UI와 상점 기능을 바로 구현하기보다는,
데이터 기반 아이템 관리 + 안전한 Shop–Inventory 연결 구조를 먼저 설계하는 것이 목표였다.

1️⃣ 오늘 작업의 핵심 요약

아이템 정보와 ASCII 아트를 .dat 파일로 분리

SimpleDataLoader를 통해 .dat를 파싱

Item 생성은 ItemFactory로 집중

아이템 효과는 Effect 중심 구조로 통합

Shop / Inventory UI는 아직 아이디어·스케치 단계

Shop 연동 방식으로 “clone 기반 구매” 구조를 설계

👉 현재 상태는 적용 완료가 아니라 설계 + 기반 구축 단계이며,
이 구조를 바탕으로 다음 단계에서 실제 기능을 붙일 예정이다.

2️⃣ Item 설계 방향 – Effect 중심 구조

회복 아이템, 버프 아이템, 장비 아이템을
각각 다른 로직으로 처리하지 않고 Effect 하나로 통합했다.

enum class EffectType {
    HealFlat,
    AddStatFlat,
    InvincibleHits
};

enum class StatType {
    Attack,
    MaxHealth
};

struct Effect {
    EffectType type;
    StatType stat;   // AddStatFlat일 때만 의미
    int value;       // 회복량 / 증감량 / 무적 횟수
    int duration;    // 지속 턴 (0이면 즉시)
};

Item은 Effect 데이터를 제공만

실제 스탯 변경은 EffectManager에서 처리

Inventory는 “언제 적용/원복할지”만 판단

3️⃣ .dat 파일 기반 아이템 + ASCII 아트 관리

아이템과 아스키 아트를 코드에 하드코딩하지 않고
텍스트 기반 .dat 파일에서 관리하도록 설계했다.

📄 items.dat 예시

ITEM|Small Potion|50
EFFECT|HealFlat|Health|50|0

ITEM|One Hand Sword|300
EFFECT|AddStatFlat|Attack|10|0

ASCII|Small Potion
 __
|  |
|__|
END

ASCII|One Hand Sword
 /\
 ||====>
 \/
END

ITEM|이름|가격

EFFECT|타입|스탯|값|duration

ASCII|이름 ~ END 사이가 아스키 아트

4️⃣ SimpleDataLoader – 현재 구현된 핵심

SimpleDataLoader는 .dat 파일을 읽어 다음을 수행한다.

ITEM 블록 파싱

EFFECT 누적

아이템 flush 시점에 ItemFactory로 Item 생성

ASCII 블록은 MapAsciiArtRepository에 저장

아이템 생성 분기(요약)

const Effect& e = currentEffects.front();
Item* item = nullptr;

if (e.type == EffectType::HealFlat)
    item = ItemFactory::createHeal(currentItemName, currentPrice, e.value);
else if (e.duration > 0)
    item = ItemFactory::createBuff(currentItemName, currentPrice, currentEffects);
else if (e.stat == StatType::Attack)
    item = ItemFactory::createWeapon(currentItemName, currentPrice, e.value);
else if (e.stat == StatType::MaxHealth)
    item = ItemFactory::createArmor(currentItemName, currentPrice, e.value);

if (item) inv.add(item);

⚠️ 현재는 Inventory에 바로 add하는 구조
→ 다음 단계에서 Shop 전용 로딩으로 확장 예정

5️⃣ Shop / Inventory UI – 현재는 “스케치 단계”

아직 실제 적용은 하지 않았고,
콘솔 UI 흐름만 아이디어로 정리한 상태다.

Shop UI 스케치
=== Shop ===
0) Small Potion - 50 G
1) One Hand Sword - 300 G
Gold: 420
[1] Buy [2] Sell [0] Exit

Inventory UI 스케치
=== Inventory ===
0) Small Potion
1) One Hand Sword
[1] Use [2] Equip [3] Unequip [0] Back

👉 실제 구매/판매/장착/사용 로직은
아래의 “clone 기반 구매 구조”를 확정한 뒤 적용할 계획이다.

6️⃣ Shop 연동 아이디어 – clone 기반 구매 (핵심)

Shop과 Inventory가 Item*를 공유하면
댕글링 포인터 / 중복 delete 문제가 발생하기 쉽다.

해결 전략

Shop은 진열용 프로토타입만 보관

구매 시에는 항상 clone()으로 새 Item 생성

Inventory가 소유권을 가짐(delete 책임)

7️⃣ Item에 clone() 추가 (최소 수정)

class Item {
public:
    virtual ~Item() = default;
    virtual std::string getName() = 0;
    virtual Item* clone() const = 0;
};

Item* HealItem::clone() const {
    return new HealItem(*this);
}

new Derived(*this) 방식으로 간단하게 복제

현재 구조에서는 값 타입 위주라 안전
8️⃣ Shop 최소 수정 – buyItem에서 clone 반환

Item* Shop::buyItem(int index) {
    if (index < 0 || index >= (int)Items.size()) return nullptr;
    return Items[index]->clone(); // 핵심
}

구매 흐름:

Item* bought = shop->buyItem(idx);
if (bought) inventory.add(bought);

Shop: 프로토타입 유지

Inventory: 실제 아이템 소유

9️⃣ 다음 적용 예정 방향

SimpleDataLoader::loadShop(path, shop, artRepo) 형태로 확장

Shop은 .dat 기반으로 진열품 구성

Inventory에는 구매 시 clone으로만 유입

이후 UI 입력/출력과 실제 구매·판매 로직 적용

profile
개발합시다!

0개의 댓글