[TIL] 24-01-02

Lev·2024년 1월 2일
0

📓TIL Archive

목록 보기
15/33

Text RPG

  • 플레이어 레벨과 경험치 구현
  • 렌더에 다형성 적용
  • 전투 전후 과정에 다형성 적용
// [TextRPG.cpp]
#include <iostream>
#include "Player.h"
#include "FightZone.h"
#include "Town.h"
#include <conio.h>

int main()
{
	Player NewPlayer = Player();
	NewPlayer.SetName("Player");

	/*
	<과제1>
	난이도별 FightZone 3개 만들기
	- 초급 사냥터 => 몬스터 Min 5 max 15 Hp 100
	- 중급 사냥터 => 몬스터 Min 10 max 20 Hp 200
	- 고급 사냥터 => 몬스터 Min 20 max 40 Hp 300
	
	"1. 마을.\n";
	"2. 초급 사냥터.\n";
	"3. 중급 사냥터.\n";
	"4. 고급 사냥터.\n";
	*/

	Town NewTownZone;

	FightZone NewFightZone0(5, 15, 100);
	FightZone NewFightZone1(10, 20, 200);
	FightZone NewFightZone2(20, 40, 300);

	// 메인 메뉴
	while (true)
	{
		NewPlayer.StatusRender();
		printf_s("어디로 가시겠습니까?\n");
		printf_s("1. 마을.\n");
		printf_s("2. 초급 사냥터.\n");
		printf_s("3. 중급 사냥터.\n");
		printf_s("4. 고급 사냥터.\n");
		int Select = _getch();

		switch (Select)
		{
		case '1':
			NewTownZone.In(NewPlayer);	// 플레이어를 마을에 넣기
			break;
		case '2':
			NewFightZone0.In(NewPlayer);	// 플레이어를 초급 사냥터에 넣기
			break;
		case '3':
			NewFightZone1.In(NewPlayer);	// 플레이어를 중급 사냥터에 넣기
			break;
		case '4':
			NewFightZone2.In(NewPlayer);	// 플레이어를 고급 사냥터에 넣기
			break;
		default:
			break;
		}

		/*
		[StatusUnit.h]
		void StatusInit(int _Hp, int _MinAtt, int _MaxAtt)
		{	
			SetMaxHp(_Hp);
			SetHp(_Hp);
			SetMinAtt(_MinAtt);
			SetMaxAtt(_MaxAtt);
		}

		[TextRPG.cpp]
		FightZone NewFightZone[3];
		NewFightZone[0].NewMonster.StatusInit(100, 5, 15);
		NewFightZone[1].NewMonster.StatusInit(200, 10, 20);
		NewFightZone[2].NewMonster.StatusInit(300, 20, 40);

		switch (Select)
		{
		case '2':
		case '3':
		case '4':
		{
			int FightSelect = Select - '2';
			NewFightZone[FightSelect].In(NewPlayer);
			break;
		}

		=> 이런 식으로 배열을 이용해 구현해도 된다
		*/

		system("cls");
	}
}
// [FightZone.h]
#pragma once
#include "Monster.h"
#include "ZoneBase.h"

class FightZone : public ZoneBase
{
public:
	FightZone();

	FightZone(const int _MinAtt, const int _MaxAtt, const int _Hp);	// <1>
	// 조건이 다른 FightZone들을 초기화(실제론 대입에 가깝지만)하기 위해 새로운 생성자를 만들었다

	void In(class Player& _Player);

protected:
	Monster NewMonster = Monster();

	bool FightLogic(FightUnit& _First, FightUnit& _Last, FightUnit& _Top, FightUnit& _Bot);
};
// [FightZone.cpp]
#include "FightZone.h"
#include <conio.h>
#include <iostream>
#include "Player.h"

FightZone::FightZone()
{
	NewMonster.SetName("Monster");
}

FightZone::FightZone(const int _MinAtt, const int _MaxAtt, const int _Hp)	// <1>
{
	NewMonster.SetName("Monster");
	NewMonster.SetMinAtt(_MinAtt);
	NewMonster.SetMaxAtt(_MaxAtt);
	NewMonster.SetHp(_Hp);
	NewMonster.SetMaxHp(_Hp);
}

void FightZone::In(Player& _Player)
{
	system("cls");

	NewMonster.FightStart(_Player);	// <3>
	_Player.FightStart(NewMonster);	// <3>

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

		bool IsEnd = false;

		if (_Player.GetRSpeed() >= NewMonster.GetRSpeed())
		{
			printf_s("%s의 선공\n", _Player.GetName());
			IsEnd = FightLogic(_Player, NewMonster, _Player, NewMonster);
		}
		else {
			printf_s("%s의 선공\n", NewMonster.GetName());
			IsEnd = FightLogic(NewMonster, _Player, _Player, NewMonster);
		}

		if (IsEnd == true)
		{
			if (NewMonster.IsDeath() == true)
			{
				NewMonster.FightEnd(_Player);	// <3>
				_Player.FightEnd(NewMonster);	// <3>

				int Input = _getch();
			}

			return;
		}
	}
}

