학습일지/C와 C++

C++ 콘솔 RPG 만들기

Tsukino Ren 2026. 3. 19. 19:40

전체 구조 설명

 Gpt를 통해 아래의 간단한 턴제 RPG 시스템  예제를 받아 작성해 보았다.

gpt가 이런식으로 예제를 줌

핵심 구성

  • Player : 플레이어 정보
  • Monster : 적 정보
  • Item : 포션 (회복 아이템)
  • vector : 인벤토리 관리
  • 전투 로직 : 공격 / 회복 / 턴 진행

클래스 설계

 Player 클래스

 
 
class Player
{
public:
	Player(std::string name, int maxhp, int hp, int maxmp, int mp, int atk, int def)
		:name(name), maxhp(maxhp), hp(hp), maxmp(maxmp), mp(mp), atk(atk), def(def) 
	{
	}
	~Player() {}
	
	std::string getName() const
	{
		return name;
	}
	int getMaxHP() const
	{
		return maxhp;
	}
	int getMaxMP()const
	{
		return maxmp;
	}
	int getHP() const
	{
		return hp;
	}
	int getMP() const
	{
		return mp;
	}
	int getATK() const
	{
		return atk;
	}
	int getDEF() const
	{
		return def;
	}
	
	void setName(std::string name)
	{
		this->name = name;
	}
	void setMaxHP(int maxhp)
	{
		this->maxhp = maxhp;
	}
	void setMaxMP(int maxmp)
	{
		this->maxmp = maxmp;
	}
	void setHP(int hp)
	{
		this->hp = hp;
	}
	void setMP(int mp)
	{
		this->mp = mp;
	}
	void setATK(int atk)
	{
		this->atk = atk;
	}
	void setDEF(int def)
	{
		this->def = def;
	}

private:
	std::string name;
	int maxhp;
	int maxmp;
	int hp;
	int mp;
	int atk;
	int def;
};

역할

  • 플레이어의 모든 능력치 관리
 

특징

  • Getter / Setter 완전 분리
  • 상태값 직접 접근 ❌ → 함수로만 접근 ⭕

이유
캡슐화 (Encapsulation)
→ 데이터 보호 + 유지보수 쉬움


Monster 클래스

 
class Monster
{
public:
	Monster(std::string type, int hp, int atk, int def)
		:type(type), hp(hp), atk(atk), def(def)
	{
	}
	std::string getType() const
	{
		return type;
	}
	int getHP() const
	{
		return hp;
	}
	int getATK() const
	{
		return atk;
	}
	int getDEF() const
	{
		return def;
	}

	void setType(std::string type)
	{
		this->type = type;
	}
	void setHP(int hp)
	{
		this->hp = hp;
	}
	void setATK(int atk)
	{
		this->atk = atk;
	}
	void setDEF(int def)
	{
		this->def = def;
	}

private:
	std::string type;
	int hp;
	int atk;
	int def;
};
 

역할

  • 몬스터 능력치 관리

특징

Player보다 단순한 이유
→ 기능이 적기 때문 (MP 없음 등)


Item 클래스

 
class Item
{
public:
	Item(std::string name, int healAmount, int count)
		:name(name), healAmount(healAmount), count(count)
	{
	}

	std::string getName() const
	{
		return name;
	}
	int gethealAmount() const
	{
		return healAmount;
	}
	int getCount() const
	{
		return count;
	}

	void setName(std::string name)
	{
		this->name = name;
	}
	void sethealAmount(int healAmount)
	{
		this->healAmount = healAmount;
	}
	void setCount(int count)
	{
		this->count = count;
	}

private:
	std::string name;
	int healAmount;
	int count;
};
 

역할

  • 포션 관리

핵심 포인트

  • 회복량
  • 개수 관리

enum class

enum class ItemType
{
	HP,
	MP,
	ALL
};
 

현재 코드에서는 직접 활용은 안 했지만 확장용 구조로 사용 예정

왜 enum class 사용하는가?

  • 타입 안정성
  • 이름 충돌 방지
  • 가독성

 

전투 로직

Player → Monster 공격

void attackMonster(Player* player, Monster* monster)
{
	int Damage;
	Damage = player->getATK() - monster->getDEF();
	
	if (Damage <= 0)
	{
		Damage = 1;
	}
	monster->setHP(monster->getHP() - Damage);

	if (monster->getHP() <= 0)
	{
		monster->setHP(0);
		std::cout << "처치했습니다\n";
	}
	
	printMonsterInfo(monster);
}
 

핵심 로직

  • 최소 대미지 보장

이유

  • 공격이 아예 안 들어가는 상황 방지
  • 역으로 HP가 회복되는 상황 방지

Monster → Player 공격

구조는 동일

