프로그램의 모든 건 위치, 크기, 형태, 값으로 이루어진다. 함수도 함수의 포인터를 만들 수 있다.
리턴형태 (*변수명) ()
형태로 선언한다.
변수명()
형태로 그 함수를 사용한다.
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
}
초급 사냥터
중급 사냥터
고급 사냥터
로 가는 선택지를 보여준다.
초급의 몬스터는 Min 5 Max 15 Hp 100
중급의 몬스터는 Min 10 Max 20 Hp 200
고급의 몬스터는 Min 20 Max 40 Hp 300
초중고급 몬스터의 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;
}
}
}
플레이어가 싸움에서 이기고나면 실행하는 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);
}
}
레벨과 경험치를 렌더링하는 것은 플레이어만 필요하므로 StatusRenderStart()함수를 StatusUnit클래스에서 virtual로 만들고, Player클래스에서 override해서 플레이어에서만 렌더링하도록 했다.
void Player::StatusRenderStart() {
printf_s("레벨 %d\n", Level);
printf_s("경험치 %d/%d\n", TotalExp, LevelUpExp);
}
몬스터는 싸움을 시작할 때 체력을 초기화하고 랜덤골드(보상)을 설정한다.
void Monster::FightStart(FightUnit& _Other) {
HpReset();
AddGold(RandomValue(5000,10000)); //랜덤골드
}
몬스터가 싸움에서 지고나면 랜덤 경험치를 설정한다. (이걸 플레이어가 받아가게 됨)
void Monster::FightEnd(FightUnit& _Other) {
// 몬스터가 싸움 끝난 후 해야 할 일
// 랜덤하게 경험치를 정해야 한다.
// 1000 ~ 2000 사이의 경험치 계산
_Other.SetExp(RandomValue(1000, 2000));
}
플레이어와 몬스터의 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;
}
}