[C++ 기초] 함수 포인터, 과제(플레이어 경험치, 레벨업 만들기)

라멘커비·2024년 1월 2일
0

CPP 입문

목록 보기
14/25

함수 포인터

🍙함수 포인터 선언 및 사용 방법

프로그램의 모든 건 위치, 크기, 형태, 값으로 이루어진다. 함수도 함수의 포인터를 만들 수 있다.
리턴형태 (*변수명) () 형태로 선언한다.
변수명() 형태로 그 함수를 사용한다.

void Test() {
	printf("AAAAA");
}
int Test2() {
	return 10;
}

int main() {
	// 함수포인터 선언
	void(*Ptr)() = Test;
	int(*Ptr2)() = Test2;

	// 함수의 포인터이기 때문에 그 함수를 사용하는 방법 그대로 사용하게 된다.
	Ptr();	// "AAAAA"
}

보통 UI 등에서 많이 사용한다.
아래 코드에서는 Button이라는 클래스를 상속받은 MoveButton, AttackButton 클래스가 있다. 각 버튼을 사용하기 위해 각각의 객체를 생성하는 모습이다. 만약 버튼이 100개, 1000개 필요할 때 이런 코드로 만들게 된다면 클래스와 객체를 100개, 1000개씩 만들어야 한다. 이를 함수 포인터를 사용해서 개선할 수 있다.

class Button 
{
public:
    void Click() 
    {

    }

public:
    int Image;
    int Color;
    int ScreenPos;
};

class MoveButton : public Button
{
public:
    void Click() override
    {
        printf_s("플레이어가 이동합니다.");
    }
};

class AttackButton : public Button
{
public:
    void Click() override
    {
        printf_s("플레이어가 공격합니다.");
    }
};

int main()
{
    MoveButton MButton;
    AttackButton AButton;
    
    MButton.Click();
    AButton.Click();
}

🍙함수 포인터 사용 예시

아래 코드에서는 클래스 하나만으로 여러 쓰임새의 버튼 객체를 만들 수 있다. 멤버변수로 함수 포인터를 넣고 사용할 함수를 각각 넣어주는 방식이다. 잠시 다른 함수를 가리키도록 할 수도 있다.
함수 포인터도 포인터이므로 크기는 8바이트이다.

void PlayerAttack() 
{
    printf_s("플레이어가 공격합니다");
}

void PlayerMove()
{
    printf_s("플레이어가 이동합니다");
}

class Button 
{
public:
    void Click() 
    {
        // 이걸 콜백 방식이라고 합니다.
        Function();
    }

public:
    void (*Function)() = nullptr;
    int Image;
    int Color;
    int ScreenPos;
};

int main()
{
    Button AttButton;
    Button MoveButton;
    
    AttButton.Function = PlayerAttack;
    MoveButton.Function = PlayerMove;
    
    // 잠시 쓰임새를 바꿀 수도 있음.
    MoveButton.Function = PlayerAttack;
    
    AttButton.Click();
    MoveButton.Click();
    
    // 이름 삭제하면 그대로 함수포인터의 자료형이 된다.
    int FunctionPointerSize = sizeof(void(*)());    //8
}

과제

🍙과제 1

FightZone의 종류를 3개로 만든다.

초급 사냥터
중급 사냥터
고급 사냥터
로 가는 선택지를 보여준다.

초급의 몬스터는 Min 5 Max 15 Hp 100
중급의 몬스터는 Min 10 Max 20 Hp 200
고급의 몬스터는 Min 20 Max 40 Hp 300

FightZone.cpp

초중고급 몬스터의 Min/Max 데미지와 체력이 다르므로 각각 설정해주려고 했다. FightUnit 클래스에 SetMinAtt, SetMaxAtt, SetMaxHp 함수를 만들어서 FightZone의 In 함수에서 하나씩 이동할 수 있도록 하였다.
선생님은 main함수에서 FightZone의 배열을 만들어서 각각의 Zone을 만들어서 구현하셨다.

// in "FightZone.cpp"

