Notice
Recent Posts
Recent Comments
Link
오븐 노트
[C++] TextRPG #3 본문
#include <iostream>
using namespace std;
// main
// - EnterLobby (PlayerInfo)
// -- CreatePlayer
// -- EnterGame (MonsterInfo)
// --- CreateMonsters
// --- EnterBattle
enum PlayerType
{
PT_Knight = 1,
PT_Archer = 2,
PT_Mage = 3,
};
enum MonsterType
{
MT_Slime = 1,
MT_Orc = 2,
MT_Skeleton = 3,
};
struct StatInfo
{
int hp = 0;
int attack = 0;
int defence = 0;
};
void EnterLobby();
void PrintMessage(const char* msg);
void CreatePlayer(StatInfo* playerInfo);
void PrintStatInfo(const char* name, const StatInfo& info); // info의 경우 pointer로 사용해도 별다른 문제는 없지만 데이터량이 커질 경우 복사에 의해서 성능 저하가 발생할 수 있기에 참조로 넘긴다면 주소를 이용하기에 성능상 단순 복사보다 우위에 있다.
void EnterGame(StatInfo* playerInfo);
void CreateMonsters(StatInfo monsterInfo[], int count);
bool EnterBattle(StatInfo* playerInfo, StatInfo* monsterInfo);
int main()
{
srand((unsigned)time(nullptr));
EnterLobby();
return 0;
}
void EnterLobby()
{
while (true)
{
PrintMessage("로비에 입장했습니다");
// PLAYER!
// 이전 textRPG의 경우 전역 메모리에 info를 생성했지만 지금은 스택 메모리에 생성 했다는 차이가 있다.
// 스택의 주소를 위부로 발설하는 것은 매우 위험한 행동이지만, 내부에서는 유효하기에 다음 함수로 넘기는것은 안전하다.
StatInfo playerInfo;
CreatePlayer(&playerInfo);
PrintStatInfo("Player", playerInfo);
EnterGame(&playerInfo);
}
}
void PrintMessage(const char* msg)
{
cout << "************************" << endl;
cout << msg << endl;
cout << "************************" << endl;
}
void CreatePlayer(StatInfo* playerInfo)
{
bool ready = false;
while (ready == false)
{
PrintMessage("캐릭터 생성창");
PrintMessage("[1] 기사 [2] 궁수 [3] 법사");
cout << "> ";
int input;
cin >> input;
switch (input)
{
case PT_Knight:
playerInfo->hp = 100;
//(*playerInfo).hp = 100; 와 같다
playerInfo->attack = 10;
playerInfo->defence = 5;
ready = true;
break;
//return; 시 ready 필요 없이 바로 함수 종료되지만 일반적인 문법으로 사용함
case PT_Archer:
playerInfo->hp = 80;
playerInfo->attack = 15;
playerInfo->defence = 3;
ready = true;
break;
case PT_Mage:
playerInfo->hp = 50;
playerInfo->attack = 25;
playerInfo->defence = 3;
ready = true;
break;
}
}
}
void PrintStatInfo(const char* name, const StatInfo& info)
{
cout << "*********************" << endl;
cout << name << " : HP = " << info.hp << " ATT = " << info.attack << " DEF = " << info.defence << endl;
cout << "*********************" << endl;
}
void EnterGame(StatInfo* playerInfo)
{
const int MONSTER_COUNT = 2;
PrintMessage("게임에 입장했습니다");
while (true)
{
StatInfo monsterInfo[MONSTER_COUNT];
// 해당 스택에서만 유효한 정보이므로 EnterGame 밖으로 빠져나가게끔 주소 전달은 불가하다. 하지만 몬스터의 경우 Game 내부에서만 유효하고 Lobby까지 오지는 않으므로 일단 이렇게 사용
CreateMonsters(monsterInfo, MONSTER_COUNT);
// 스택 메모리의 생명 주기(라이프 사이클)를 항상 생각해야한다.
for (int i = 0; i < MONSTER_COUNT; i++)
{
PrintStatInfo("Monster", monsterInfo[i]);
// 해당 변수에서는 포인터가 아닌 const 참조를 받도록 해두었기에 monsterInfo[i]의 주소를 넘겨주는것이 아닌 바구니 자체를 넘겨주는 형태로 제작함
}
PrintStatInfo("Player", *playerInfo);
// 해당 변수에서 StatInfo를 참조값으로 받고 있지만 EnterGame에서 playerInfo를 포인터로 받고 있었음
// 그저 주소값일뿐이기에 해당 주소로 이동하기 위한 *를 추가하여야한다.
// 포인터는 무조건 주소를 넘겨주어야하고 참조 형태는 마치 복사를 하듯 유도를 해줘야함
PrintMessage("[1] 전투 [2] 전투 [3] 도망");
int input;
cin >> input;
if (input == 1 || input == 2)
{
int index = input - 1;
bool victory = EnterBattle(playerInfo, &monsterInfo[index]);
// playerInfo는 현재 함수의 매개변수에서 포인터값으로 받고 있기에 그대로 전달
// 현재 사용중인 monsterInfo는 배열. monsterInfo라는 이름은 주소값을 가리킨다. 사실상 포인터와 같은 느낌이다.
// 하지만 배열에서 특정 순서를 전달하기 위해서 [index]를 넣게 되면 주소값이 아닌 값을 찾게됨.
// monsterInfo[index]의 형태로 사용하게 되면 결국 주소값이 아닌 배열의 index번째에 저장된 StatInfo 값을 찾아오기 때문에 주소값을 지칭하는 &를 붙여야함.
if (victory == false)
break;
}
}
}
void CreateMonsters(StatInfo monsterInfo[], int count)
{
for (int i = 0; i < count; i++)
{
int randValue = 1 + rand() % 3;
switch (randValue)
{
case MT_Slime:
monsterInfo[i].hp = 30;
monsterInfo[i].attack = 5;
monsterInfo[i].defence = 1;
// 1차 포인터와 1차 배열은 완전히 호환이 가능하므로 매개변수로 받은 포인터를 배열 형식으로 사용 가능하다.
break;
case MT_Orc:
monsterInfo[i].hp = 40;
monsterInfo[i].attack = 8;
monsterInfo[i].defence = 2;
break;
case MT_Skeleton:
monsterInfo[i].hp = 50;
monsterInfo[i].attack = 15;
monsterInfo[i].defence = 3;
break;
}
}
}
bool EnterBattle(StatInfo* playerInfo, StatInfo* monsterInfo)
{
while (true)
{
int damage = playerInfo->attack - monsterInfo->defence;
if (damage < 0)
damage = 0;
monsterInfo->hp -= damage;
if (monsterInfo->hp < 0)
monsterInfo->hp = 0;
PrintStatInfo("Monster", *monsterInfo);
// const 참조로 받고있는데 현재 함수에는 포인터로 받고있기에 해당 주소값으로 들어가는 *를 추가
if (monsterInfo->hp == 0)
{
PrintMessage("몬스터를 처치했습니다");
return true;
}
damage = monsterInfo->attack - monsterInfo->defence;
if (damage < 0)
damage = 0;
playerInfo->hp -= damage;
if (playerInfo->hp < 0)
playerInfo->hp = 0;
PrintStatInfo("Player", *playerInfo);
// 참조값은 실질적인 포인터가 아닌 내부에 저장된 값 자체의 복사값으로 넘겨줘야함
if (playerInfo->hp == 0)
{
PrintMessage("Game Over");
return false;
}
}
}
[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 - 인프런 | 강의
C++ 카테고리의 글은 인프런 Rookiss님의 강의를 공부하며 정리하는 내용입니다.
이미 알고 있는 내용도 다시 정리 되어있을 수 있습니다.
모든 글은 제가 공부하기 위해 작성합니다.
'Develop > C++' 카테고리의 다른 글
[C++] 연습문제 (달팽이) (0) | 2022.08.22 |
---|---|
[C++] 연습 문제 (문자열) #1 ~ 2 (0) | 2022.08.16 |
[C++] 포인터 마무리 (0) | 2022.07.15 |
[C++] 다차원 배열 (0) | 2022.07.15 |
[C++] 다중 포인터 (0) | 2022.07.14 |