void attackPlayer(Monster* monster, Player* player)
{
	int Damage;
	Damage = monster->getATK() - player->getDEF();

	if (Damage <= 0)
	{
		Damage = 1;
	}
	player->setHP(player->getHP() - Damage);

	if (player->getHP() <= 0)
	{
		player->setHP(0);
		std::cout << "사망하셨습니다.\n";
	}

	std::cout << "\n몬스터의 공격 턴\n";
	printPlayerInfo(player);
}
 

 


포션 사용

void usePotion(Item* potion, Player* player)
{
	if (potion->getCount() > 0)
	{
		int newHP = player->getHP() + potion->gethealAmount();
		
		if (player->getHP() >= player->getMaxHP())
		{
			std::cout << "\n체력이 이미 최대치입니다.\n";
		}
		else
		{
			player->setHP(newHP);
			if (player->getHP() > player->getMaxHP())
			{
				player->setHP(player->getMaxHP());
			}
			std::cout << "\n체력이 회복되었습니다!\n";
			printPlayerInfo(player);
			potion->setCount(potion->getCount() - 1);
		}
		std::cout << "\n남은 포션: " << potion->getCount() << std::endl;
	}
	else
	{
		std::cout << "\n포션이 없습니다..\n";
	}
}

 

처리 과정

  • 포션 개수 확인
  • 최대 HP 초과 방지
  • HP 회복
  • 포션 개수 감소

인벤토리 시스템 (vector)

std::vector<Item> Inventory;
Inventory.reserve(3u);

Inventory.emplace_back("HP포션", 30, 3);
Inventory.emplace_back("MP포션", 30, 2);
Inventory.emplace_back("만능 회복약", 100, 1);

추후 변경점에선 클래스 나누면서 인벤토리도 과제에서 했던 것처럼 바뀔 예정이다.

특징

함수설명
reserve 미리 공간 확보 (성능 최적화)
emplace_back 객체 직접 생성

 push_back을 안 쓴 이유는 enum class에 있는 Type을 쓰기 위해 미리 변경해 뒀다.


게임 루프

while (player.getHP() > 0 && m.getHP() > 0)
{
	int num;

	std::cout << "\n|[1] 공격|[2] 회복|[0] 종료|\n";
	std::cout << "번호를 입력하세요 : ";
	std::cin >> num;

	switch (num)
	{
	case 0:
		return 0;
	case 1:
		attackMonster(&player, &m);

		if (m.getHP() <= 0)
		{
			break;
		}

		attackPlayer(&m, &player);

		if (player.getHP() <= 0)
		{
			break;
		}
		break;
	case 2:
		std::cout << "\n|[1] HP포션|[2] MP포션|[3] 만능 회복약|\n";
		
		int itemNum;
		std::cin >> itemNum;
		if (0 < itemNum && itemNum <= Inventory.size())
		{
			usePotion(&Inventory[itemNum - 1], &player);
		}
		else
		{
			std::cout << "\n입력을 제대로 못한 죄로 이번턴은 그냥 넘깁니다.\n";
		}
		attackPlayer(&m, &player);
		break;
	default:
		std::cout << "\n번호를 다시 입력하세요\n";
	}
}

반복 조건

  • 플레이어 살아있고
  • 몬스터 살아있을 때

필자가 만들 때마다 즐겨 사용하는 while문 정말 좋다..

선택지

[1] 공격

[2] 회복

[3] 종료

공격 흐름

  • 플레이어 공격
  • 몬스터 죽었는지 확인
  • 몬스터 공격

회복 흐름

  • 아이템 선택
  • 회복
  • 몬스터 공격 (턴 유지)

중요 포인트!

행동 후 몬스터 턴 진행

핵심 정리

포인터 사용
  • 원본 객체 수정 가능

캡슐화

  • 직접 접근 대신 함수 사용

최소 대미지 처리

  • 게임 밸런스 핵심

vector 활용

  • 동적 배열
  • 인벤토리 구현에 적합

턴제 전투 구조

플레이어 행동 → 몬스터 행동으로 게임 기본 구조

개선점

  • enum class 활용하기
  • class 분리해서 정리하기 (지금은 main 에만 다 구현되어 있음)
  • 함수 전투 부분 포인터 대신 참조 사용하기

마무리

오늘은 GPT를 활용해 보며 예제들을 받아 배운 것들을 통해 직접 구현해 보는 과정을 가졌다.

직접 테스트해보며 문제가 있으면 수정하고 완성본을 보여주면서 괜찮았던 점 아쉬웠던 점 등을 gpt가 알려주며 다른 방식도 있다는 것 정도만 알려주고 다음 예제를 받아 추가하는 식으로 해봤습니다.

 

'학습일지 > C와 C++' 카테고리의 다른 글

C++ 첫 번째 협업 체험 팀 프로젝트  (0) 2026.03.26
C++ 연금술 공방 관리 시스템 구현 과제  (0) 2026.03.24
C++ 스마트 포인터  (0) 2026.03.18
C++ 인벤토리 시스템 구현  (0) 2026.03.17
C++ Debugging  (0) 2026.03.16