void FightZone::In(Player& _Player)
{
	//초급 중급 고급 사냥터 고르기
	_Player.StatusRender();
	printf_s("어디로 가시겠습니다.\n");
	printf_s("1. 초급 사냥터.\n");
	printf_s("2. 중급 사냥터.\n");
	printf_s("3. 고급 사냥터.\n");
	printf_s("4. 나간다.\n");
	int Select = _getch();

	system("cls");

	switch (Select)
	{
	case '1':
	{
		// 초급
		NewMonster.SetMinAtt(5);
		NewMonster.SetMaxAtt(15);
		NewMonster.SetMaxHp(100);
		NewMonster.Heal();
		NewMonster.AddGold(8000);
		break;
	}
	case '2':
	{
		// 중급
		NewMonster.SetMinAtt(10);
		NewMonster.SetMaxAtt(20);
		NewMonster.SetMaxHp(200);
		NewMonster.Heal();
		NewMonster.AddGold(11000);
		break; 
	}
	case '3':
	{
		// 고급
		NewMonster.SetMinAtt(20);
		NewMonster.SetMaxAtt(40);
		NewMonster.SetMaxHp(300);
		NewMonster.Heal();
		NewMonster.AddGold(13000);
		break;
	}
	case '4':
		return;
	default:
		break;
	}

	system("cls");

	while (true)
	{
		_Player.StatusRender();
		NewMonster.StatusRender();

		// 선공 후공이 결정 나고
		// 조건에 따라서

		bool IsEnd = false;

		if (_Player.GetRandomSpeed() >= NewMonster.GetRandomSpeed())
		{
			printf_s("플레이어의 선공\n");

			IsEnd = FightLogic(_Player, NewMonster, _Player, NewMonster);
		}
		else 
		{
			printf_s("몬스터의 선공\n");
			IsEnd = FightLogic(NewMonster, _Player, _Player, NewMonster);
		}

			if (true == IsEnd) // 누군가 죽어서 싸움이 끝남.
			{
				NewMonster.Heal();
				if (!_Player.IsDeath()) { // Player가 이긴거라면 골드를 지급.
					_Player.FightStart(NewMonster);
					_getch();
				}
				return;
			}
	}

}

🍙과제 2

플레이어 레벨업 만들기.

  • 플레이어 레벨업 만들고 능력치 향상시키기, 싸움이 끝나면 레벨업 시키기.
    • 게임이 끝나고 할 일을 아예 묶어버림. 싸움 시작에 할 일도 묶기.(FightStart, FightEnd 인터페이스)
    • Monster와 Player가 싸움 끝나고 할 일이 다르므로 FightStart, FightEnd 함수를 virtual로 정의 후 override하기.

Player::FightEnd

플레이어가 싸움에서 이기고나면 실행하는 FightEnd함수이다.
플레이어가 이기면 몬스터의 랜덤Exp를 받고, 만약 레벨업이 가능한 경험치라면 레벨업한다. 레벨업을 하면 능력치향상이 되고 Hp가 리셋된다.
(저번 과제에서의 Heal함수가 Hp를 MaxHp로 만드는 함수이기 때문에 이름을 HpReset으로 바꿨다. 이유 : 더 적절한 이름같아서.)

void Player::FightEnd(FightUnit& _Other) {
	int MonsterGold = _Other.GetGold();
	printf("%s가 %d의 골드를 얻었습니다.\n", GetName(), MonsterGold);
	AddGold(MonsterGold);

	// 경험치 얻기, 경험치 차면 레벨업
	printf("%s가 %d의 경험치를 얻었습니다.\n", GetName(), Exp);
	TotalExp += Exp;

	while (TotalExp >= LevelUpExp) {
		Level ++;
		TotalExp -= LevelUpExp;
		SetMinAtt(GetMinAtt() + 10);
		SetMaxAtt(GetMaxAtt() + 10);
		SetMaxHp(GetMaxHP() + 50);
		LevelUpExp = Level * 1000;
		HpReset();
		printf("%s가 %d.Lv로 레벨업 했습니다.\n", GetName(), Level);
	}
}

Player::StatusRenderStart

