프로젝트/[인프런] 게임 프로그래머 입문 올인원

[게임 프로그래머 입문 올인원] 동적할당과 캐스팅 : 아이템 드랍 (36강 + 38강)

순정법사 2023.09.14

A. 아이템 드랍 실습 (text RPG)

1. 새 프로젝트 생성하기

a. 솔루션은 서류 가방

솔루션은 서류가방이고 프로젝트는 서류야,,,!

 

우클릭으로 생성

 

b. 시작 프로그램(프로젝트) 설정

솔루션 우클릭
요로코롬 나온다

 

아니면 그냥 아래처럼 프로젝트 우클릭으로 설정해도 됨 (우클릭+a)

 

 

 

2. Item 생성 및 출력하기

a. Item 클래스 생성하기 

  1. Item.cpp / Item.h 파일 생성
  2. 아이템 클래스의 기본적인 틀(생성,소멸자, 멤버변수) 작성
  3. 아이템 희귀도 enum을 작성해주고 멤버변수에 포함
  4. 아이템을 상속할 Weapon과 Armor class를 작성
  5. 아이템 클래스와 동일하게 기본적인 틀 작성
  6. 각 자식클래스의 멤버변수에 접근하기 위한 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. 몬스터를 잡으면 아이템을 드랍하는 함수 작성

  1. 아이템 주소값을 리턴하는 ItemDrop() 생성
  2. main()에 랜덤 시드값을 설정해주고
  3. 그 랜덤값에 따라 짝수면 Weapon, 홀수면 Armor로 각 클래스에 맞는 포인터 생성
  4. 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. 아이템의 정보 출력해보기

  1. 어떤 아이템의 종류인지 출력하기 위해 Item의 PrintInfo() 함수를 생성
  2. 먼저 공통된 item의 멤버 변수인 희귀도를 item 생성자에서 초기화 해주고
  3. 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. 가상함수로 변경해주기

"정보를 출력한다는 것"도 아이템에 있어야 하는 값이고,

그 자식 클래스들의 "정보를 출력한다는 것"도 동일하게 있어야 하는 행동임

따라서 가상함수로 변경하면 좋음!

 

  1. PrintInfo()를 가상함수로 변경해주고
  2. 각 Weapon과 Armor에 PrintInfo()를 선언 해주고
  3. cpp 파일에 와서 각 멤버변수를 출력해주고
  4. 위에서 작성한 아이템의 희귀도까지 같이 출력해준다 (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에 존재하지 않는 값이고

각 멤버 변수를 출력하려고 부모 클래스에서 가상함수를 사용하는건 설계상 이상해짐

 

따라서 원래 아이템 타입을 기입해 나중에 형변환 해도그 값에 맞는 함수를 호출하는 것이 맞기 때문에

💥 가상함수를 이용하면 안됨!

 

그렇다고 이렇게 무작정 변환하면 X

 

위와 같이 Weapon인지 Armor인지 모르는 item값을 하나의 값으로 형변환 하게 된다면 위험해짐

👉 꼭 값을 체크하도록 하자

 

2) 아이템 타입을 item에 멤버 변수로 추가 기입 

 

a. enum 파일 작성하기

  1. 파일을 보기쉽게 분류하기 위해 따로 enums.h 파일로 작성 (헤더 파일만 필요)
  2. 아이템 타입을 추가적으로 알려줄 값을 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. 아이템 타입을 초기화 하기

  1. 위에서 생성한 Enums.h 파일을 받아주고
  2. 아이템 타입을 멤버 변수로 받아줌 
  3. 아이템 타입을 간접적으로만 생성할 수 있도록(무기, 방어구로만) 기본 생성자를 막고. protected 처리  
  4. itemType을 생성자의 인수로 받아 바로 초기화 할 수 있도록 해줌
  5. 아이템 타입 값이 없는 생성자를 막으면 자식 클래스는 부를 생성자가 없어지기 때문에, 각 생성자를 부를 때 각 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. 아이템 타입 내보내기

  1. item에 GetItemType 함수를 생성해 itemType을 리턴하게 해줌
  2. 값을 체크했다면 안전하게 형변환 할 수 있음

 

🖇 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를 활용하는 경우도 있음! (취향 차이) 

 

 

 


출처 : https://www.inflearn.com/course/%EA%B2%8C%EC%9E%84-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-%EC%9E%85%EB%AC%B8-%EC%98%AC%EC%9D%B8%EC%9B%90-rookiss#curriculum

 

[게임 프로그래머 입문 올인원] C++ & 자료구조/알고리즘 & STL & 게임 수학 & Windows API & 게임 서버 -

어디부터 시작할지 막막한 게임 프로그래밍 입문자를 위한 All-In-One 커리큘럼입니다. C++, 자료구조/알고리즘, STL, 게임 수학, Windows API, 게임 서버 입문으로 이어지는 알찬 커리큘럼으로 게임 프

www.inflearn.com