bool FightZone::FightLogic(FightUnit& _First, FightUnit& _Second, FightUnit& _Top, FightUnit& _Bot)
{
	{
		int Input = _getch();
	}
	system("cls");

	_Second.DamageLogic(_First);
	_Top.StatusRender();
	_Bot.StatusRender();
	_First.DamageRender();
	if (_Second.IsDeath() == true)
	{
		printf_s("게임 종료\n");
		int Input = _getch();
		return true;
	}

	{
		int Input = _getch();
	}
	system("cls");

	_First.DamageLogic(_Second);
	_Top.StatusRender();
	_Bot.StatusRender();
	_First.DamageRender();
	_Second.DamageRender();
	if (_First.IsDeath() == true)
	{
		printf_s("게임 종료\n");
		int Input = _getch();
		return true;
	}

	{
		int Input = _getch();
	}
	system("cls");

	return false;
};
// [StatusUnit.h]
#pragma once
#include "NameUnit.h"

class StatusUnit : public NameUnit
{
public:
	// Get 함수
	inline int GetHp() const
	{
		return Hp;
	}

	inline int GetMaxHp() const
	{
		return MaxHp;
	}

	inline int GetGold() const
	{
		return Gold;
	}

	inline int GetExp() const	// <2>
	{
		return Exp;
	}

	// Set? 함수
	inline void SetHp(int _Hp)
	{
		if (_Hp > MaxHp)
		{
			_Hp = MaxHp;
		}
		Hp = _Hp;
	}

	inline void SetMaxHp(const int _MaxHp)
	{
		MaxHp = _MaxHp;
	}

	inline void HpReset()
	{
		Hp = MaxHp;
	}

	inline void SetMinAtt(const int _MinAtt)
	{
		MinAtt = _MinAtt;
	}

	inline void SetMaxAtt(const int _MaxAtt)
	{
		MaxAtt = _MaxAtt;
	}

	inline void AddGold(const int _Gold)
	{
		Gold += _Gold;
	}

	inline void AddExp(const int _Exp)	// <2>
	{
		Exp += _Exp;
	}

	// 난수를 계산해주는 함수 (Gold와 Exp 계산에 사용)
	int Random(int _Min, int _Max);	// <2>

	/*
	<과제4>
	StatusRender()를 세 부분으로 나누고, 다형성 적용하기
	- Player는 레벨과 경험치를 추가로 보여줘야 한다
	*/
	void StatusRender();

	virtual void StatusRenderStart();	// <4>

	virtual void StatusRenderBase();	// <4>

	virtual void StatusRenderEnd();	// <4>

protected:
	int Hp = 100;
	int MaxHp = 100;
	int MinAtt = 10;
	int MaxAtt = 20;
	int Speed = 20;
	int Gold = 0;
	int Exp = 0;	// <2>
};
// [StatusUnit.cpp]
#include "StatusUnit.h"
#include <iostream>
#include <random>

int StatusUnit::Random(int _Min, int _Max)
{
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_int_distribution<> dist(_Min, _Max);
	
	return dist(gen);
}

void StatusUnit::StatusRender()
{
	int Size = printf_s("%s", Name);

	for (int i = 0; i < 50 - Size; i++)
	{
		int a = 0;
		printf_s("-");
	}

	printf_s("\n");
	StatusRenderStart();	// <4>
	StatusRenderBase();	// <4>
	StatusRenderEnd();	// <4>

	for (int i = 0; i < 50; i++)
	{
		printf_s("-");
	}

	printf_s("\n");
}

void StatusUnit::StatusRenderStart()	// <4>
{

}

void StatusUnit::StatusRenderBase()	// <4>
{
	printf_s("공격력 %d ~ %d\n", MinAtt, MaxAtt);
	printf_s("체력 %d / %d\n", Hp, MaxHp);
	printf_s("소지금 %d\n", Gold);
}
void StatusUnit::StatusRenderEnd()	// <4>
{

}
// [FightUnit.h]
#pragma once
#include "StatusUnit.h"

class FightUnit : public StatusUnit
{
public:
	// Get 함수
	inline int GetMinAtt() const
	{
		return MinAtt;
	}

	inline int GetMaxAtt() const
	{
		return MaxAtt;
	}

	inline bool IsDeath()
	{
		return Hp == 0;
	}

	// 난수로 공격값과 선공을 정하기 위한 함수
	virtual int GetRAtt();

	int GetRSpeed() const;

	// Fight용 함수
	void DamageLogic(FightUnit& _Unit);

	void DamageRender();

	/*
	<과제3>
	Player와 Monster의 전투 전후로 진행되는 요소를 함수로 만들고, 다형성 적용하기
	- Player는 전투 후에 골드와 경험치를 얻고, 레벨업을 해야할지 결정한다
	- Monster는 전투 전에 본인이 소지할 골드와 경험치를 계산한다
	*/
	virtual void FightStart(FightUnit& _Other)	// <3>
	{

	}

	virtual void FightEnd(FightUnit& _Ohter)	// <3>
	{

	}

protected:
	int CurAtt = 0;
};
// [Player.h]
#pragma once
#include "FightUnit.h"
#include "Weapon.h"

