Learning
레슨 7 / 8·25분

실전: 간단한 게임 엔진

프로젝트 개요 — 텍스트 기반 RPG 엔진

지금까지 배운 C++ 기능을 활용하여 텍스트 기반 RPG 게임 엔진을 만들어 봅니다. 상속, 다형성, STL 컨테이너, 스마트 포인터를 실전에서 어떻게 조합하는지 경험합니다.

게임 엔티티 설계

cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <map>
#include <cstdlib>
#include <ctime>
using namespace std;

// ── 기본 엔티티 클래스 ──
class Entity {
protected:
    string name;
    int hp;
    int maxHp;
    int attack;
    int defense;
public:
    Entity(const string& n, int h, int atk, int def)
        : name(n), hp(h), maxHp(h), attack(atk), defense(def) {}

    virtual ~Entity() = default;

    virtual void showStatus() const {
        cout << name << " [HP: " << hp << "/" << maxHp
             << " ATK: " << attack << " DEF: " << defense << "]" << endl;
    }

    bool isAlive() const { return hp > 0; }
    const string& getName() const { return name; }
    int getAttack() const { return attack; }

    void takeDamage(int dmg) {
        int actual = max(1, dmg - defense);
        hp = max(0, hp - actual);
        cout << name << "이(가) " << actual << " 데미지를 받았다! "
             << "(HP: " << hp << ")" << endl;
    }

    void heal(int amount) {
        hp = min(maxHp, hp + amount);
        cout << name << " HP " << amount << " 회복! "
             << "(HP: " << hp << ")" << endl;
    }
};

플레이어와 몬스터 구현

cpp
// ── 아이템 시스템 ──
struct Item {
    string name;
    string type;  // "weapon", "potion"
    int value;
};

// ── 플레이어 클래스 ──
class Player : public Entity {
    int level;
    int exp;
    vector<Item> inventory;
public:
    Player(const string& n)
        : Entity(n, 100, 15, 5), level(1), exp(0) {}

    void showStatus() const override {
        cout << "=== " << name << " (Lv." << level << ") ===" << endl;
        Entity::showStatus();
        cout << "경험치: " << exp << "/100" << endl;
        cout << "인벤토리: " << inventory.size() << "개 아이템" << endl;
    }

    void gainExp(int amount) {
        exp += amount;
        cout << name << "이(가) 경험치 " << amount << " 획득!" << endl;
        while (exp >= 100) {
            exp -= 100;
            levelUp();
        }
    }

    void addItem(const Item& item) {
        inventory.push_back(item);
        cout << item.name << "을(를) 획득했다!" << endl;
    }

    void usePotion() {
        for (auto it = inventory.begin(); it != inventory.end(); ++it) {
            if (it->type == "potion") {
                heal(it->value);
                inventory.erase(it);
                return;
            }
        }
        cout << "포션이 없습니다!" << endl;
    }

private:
    void levelUp() {
        level++;
        maxHp += 20;
        hp = maxHp;
        attack += 3;
        defense += 2;
        cout << "*** 레벨 업! Lv." << level << " ***" << endl;
    }
};

// ── 몬스터 클래스 ──
class Monster : public Entity {
    int expReward;
public:
    Monster(const string& n, int h, int atk, int def, int expR)
        : Entity(n, h, atk, def), expReward(expR) {}

    int getExpReward() const { return expReward; }
};

전투 시스템과 게임 루프

cpp
// ── 전투 시스템 ──
class BattleSystem {
public:
    static bool fight(Player& player, Monster& monster) {
        cout << "\n=== 전투 시작: " << monster.getName()
             << " 등장! ===" << endl;

        while (player.isAlive() && monster.isAlive()) {
            // 플레이어 턴
            int playerDmg = player.getAttack() + (rand() % 5);
            monster.takeDamage(playerDmg);

            if (!monster.isAlive()) {
                cout << monster.getName() << "을(를) 처치했다!" << endl;
                player.gainExp(monster.getExpReward());
                return true;
            }

            // 몬스터 턴
            int monsterDmg = monster.getAttack() + (rand() % 3);
            player.takeDamage(monsterDmg);
        }

        cout << "패배했습니다..." << endl;
        return false;
    }
};

// ── 몬스터 팩토리 ──
class MonsterFactory {
    static vector<tuple<string, int, int, int, int>> templates;
public:
    static void init() {
        templates = {
            {"슬라임", 30, 5, 2, 20},
            {"고블린", 50, 10, 4, 35},
            {"오크", 80, 15, 8, 50},
            {"드래곤", 150, 25, 12, 100},
        };
    }

    static unique_ptr<Monster> create(int difficulty) {
        int idx = min(difficulty, (int)templates.size() - 1);
        auto& [n, h, a, d, e] = templates[idx];
        return make_unique<Monster>(n, h, a, d, e);
    }
};

vector<tuple<string, int, int, int, int>> MonsterFactory::templates;

// ── 메인 게임 루프 ──
int main() {
    srand(time(nullptr));
    MonsterFactory::init();

    Player hero("용사");
    hero.addItem({"체력 포션", "potion", 30});
    hero.addItem({"체력 포션", "potion", 30});

    cout << "\n=== 텍스트 RPG 시작! ===" << endl;
    hero.showStatus();

    for (int stage = 0; stage < 4; stage++) {
        auto monster = MonsterFactory::create(stage);
        if (!BattleSystem::fight(hero, *monster)) break;
        hero.usePotion();
        hero.showStatus();
    }

    cout << "\n=== 게임 종료 ===" << endl;
    return 0;
}
  • 상속과 다형성 — Entity 기반 클래스에서 Player, Monster 파생
  • 스마트 포인터 — MonsterFactory가 unique_ptr로 몬스터 생성
  • STL 컨테이너 — vector로 인벤토리, map으로 데이터 관리
  • 가상 함수 — showStatus()를 override하여 다형적 동작
  • 팩토리 패턴 — MonsterFactory로 객체 생성 캡슐화
💡

이 프로젝트를 확장해 보세요: 마법 시스템, 장비 착용, 상점, 던전 탐험, 파일로 세이브/로드 기능 등을 추가하면 더 풍부한 게임이 됩니다. 각 기능을 별도 클래스로 분리하는 연습을 하세요.