레벨과 경험치를 렌더링하는 것은 플레이어만 필요하므로 StatusRenderStart()함수를 StatusUnit클래스에서 virtual로 만들고, Player클래스에서 override해서 플레이어에서만 렌더링하도록 했다.

void Player::StatusRenderStart() {
	printf_s("레벨 %d\n", Level);
	printf_s("경험치 %d/%d\n", TotalExp, LevelUpExp);
}

Monster::FightStart

몬스터는 싸움을 시작할 때 체력을 초기화하고 랜덤골드(보상)을 설정한다.

void Monster::FightStart(FightUnit& _Other) {
	HpReset();
	AddGold(RandomValue(5000,10000)); //랜덤골드
}

Monster::FightEnd

몬스터가 싸움에서 지고나면 랜덤 경험치를 설정한다. (이걸 플레이어가 받아가게 됨)

void Monster::FightEnd(FightUnit& _Other) {
	// 몬스터가 싸움 끝난 후 해야 할 일

	// 랜덤하게 경험치를 정해야 한다.
	// 1000 ~ 2000 사이의 경험치 계산
	_Other.SetExp(RandomValue(1000, 2000));
}

FightZone::In

플레이어와 몬스터의 FightStart, FightEnd 함수가 사용됐다.

void FightZone::In(Player& _Player)
{
	NewMonster.FightStart(_Player);
	//_Player.FightStart(NewMonster); 플레이어는 Start할 때 할 게 없음.

	//초급 중급 고급 사냥터 고르기
	_Player.StatusRender();
	printf_s("어디로 가시겠습니다.\n");
	printf_s("1. 초급 사냥터.\n");
	printf_s("2. 중급 사냥터.\n");
	printf_s("3. 고급 사냥터.\n");
	printf_s("4. 나간다.\n");
	int Select = _getch();

	system("cls");

	switch (Select)
	{
	case '1':
	{
		// 초급
		NewMonster.SetMinAtt(5);
		NewMonster.SetMaxAtt(15);
		NewMonster.SetMaxHp(100);
		NewMonster.HpReset();
		NewMonster.AddGold(8000);
		break;
	}
	case '2':
	{
		// 중급
		NewMonster.SetMinAtt(10);
		NewMonster.SetMaxAtt(20);
		NewMonster.SetMaxHp(200);
		NewMonster.HpReset();
		NewMonster.AddGold(11000);
		break; 
	}
	case '3':
	{
		// 고급
		NewMonster.SetMinAtt(20);
		NewMonster.SetMaxAtt(40);
		NewMonster.SetMaxHp(300);
		NewMonster.HpReset();
		NewMonster.AddGold(13000);
		break;
	}
	case '4':
		return;
	default:
		break;
	}

	system("cls");

	while (true)
	{
		_Player.StatusRender();
		NewMonster.StatusRender();

		bool IsEnd = false;

		if (_Player.GetRandomSpeed() >= NewMonster.GetRandomSpeed())
		{
			printf_s("플레이어의 선공\n");

			IsEnd = FightLogic(_Player, NewMonster, _Player, NewMonster);
		}
		else 
		{
			printf_s("몬스터의 선공\n");
			IsEnd = FightLogic(NewMonster, _Player, _Player, NewMonster);
		}

			if (true == IsEnd) // 누군가 죽어서 싸움이 끝남.
			{
				NewMonster.HpReset();
				if (!_Player.IsDeath()) {
                
					NewMonster.FightEnd(_Player);
					_Player.FightEnd(NewMonster);
                    
                    
					int Test = _getch();
				}
				return;
			}
	}

실행 모습

  • "초급 사냥터"에서 사냥 후 레벨업(1레벨 > 2레벨)
    경험치를 얻어 레벨업이 된다. 레벨이 올라가면 공격력과 체력이 늘어난다.
  • 강화 만렙, 6레벨 상태에서 "고급 사냥터" 싸움 모습(6레벨 > 7레벨)
    체력이 더 높은 몬스터를 이기는 모습이다.
profile
일단 시작해보자

0개의 댓글

관련 채용 정보