A. 아이템 드랍 실습 (text RPG)
1. 새 프로젝트 생성하기
a. 솔루션은 서류 가방
솔루션은 서류가방이고 프로젝트는 서류야,,,!
b. 시작 프로그램(프로젝트) 설정
아니면 그냥 아래처럼 프로젝트 우클릭으로 설정해도 됨 (우클릭+a)
2. Item 생성 및 출력하기
a. Item 클래스 생성하기
- Item.cpp / Item.h 파일 생성
- 아이템 클래스의 기본적인 틀(생성,소멸자, 멤버변수) 작성
- 아이템 희귀도 enum을 작성해주고 멤버변수에 포함
- 아이템을 상속할 Weapon과 Armor class를 작성
- 아이템 클래스와 동일하게 기본적인 틀 작성
- 각 자식클래스의 멤버변수에 접근하기 위한 get,set메소드 처리
🖇 Item.h
#pragma once
//아이템 희귀도
enum ItemRarity //3.
{
IR_None,
IR_Normal,
IR_Rare,
IR_Unique,
};
class Item //2.
{
public:
Item();
virtual ~Item(); //소멸자에는 virtual 필수
protected: //자식들이 사용할 수 있게
int _itemId = 0;
int _itemCount = 0;
ItemRarity _rarity = IR_Normal;
};
//상속1 4.5.
class Weapon : public Item {
public:
Weapon();
virtual ~Weapon();
void SetDamage(int damage) { _damage = damage; } //6.
int GetDamage() { return _damage; }
private:
int _damage = 0;
};
//상속2 4.5.
class Armor : public Item {
public:
Armor();
virtual ~Armor();
void SetArmor(int defence) { _defence = defence; }
int GetArmor() { return _defence; }
private:
int _defence = 0;
};
b. 몬스터를 잡으면 아이템을 드랍하는 함수 작성
- 아이템 주소값을 리턴하는 ItemDrop() 생성
- main()에 랜덤 시드값을 설정해주고
- 그 랜덤값에 따라 짝수면 Weapon, 홀수면 Armor로 각 클래스에 맞는 포인터 생성
- Item 포인터를 리턴해준다
🖇 RPGGame.cpp
#include <iostream>
#include "Item.h"
Item* DropItem() { //1.
if (rand() % 2 == 0) { //3.
Weapon* weapon = new Weapon(); //4.
return weapon;
}
else {
Armor* armor = new Armor();
return armor;
}
}
int main()
{
srand((unsigned int)time(0)); //2.
}
c. 아이템의 정보 출력해보기
- 어떤 아이템의 종류인지 출력하기 위해 Item의 PrintInfo() 함수를 생성
- 먼저 공통된 item의 멤버 변수인 희귀도를 item 생성자에서 초기화 해주고
- Item::PrintInfo()에 그 값에 따른 출력 로그를 작성해준다
🖇 Item.h
class Item
{
public:
...
void PrintInfo(); //1.
...
};
🖇 Item.cpp
#include "Item.h"
#include <iostream>
using namespace std;
Item::Item() /2.
{
int randValue = rand() % 100;
if (randValue < 50) {
_rarity = IR_Normal;
}
else if (randValue < 80) {
_rarity = IR_Rare;
}
else {
_rarity = IR_Unique;
}
}
void Item::PrintInfo() //3.
{
switch (_rarity) {
case IR_Normal:
cout << "IR_Normal" << endl;
break;
case IR_Rare:
cout << "IR_Rare" << endl;
break;
case IR_Unique:
cout << "IR_Unique" << endl;
break;
}
}
d. 가상함수로 변경해주기
"정보를 출력한다는 것"도 아이템에 있어야 하는 값이고,
그 자식 클래스들의 "정보를 출력한다는 것"도 동일하게 있어야 하는 행동임
따라서 가상함수로 변경하면 좋음!
- PrintInfo()를 가상함수로 변경해주고
- 각 Weapon과 Armor에 PrintInfo()를 선언 해주고
- cpp 파일에 와서 각 멤버변수를 출력해주고
- 위에서 작성한 아이템의 희귀도까지 같이 출력해준다 (item::PrintInfo())
🖇 Item.h
...
class Item
{
public:
...
virtual void PrintInfo(); //1.
...
};
class Weapon : public Item {
public
...
virtual void PrintInfo() override; //2.
...
};
class Armor : public Item {
public:
...
virtual void PrintInfo() override; //2.
...
};
🖇 Item.cpp
...
void Weapon::PrintInfo() //3.
{
cout << "**********************" << endl;
cout << "[아이템 타입] : 무기" << endl;
cout << "[공격력] : " << _damage << endl;
Item::PrintInfo(); //4.
cout << "**********************" << endl;
}
...
void Armor::PrintInfo()
{
cout << "**********************" << endl;
cout << "[아이템 타입] : 방어구" << endl;
cout << "[방어력] : " << _defence << endl;
Item::PrintInfo();
cout << "**********************" << endl;
}
e. 희귀도에 따른 멤버변수 초기화 하기
처음 설정한 각 멤버 변수의 값을 희귀도에 따라 랜덤한 값으로 설정해주고 초기화 함
🖇 Item.cpp
...
Weapon::Weapon()
{
switch (_rarity) {
case IR_Normal:
_damage = 1 + rand() % 5;
break;
case IR_Rare:
_damage = 1 + rand() % 15;
break;
case IR_Unique:
_damage = 1 + rand() % 25;
break;
}
}
...
Armor::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;
}
}
f. 중간 점검
🖇 RPGGame.cpp
main(){
..
for (int i = 0; i < 100; i++) {
Item* item = DropItem();
//무엇이 아이템으로 나왔는지 출력
item->PrintInfo();
...
}
3. 무기, 방어구의 값을 가지고 오기
1) 형변환
무기 방어구의 값은 Item에 존재하지 않는 값이고
각 멤버 변수를 출력하려고 부모 클래스에서 가상함수를 사용하는건 설계상 이상해짐
따라서 원래 아이템 타입을 기입해 나중에 형변환 해도그 값에 맞는 함수를 호출하는 것이 맞기 때문에
💥 가상함수를 이용하면 안됨!
위와 같이 Weapon인지 Armor인지 모르는 item값을 하나의 값으로 형변환 하게 된다면 위험해짐
👉 꼭 값을 체크하도록 하자
2) 아이템 타입을 item에 멤버 변수로 추가 기입
a. enum 파일 작성하기
- 파일을 보기쉽게 분류하기 위해 따로 enums.h 파일로 작성 (헤더 파일만 필요)
- 아이템 타입을 추가적으로 알려줄 값을 enum 값으로 생성
🖇 Enums.cpp
#pragma once
//아이템 희귀도
enum ItemRarity
{
IR_None,
IR_Normal,
IR_Rare,
IR_Unique,
};
//아이템 타입
enum ItemType{
IT_None,
IT_Weapon,
IT_Armor,
IT_Consumable,
};
b. 아이템 타입을 초기화 하기
- 위에서 생성한 Enums.h 파일을 받아주고
- 아이템 타입을 멤버 변수로 받아줌
- 아이템 타입을 간접적으로만 생성할 수 있도록(무기, 방어구로만) 기본 생성자를 막고. protected 처리
- itemType을 생성자의 인수로 받아 바로 초기화 할 수 있도록 해줌
- 아이템 타입 값이 없는 생성자를 막으면 자식 클래스는 부를 생성자가 없어지기 때문에, 각 생성자를 부를 때 각 itemType의 값을 item()의 인수로 넣어 따로 생성자를 불러줘야 함
🖇 Item.h
...
#include "Enums.h" //1.
class Item
{
protected:
//Item(); 3. 아이템 타입값이 없는 생성자를 막음
Item(ItemType itemType); //4.
...
protected:
...
ItemType _itemType = IT_None; //2.
};
class Weapon : public Item {
...
private:
...
ItemType itemType = IT_None;
};
class Armor : public Item {
...
private:
...
ItemType itemType = IT_None;
};
🖇 Item.cpp
...
Item::Item(ItemType itemType)
: _itemType(itemType) //아이템 타입 초기화
...
Weapon::Weapon() : Item(IT_Armor) //5.
...
Armor::Armor() :Item(IT_Armor)
c. 아이템 타입 내보내기
- item에 GetItemType 함수를 생성해 itemType을 리턴하게 해줌
- 값을 체크했다면 안전하게 형변환 할 수 있음
🖇 Item.h
public:
ItemType GetItemType() { return _itemType; } //1.
🖇 RPGGame.cpp
int main(){
...
//2. 아이템 타입에 따라 멤버 변수를 가져오고 싶을 때
ItemType itemType = item->GetItemType();
if (itemType == IT_Weapon) {
Weapon* weapon = (Weapon*)item;
weapon->GetDamage();
}
else if(itemType == IT_Armor)
{
Armor* armor = (Armor*)item;
armor->GetDefence();
}
}
실제 타입을 넣는 경우가 아니라 dynamic cast를 활용하는 경우도 있음! (취향 차이)
[게임 프로그래머 입문 올인원] C++ & 자료구조/알고리즘 & STL & 게임 수학 & Windows API & 게임 서버 -
어디부터 시작할지 막막한 게임 프로그래밍 입문자를 위한 All-In-One 커리큘럼입니다. C++, 자료구조/알고리즘, STL, 게임 수학, Windows API, 게임 서버 입문으로 이어지는 알찬 커리큘럼으로 게임 프
www.inflearn.com
'프로젝트 > [인프런] 게임 프로그래머 입문 올인원' 카테고리의 다른 글
[게임 프로그래머 입문 올인원] 2D게임 프레임워크 설계 : 스프라이트 (102강) (0) | 2024.04.17 |
---|---|
[게임 프로그래머 입문 올인원] 동적할당과 캐스팅 : 인벤토리 (38강) (1) | 2023.09.14 |
[게임 프로그래머 입문 올인원] 포인터와 배열 : 달팽이 문제 (25강) (2) | 2023.09.07 |
[게임 프로그래머 입문 올인원] 포인터와 배열 : 로또 번호 생성기 (24강) (0) | 2023.09.07 |
[게임 프로그래머 입문 올인원] 포인터와 배열 : 배열 실습 (19강) (0) | 2023.09.06 |