class Player : public FightUnit
{
public:
	Player();

	Weapon Weapon;

	void FightEnd(FightUnit& _Other) override;	// <3>

	void StatusRenderStart() override;	// <4>

	/*
	<과제2>
	Player의 레벨과 경험치 구현하기
	- Exp가 LevelUpExp를 넘어설 경우 레벨업
	- 레벨업 시 공격력, 체력 상승
	*/
	void LevelUp();	// <2>

protected:
	int GetRAtt() override;

private:
	int Level = 1;	// <2>
	int LevelUpExp = 100;	// <2>
};
// [Player.cpp]
#include "Player.h"
#include <iostream>
#include <conio.h>

Player::Player()
{
	Weapon.SetName("Steel Sword");
	Weapon.SetAtt(10);
}

void Player::FightEnd(FightUnit& _Other)	// <3>
{
	int Exp = _Other.GetExp();	// 몬스터가 준 경험치
	AddExp(Exp);
	printf_s("%d만큼의 경험치를 얻었다!\n", Exp);

	int Input = _getch();
	
	if (GetExp() >= LevelUpExp)	// 가지고 있는 경험치와 요구 경험치 비교
	{
		LevelUp();	// <2>
	}

	int Gold = _Other.GetGold();	// 몬스터가 준 골드
	AddGold(Gold);
	printf_s("%d만큼의 골드를 벌었다!\n", Gold);
}

void Player::StatusRenderStart()	// <4>
{
	printf_s("레벨 %d (%d / %d)\n", Level, Exp, LevelUpExp);
	// Monster와 달리 Level과 Exp 표시
}

void Player::LevelUp()	// <2>
{
	while (GetExp() >= LevelUpExp)	// 한꺼번에 여러번 레벨업 할 경우 대비
	{
		// 레벨 업
		++Level;

		// 경험치 소모, 요구 경험치 늘리기
		Exp -= LevelUpExp;
		LevelUpExp += 100;

		// 체력 증가
		MaxHp += 100;
		HpReset();

		// 공격력 증가
		MinAtt += 10;
		MaxAtt += 10;

		printf_s("레벨 업! %d -> %d\n", Level - 1, Level);

		int Input = _getch();
	}
}

int Player::GetRAtt()
{
	CurAtt = FightUnit::GetRAtt() + Weapon.GetAtt() + Weapon.GetEquipUp();
	return CurAtt;
}
// [Monster.h]
#pragma once
#include "FightUnit.h"

class Monster : public FightUnit
{
public:
	void FightStart(FightUnit& _Other) override;	// <3>
};
// [Monster.cpp]
#include "Monster.h"

void Monster::FightStart(/*Monster* const this, */FightUnit& _Player)	// <3>
{
	/*this->*/HpReset(/*this*/);

	// Player에게 얼마만큼의 골드와 경험치를 줄 지 미리 난수로 계산해둔다
	int Gold = Random(1000, 10000);
	AddGold(Gold);

	int Exp = Random(50, 100);
	AddExp(Exp);
}

포인터 (이어서)

함수 포인터 Function Pointer

💡 잊지 말자!
프로그램의 모든 것은 위치, 크기, 형태, 값을 가지고 있다.
함수도 마찬가지이다.

#include <iostream>

int Test()
{
	return 10;
}

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

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

class Button
{
public:
	void (*Function)() = nullptr;
	int Image;
	int Color;
	int ScreenPos;

	virtual void Click()
	{
		Function();	// 콜백 방식
	}
};

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

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

int main()
{
	{
		int (*Ptr)() = Test;	// [함수 포인터] 선언 => (자료형) (*변수명)()
		// 포인터의 자료형이 본래 함수의 자료형과 같아야 한다

		Ptr();
		// 함수의 포인터이기 때문에 함수 사용하는 방법 그대로 사용 가능하다

		sizeof(void(*)());	// 8byte
		// 변수명을 삭제하면 그대로 함수포인터자료형이 된다
	}

	// 함수포인터 문법은 UI 등지에서 가장 많이 사용된다
	{
		// 모든 버튼마다 새로운 클래스를 만들어주는 방식 (X)
		// - 버튼마다 클래스를 만드는 것이 번거로움
		// - 일부만 다른 클래스가 너무 많이 생성됨
		MoveButton MButton;
		AttackButton AButton;

		MButton.Click();
		AButton.Click();
	}
	{
		// 버튼의 기능별로 클릭했을 때 실행되는 함수를
		// 포인터가 각각 다르게 가리키고 있는 방식 (O)
		Button MoveButton;
		Button AttButton;

		MoveButton.Function = PlayerMove;
		AttButton.Function = PlayerAttack;

		MoveButton.Click();
		AttButton.Click();
	}
}
profile
⋆꙳⊹⋰ 𓇼⋆ 𝑻𝑰𝑳 𝑨𝑹𝑪𝑯𝑰𝑽𝑬 ⸝·⸝⋆꙳⊹⋰

0개의 댓글