0. 파일 구조
RPGGame.cpp
⊢ Enums.h : 아이템 희귀도와 타입을 열거형으로 저장
⊢ Inventory.cpp, Inventory.h : 인벤토리(컨테이너가 아닌 배열로 구현됨)
⨽ Item.cpp, Item.h : 아이템
1. 코드
(1) Item.h
#pragma once
#include "Enums.h"
// Item
// - Weapon
// - Armor
// - Consumable(소비템)
// --------Item---------
class Item
{
public:
// Item();
// 아머와 웨폰에 아이템 타입을 할당하도록 한다
Item(ItemType itemType);
// 부모 생성자 중복호출 및 함수 오버라이딩 문제
virtual ~Item();
virtual void PrintInfo();
ItemType GetItemType() { return _itemType; }
protected:
int _itemId = 0;
int _itemCount = 0;
ItemRarity _rarity = IR_Normal;
ItemType _itemType = IT_None;
};
// --------Weapon---------
class Weapon : public Item
{
public:
Weapon();
virtual ~Weapon();
virtual void PrintInfo() override;
void SetDamage(int damage) { _damage = damage; }
int GetDamage() { return _damage; }
private:
int _damage = 0;
};
// --------Armor---------
class Armor : public Item
{
public:
Armor();
virtual ~Armor();
virtual void PrintInfo() override;
void SetDefence(int defence) { _defence = defence; }
int GetDefence() { return _defence; }
private:
int _defence = 0;
};
(2) Item.cpp
#include "Item.h"
#include <iostream>
using namespace std;
// --------Item---------
Item::Item(ItemType itemType) : _itemType(itemType)
{
int randValue = rand() % 100;
if (randValue < 50)
{
_rarity = IR_Normal;
}
else if (randValue < 80)
{
_rarity = IR_Rare;
}
else
{
_rarity = IR_Unique;
}
}
Item::~Item()
{
}
void Item::PrintInfo()
{
switch (_rarity)
{
case IR_Normal:
cout << "희귀도 : 일반" << endl;
break;
case IR_Rare:
cout << "희귀도 : 레어" << endl;
break;
case IR_Unique:
cout << "희귀도 : 유니크" << endl;
break;
}
}
// --------Weapon---------
Weapon::Weapon() : Item(IT_Weapon)
{
switch (_rarity)
{
case IR_Normal:
_damage = 1 + rand() % 5;
break;
case IR_Rare:
_damage = 10 + rand() % 5;
break;
case IR_Unique:
_damage = 50 + rand() % 5;
break;
}
}
Weapon::~Weapon()
{
}
void Weapon::PrintInfo()
{
cout << "====================" << endl;
Item::PrintInfo();
cout << "[무기]" << endl;
cout << "[공격력 : " << _damage <<"]" << endl;
cout << "====================" << endl;
}
// --------Armor---------
Armor::Armor() : Item(IT_Armor)
{
switch (_rarity)
{
case IR_Normal:
_defence = 1 + rand() % 3;
break;
case IR_Rare:
_defence = 2 + rand() % 4;
break;
case IR_Unique:
_defence = 3 + rand() % 5;
break;
}
}
Armor::~Armor()
{
}
void Armor::PrintInfo()
{
cout << "====================" << endl;
Item::PrintInfo();
cout << "[방어구]" << endl;
cout << "[방어력 : " << _defence << "]" << endl;
cout << "====================" << endl;
}
(3) Main (RPGGame.cpp)
#include <iostream>
#include "Item.h"
using namespace std;
// 아이템은 아이템 타입으로 반환
Item* DropItem()
{
if (rand() % 2 == 0)
{
Weapon* weapon = new Weapon();
return weapon;
}
else
{
Armor* armor = new Armor();
return armor;
}
}
int main()
{
srand((unsigned)time(0));
for (int i = 0; i < 100; i++)
{
Item* item = DropItem();
// PrintInfo를 가상함수로 만듬
// override까지 하면 각 타입의 아이템에서 사용가능
item->PrintInfo();
}
}
2. 코드 분석
(1) 아이템의 종류
아이템은 크게 두 가지 분류로 나뉜다(단순 분류, 투사체 생략).
- Consumable (소모품)
- Non-Consumable (비 소모품, 장비)
그리고 장비는 공격력을 가지는 무기와 방어력을 가지는 방어구(이하 갑옷)으로 나뉜다.
class Item
{
public:
Item();
// 부모 생성자 중복호출 및 함수 오버라이딩 문제
virtual ~Item();
virtual void PrintInfo();
ItemType GetItemType() { return _itemType; }
protected:
int _itemId = 0;
int _itemCount = 0;
ItemRarity _rarity = IR_Normal;
ItemType _itemType = IT_None;
};
아이템의 큰 틀을 살펴보면, 각 아이템은 고유의 ID를 가지도록 하고(_itemID), 떨어진 아이템의 개수를 관리할 수 있도록 _itemCount를 가지게 한다.
// ------------- Enums.h-------------------
#pragma once
enum ItemRarity
{
IR_None,
IR_Normal,
IR_Rare,
IR_Unique,
};
enum ItemType
{
IT_None,
IT_Weapon,
IT_Armor,
IT_Consumable,
};
또한 아이템의 희귀도인 _rarity와 무기, 갑옷, 소모품을 판단하는 _itemType을 설정한다.
(2) 가상 함수
Item 클래스의 소멸자를 가상함수로 만들어 준 이유는 자식 클래스의 소멸자를 올바르게 호출하기 위함이다.
부모 클래스를 상속받은 자식 클래스가 소멸할 때 부모 클래스의 소멸자가 virtual이 아니라면
자식 클래스(Weapon, Armor)의 소멸자가 호출되지 않아 메모리가 반환되지 않게 된다.
이후에 자식 클래스인 Weapon과 Armor의 정보를 출력할 수 있도록 PrintInfo()를 가상 함수(virtual)로 만들어준다.
이후에 Weapon과 Armor에서 PrintInfo 함수를 오버라이딩(재정의)하여 사용할 것이다.
(3) 아이템 구현부
Item::Item(ItemType itemType)) : _itemType(itemType)
{
int randValue = rand() % 100;
if (randValue < 50)
{
_rarity = IR_Normal;
}
else if (randValue < 80)
{
_rarity = IR_Rare;
}
else
{
_rarity = IR_Unique;
}
}
Item::~Item()
{
}
void Item::PrintInfo()
{
switch (_rarity)
{
case IR_Normal:
cout << "희귀도 : 일반" << endl;
break;
case IR_Rare:
cout << "희귀도 : 레어" << endl;
break;
case IR_Unique:
cout << "희귀도 : 유니크" << endl;
break;
}
}
아이템이 드랍될 때 아이템의 희귀도를 어떤 확률에 따라 설정하는 코드이다.
PrintInfo 함수는 희귀도에 따라 각 희귀도를 출력하도록 만든다.
(4) Weapon 클래스
class Weapon : public Item
{
public:
Weapon();
virtual ~Weapon();
virtual void PrintInfo() override;
void SetDamage(int damage) { _damage = damage; }
int GetDamage() { return _damage; }
private:
int _damage = 0;
};
Weapon 클래스는 Item 클래스의 자식 클래스이다.
PrintInfo를 오버라이딩하여 생성된 Weapon 객체의 공격력을 출력할 것이다.
멤버 변수를 private으로 지정하여 외부에서 아이템의 공격력을 수정할 수 없게 만들고, public 영역 내에 GetDamage 함수와 SetDamage 함수를 이용하여 _damage에 접근할 수 있도록 한다.
(4-1) Weapon 클래스 구현부
Weapon::Weapon() : Item(IT_Weapon)
{
switch (_rarity)
{
case IR_Normal:
_damage = 1 + rand() % 5;
break;
case IR_Rare:
_damage = 10 + rand() % 5;
break;
case IR_Unique:
_damage = 50 + rand() % 5;
break;
}
}
Weapon::~Weapon()
{
}
void Weapon::PrintInfo()
{
cout << "====================" << endl;
Item::PrintInfo();
cout << "[무기]" << endl;
cout << "[공격력 : " << _damage <<"]" << endl;
cout << "====================" << endl;
}
단순하게 희귀도에 따라 공격력에 차등을 주는 코드이다.
오버라이딩 된 PrintInfo 함수는 드랍된 아이템이 무기 타입이고, 공격력이 얼마인지 출력하게 한다.
(5) Armor 클래스
class Armor : public Item
{
public:
Armor();
virtual ~Armor();
virtual void PrintInfo() override;
void SetDefence(int defence) { _defence = defence; }
int GetDefence() { return _defence; }
private:
int _defence = 0;
};
Armor 클래스도 무기와 동일하지만 damage 대신 defence를 사용한다.
(5-1) Armor 클래스 구현부
Armor::Armor() : Item(IT_Armor)
{
switch (_rarity)
{
case IR_Normal:
_defence = 1 + rand() % 3;
break;
case IR_Rare:
_defence = 2 + rand() % 4;
break;
case IR_Unique:
_defence = 3 + rand() % 5;
break;
}
}
Armor::~Armor()
{
}
void Armor::PrintInfo()
{
cout << "====================" << endl;
Item::PrintInfo();
cout << "[방어구]" << endl;
cout << "[방어력 : " << _defence << "]" << endl;
cout << "====================" << endl;
}
희귀도에 따라 방어력에 차등을 준다.
오버라이딩 된 PrintInfo 함수를 사용하여 방어구 타입을 표시하고 방어력을 출력하게 한다.
(6) Main (RPGGame.cpp)
#include <iostream>
#include "Item.h"
using namespace std;
// 아이템은 아이템 타입으로 반환
Item* DropItem()
{
if (rand() % 2 == 0)
{
Weapon* weapon = new Weapon();
return weapon;
}
else
{
Armor* armor = new Armor();
return armor;
}
}
int main()
{
srand((unsigned)time(0));
for (int i = 0; i < 100; i++)
{
Item* item = DropItem();
item->PrintInfo();
}
}
DropItem 함수는 1/2 확률로 무기 혹은 아머를 드랍하는 함수이다.
무작위로 수를 추출하기 위해 시드를 설정한다.
그리고 100개의 아이템을 드랍하도록 한다.
이 때 DropItem 함수는 Item 클래스의 포인터를 반환한다.
드랍된 아이템은 Item으로 형변환이 된다(Weapon -> Item, Armor -> Item).
Item 클래스에서 PrintInfo 함수는 가상 함수이고, Weapon 클래스와 Armor 클래스에서 오버라이딩 되어 있기 때문에
Weapon 클래스와 Armor 클래스의 PrintInfo를 호출하게 된다.