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를 호출하게 된다.

'포트폴리오 > 기능 구현 연습' 카테고리의 다른 글

2. 인벤토리  (0) 2023.09.22