티스토리 뷰

██████████████████████████████████████████████████
██████████████████████████████████████████████████
███████████▓██████████████████████████████████████
██████████████████████████████████████████████████
██████████████████████████████████████████████████
██████████████████████████████████████████████████
█████████▓▒███████████████████████████████████░░██
█████████░░███████████████████████████████████░░██
███████▓░░░███████████████████░████████████░▓░░░▓█
███████░░░░████████████████░░▒░███████████─░░░░░░█
███████░░░░─█████████████▒░░░░▓███████████░░░░░░▒█
███████░░░░─████████████░░░░░░███████████░░░░░░░▒▓
███████░░░░█░░░░████░▓░░░░░░░▓█████████░░░░░░░░░░░
███████░░░▒▒░░░░▒▒░█░░░░░░░░░██████████░░░░░░░░░░░
███████░░░░░░░░░░░░░░░░░░░░░▒█████████░░░░░░░░░░░▓
██████▓░░░░░░░▓█▓░░░░░░░░░░█████████▒░░░░░░░░░░░░█
█████▓░░░░░░░▒▒▒▓░░░░░░░▒██████████░░░░░░░░░░░░░░▒
████░░▒░░░░░░▒██▓░░░░░░████████████░░░░░░░░░░░░░░░
██████▒░░░░░░░░░░░▓░░░░▒██████████░░░░░░░░░░░░░▒▓─
████░█▒░░░░░░░░░░░░░░░░░█████████░░░░░░░░░░░░░░███
████░░░░░░░▒▓░░░▒░░░▒░░░░███████▓▒░░░░░░░░░░░▓████
████░░░░░▒▒▒▒░░░▓░░░▒░░░░▒██████▒░░░░░░░░░░▒░█████
████░░░░░▓▒▒▓░░░░░░░░░░░░░███████░░░░░░░░░░███████
████░░░░░░▒▒░░░░▒▒░▓░░░░░▓████████░░░░░░░█████████
█████░░░░░▒░░░░░░░░░░░░░░░░███████▒░░░░▓██████████
████▒░░░░░▒░░░░░░░░░░░▒▒░░░████████░░░████████████
█████░▓░░░░░░░░░░░░░▒░░░░░░████████▓░░████████████
█████░░█░░░░░░░░░░░░▓░░▓░░░▓▓█████░▒░░████████████
██░░▒░░░░░░░░░░░░░░░▒░░░░░░░▓████▒░▒▒▒████████████
█▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░█▒▒▒▒▒▒▒████████████
▓▒▒░░░░░░░░░░░░░░░░░▓░░░░░░░░▓█▒▒▒▒▒█▒████████████
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒█▒▒▒▒██████████████
░░░▒░░░░░░░░░░░░░░░░░░░░░░░░░░░▓█▒▒▒▒█████████████
████░░░░▒▒░░░░░░░░░░░░░░░░░░░░░███▓▒▒▓████████████
██████████░░░░░░░░░░░░░░░░░░░░░░░█▒▒▒█████████████
██████████░░░░░░░░░░░░░░░░░░░░░░▒▓▒▓▓█████████████
██████████░░░░░░░░░░░░░░░░░░░░░░░▒▒███████████████
███████████░░░░░░░░░░░░░░░░░░░░░░▒▒███████████████
███████████░░░░░░░░░░░░░░░░░░░░░░▓▒▒██████████████
███████████░░░░░░░░░░░░░░░░░░░░░░█████████████████
███████████░░░░░░░░░░░░░░░░░░░░░░▓████████████████
███████████░░░░░░░░░░░░░░░░░░░░░░░████████████████
████████████░░░░░░░░░░░░░░░░░░░░░░████████████████
████████████▓▒░░░░░▒░▓▓░▒▒░░░░░░▓█████████████████
█████████████▓▓░░▒▒██████▒░▒▓▓▒▒▓█████████████████
███████████████░▒▒▒███████████▒▒▓█████████████████
███████████████▒▒░░███████████▒▒█▓████████████████
████████████████▓▒▒████████████▒░▒████████████████
███████████████████████████████░░█████████████████
██████████████████████████████████████████████████

 

학원의 수업 커리큘럼은 C++ 공부부터 시작했다.

비주얼 스튜디오를 사용해 콘솔 앱으로 간단한 프로그램부터 게임을 만들어 보았다.

구조체와 클래스를 사용해 몬스터를 생성하고

공격하여 체력을 0으로 만들고 쓰러트리는 그런 텍스트 들을 출력했다.

유니티 엔진 사용전에 언어를 익히고

게임 로직 구현에 대한 훈련을 위한 훈련 같았다.

 

이번엔 텍스트 RPG를 만들기로 했는데,

그 전 과제가 내가 잘 모르는 플레잉 카드 게임이어서 어려웠던 점을 고려해

포켓몬 게임의 로직을 구현하는 데 목표로 했다.

 


 

 

아래는 코드 전문.

cpp 파일 하나에 전부 작성해버렸다;

하루 만에 완성하느라 두서 없이 코드를 마구 짰지만 제대로 작동한다~

 

#include <iostream>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <ctime>

using namespace std;

const int memberNumber = 6;

enum class type {
	None = 0,
	normal,
	fire,
	water,  
	grass,
	electr,
	ice,
	fight,
	poison,
	ground,
	flying,
	psychc,
	bug,
	rock,
	ghost,
	dragon,
	dark,
	steel,
	fairy
};

struct skill {
	string name = "";
	type atkType = type::None;
	string typeStr = "";
	int damage = 0;
	int accuracy = 0;
	int spellSpeed = 0;
	int maxPoint = 0;
	int balancePoint = 0;

	bool attackType = false; //0 물리 1 특수
};

struct Monster {
	string name;
	string ownerName;
	int maxHp;
	int balanceHp;
	int atk; //물리 공격력
	int def; //물리 방어력
	int spa; //특수 공격력
	int spd; //특수 방어력
	int spe; //속도
	type type_1;
	type type_2;
	skill skills[4];
	bool isAlive = true;
	int choiceSkill = 0;
};

class Trainer {
public:
	string name;
	Monster monsters[6];
	int inbattle = 0;
};


skill VoltTackle{ "볼트 태클", type::electr,"전기",120, 100,0,15, 15,0 };
skill QuickAttack{ "전광석화",type::normal,"노멀",40,100,1,30,30,0 };
skill IronTail{ "아이언테일",type::steel,"강철",100,75,0,15,15,0 };
skill BrickBreak{ "깨트리기",type::fight,"격투",75,100,0,15,15,0 };
skill Earthquake{ "지진",type::ground,"땅",100,100,0,10,10,0 };
skill  LeafStorm{ "리프스톰",type::grass,"풀", 130,90,0,5,5,1 };
skill SludgeBomb{ "오물폭탄",type::poison,"독", 90,100,0,10,10,1 };
skill BodySlam{ "누르기",type::normal, "노멀",85,100,0,15,15,0 };
skill  FireBlast{ "불대문자",type::fire,"불",110,85,0,5,5,1 };
skill AirSlash{ "에어슬래시",type::flying,"비행", 75,95,0,15,15,1 };
skill FocusBlast{ "기합구슬",type::fight, "격투",120,70,0,5,5,1 };
skill DragonPulse{ "용의파동",type::dragon, "드래곤",85,100,0,10,10,1 };
skill  HydroPump{ "하이드로펌프",type::water, "물",110,80,0,5,5,1 };
skill FlashCannon{ "러스터캐논",type::steel, "강철",80,100,0,10,10,1 };
skill DarkPulse{ "악의파동",type::dark,"악", 80,100,0,15,15,1 };
skill  IceBeam{ "냉동빔",type::ice, "얼음",90,100,0,10,10,1 };
skill Blizzard{ "눈보라",type::ice,"얼음", 110,70,0,5,5,1 };
skill IceShard{ "얼음뭉치",type::ice,"얼음", 40,100,1,30,30,0 };
skill Psychic{ "사이코키네시스",type::psychc,"에스퍼", 90,100,0,10,10,1 };
skill Surf{ "파도타기",type::water, "물",90,100,0,15,15,1 };
skill Crunch{ "깨물어부수기",type::dark,"악", 80,100,0,15,15,0 };
skill  HeavySlam{ "헤비봄버",type::steel,"강철", 160,100,-1,10,10,0 };
skill HighHorsepower{ "10만마력",type::ground,"땅", 95,95,0,10,10,0 };
skill ShadowBall{ "섀도볼",type::ghost,"고스트", 80,100,0,15,15,1 };
skill RockSlide{ "스톤샤워",type::rock,"바위", 75,90,0,10,10,0 };
skill  IronHead{ "아이언헤드",type::steel,"강철", 80,100,0,15,15,0 };
skill  ExtremeSpeed{ "신속",type::normal,"노말", 80,100,1,5,5,0 };
skill FlareBlitz{ "플레어드라이브",type::fire,"불", 120,100,0,15,15,0 };
skill ThunderFang{ "번개엄니",type::electr,"전기",65 ,95,0,15,15,0 };
skill Bulldoze{ "땅고르기",type::ground, "땅",60,100,0,20,20,0 };
skill AquaTail{ "아쿠아테일",type::water,"물",90,90,0,10,10,0 };
skill  IceFang{ "얼음엄니",type::ice,"얼음", 65,95,0,15,15,0 };
skill PoisonJab{ "독찌르기",type::poison,"독",80 ,100,0,20,20,0 };
skill  KnockOff{ "탁쳐서떨구기",type::dark, "악",65,100,0,20,20,0 };
skill CrossChop{ "크로스촙",type::fight, "격투",100,80,0,5,5,0 };

Monster pikachu = { "피카츄","레드",35,35,55,40,50,50,90,type::electr,type::None, {VoltTackle ,QuickAttack ,IronTail ,BrickBreak}, true ,0 };
Monster venusaur = { "이상해꽃","레드",80,80,82,83,100,100,80,type::grass,type::poison,{ Earthquake ,LeafStorm ,SludgeBomb ,BodySlam },true ,0 };
Monster charizard = { "리자몽","레드",78,78,84,78,109,85,100,type::fire,type::flying,{ FireBlast ,AirSlash ,FocusBlast ,DragonPulse },true ,0 };
Monster blastoise = { "거북왕","레드",79,79,83,100,85,105,75,type::water,type::None,{ HydroPump ,FlashCannon ,DarkPulse ,IceBeam },true ,0 };
Monster lapras = { "라프라스","레드",130,130,85,80,85,95,60,type::water,type::ice ,{ Blizzard ,IceShard ,Psychic ,Surf },true ,0 };
Monster snorlax = { "잠만보","레드",160,160,110,65,65,65,30,type::normal,type::None,{ Crunch ,HeavySlam ,HighHorsepower ,BodySlam },true ,0 };

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Monster alakazam = { "후딘 ","그린",55,55,50,45,135,95,120,type::psychc,type::None,{Psychic ,ShadowBall ,ShadowBall ,Psychic},true ,0 };
Monster aerodactyl = { "프테라","그린",80,80,105,65,60,75,130,type::rock,type::flying,{ RockSlide ,Crunch ,IronHead ,Crunch },true ,0 };
Monster arcanine = { "윈디","그린",90,90,110,80,100,80,95,type::fire,type::None,{ ExtremeSpeed ,FlareBlitz ,ThunderFang ,Bulldoze },true ,0 };
Monster gyarados = { "갸라도스","그린",95,95,125,79,60,100,81,type::water,type::flying,{ AquaTail ,Crunch ,IceFang ,Earthquake },true ,0 };
Monster machamp = { "괴력몬","그린",90,90,130,80,65,85,55,type::fight,type::None,{ PoisonJab ,KnockOff ,CrossChop ,RockSlide },true ,0 };
Monster exeggutor = { "나시","그린",95,95,95,85,125,75,55,type::grass,type::psychc,{ SludgeBomb ,Psychic ,LeafStorm ,BodySlam },true ,0 };


Trainer red = { "레드", {pikachu,venusaur,charizard,blastoise,lapras,snorlax},  100 };
Trainer green = { "그린", {alakazam,aerodactyl,arcanine,gyarados,machamp,exeggutor}, 100 };


//타입 상성표
float typeWin[19][19] =
{
	{1,		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1}, // None
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	{1,		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,.5f,	0,	1,	1,.5f,	1}, //노말 1
	{1,		1,.5f,.5f,	2,	1,	2,	1,	1,	1,	1,	1,	2,.5f,	1,.5f,	1,	2,	1}, // 불꽃 2
	{1,		1,	2,.5f,.5f,	1,	1,	1,	1,	2,	1,	1,	1,	2,	1,.5f,	1,	1,	1}, // 물 3
	{1,		1,.5f,	2,.5f,	1,	1,	1,.5f,	2,.5f,	1,.5f,	2,	1,.5f,	1,.5f,	1}, // 풀 4
	{1,		1,	1,	2,.5f,.5f,	1,	1,	1,	0,	2,	1,	1,	1,	1,.5f,	1,	1,	1}, // 전기 5
	{1,		1,.5f,.5f,	2,	1,.5f,	1,	1,	2,	2,	1,	1,	1,	1,	2,	1,.5f,	1}, // 얼음 6
	{1,		2,	1,	1,	1,	1, 2,	1,.5f,	1,.5f,.5f,.5f,	2,	0,	1,	2,	2,.5f}, // 격투 7
	{1,		1,	1,	1,	2,	1,	1,	1,.5f,.5f,	1,	1,	1,.5f,.5f,	1,	1,	0,	2}, // 독 8
	{1,		1,	2,	1,.5f,	2,	1,	1,	2,	1,	0,	1,.5f,	2,	1,	1,	1,	2,	1}, // 땅 9
	{1,		1,	1,	1,	2,.5f,	1,	2,	1,	1,	1,	1,	2,.5f,	1,	1,	1,.5f,	1}, // 비행 10
	{1,		1,	1,	1,	1,	1,	1,	2,	2,	1,	1,.5f,	1,	1,	1,	1,	0,.5f,	1}, // 에스퍼 11
	{1,		1,.5f,	1,	2,	1,	1,.5f,.5f,	1,.5f,	2,	1,	1,.5f,	1,	2,.5f,.5f}, // 벌레 12
	{1,		1, 2,	1,	1,	1,	2,.5f,	1,.5f,	2,	1,	2,	1,	1,	1,	1,.5f,	1}, // 바위 13
	{1,		0,	1,	1,	1,	1,	1,	1,	1,	1,	1,	2,	1,	1,	2,	1,.5f,	1,	1}, // 고스트 14
	{1,		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	2,	1,.5f,	0}, // 드래곤 15
	{1,		1,	1,	1,	1,	1,	1,.5f,	1,	1,	1,	2,	1,	1,	2,	1,.5f,	1,.5f}, // 악 16
	{1,		1,.5f,.5f,	1,.5f,	2,	1,	1,	1,	1,	1,	1,	2,	1,	1,	1,.5f,	2}, // 강철 17
	{1,		1,.5f,	1,	1,	1,	1,	2,.5f,	1,	1,	1,	1,	1,	1,	2,	2,.5f,	1}, // 페어리 18
};

void showScreen();
int hpBar(Monster _monster);
void caculation(Monster& _attacker, Monster& _hitted, skill _skill) //공격자, 피격자, 기술
{
	float typeBonus = 1;

	if (((int)(_skill.atkType) == (int)(_attacker.type_1)) || ((int)(_skill.atkType) == (int)(_attacker.type_2)))
	{
		typeBonus *= 1.5f;
	}

	float effetive = (typeWin[(int)(_skill.atkType)][(int)(_hitted.type_1)]) * (typeWin[(int)(_skill.atkType)][(int)(_hitted.type_2)]);
	typeBonus *= effetive;

	//~의 (기술명)!
	showScreen();
	std::cout << _attacker.name << "의 " << _skill.name << "!" << endl;

	system("PAUSE>nul");

	//공격 빗나감!
	if ((rand() & 100) > _skill.accuracy) {
		std::cout << "공격은 맞지 않았다!" << endl;
		system("PAUSE>nul");
	}

	else {
		if (effetive == 0)
		{
			std::cout << "효과는 없었다..." << endl;
			system("PAUSE>nul");
		}
		else if (effetive < 1)
		{
			std::cout << "효과가 별로인 것 같다..." << endl;
			system("PAUSE>nul");
		}
		else if (effetive > 1)
		{
			std::cout << "효과가 굉장했다!!" << endl;
			system("PAUSE>nul");
		}


		//물리 공격
		if (_skill.attackType == 0)
		{
			_hitted.balanceHp -= (int)(typeBonus * (_skill.damage*_attacker.atk * 2.4f) / _hitted.def*0.2f);
			
		}

		//특수 공격
		else
		{
			_hitted.balanceHp -= (int)(typeBonus * (_skill.damage * _attacker.spa * 2.4f) / _hitted.spd * 0.2f);
		}
	}
	
	if (_hitted.balanceHp <= 0)
	{
		_hitted.balanceHp = 0;
		_hitted.isAlive = false;
	}
	showScreen();

	if (_hitted.isAlive == false) {
		std::cout << _hitted.name << "은 쓰러졌다!" << endl;
		system("PAUSE>nul");
	}
}

void initializeMonster(Trainer _tr) {
	for (int i = 0; i < 6; i++) {
		_tr.monsters[i].balanceHp = _tr.monsters[i].maxHp;
		for (int j = 0; j < 4; j++) {
			_tr.monsters[i].skills[j].balancePoint = _tr.monsters[i].skills[j].maxPoint;
		}
	}
}

// 남은 몬스터 수 파악
int alivePoke(Trainer _tr) {
	int aliveCount = 0;

	for (int i = 0; i < 6; i++)
		if (_tr.monsters[i].isAlive)
			aliveCount++;

	return aliveCount;
}

void selectedPoke() {
	int inputKeyCode;
	int choiceNumber = 0;

	while (1) {
		system("cls");
		std::cout << "[↑] 꺼낼 포켓몬을 골라 주세요 [↓]" << endl;
		std::cout << "선택한 포켓몬 : " << red.monsters[choiceNumber].name << endl;

		if (red.monsters[choiceNumber].isAlive == false) {
			std::cout << " << 기절했습니다... >> " << endl;
		}
		else {
			std::cout << " << HP : " << red.monsters[choiceNumber].balanceHp << " / " << red.monsters[choiceNumber].maxHp << " >>" << endl;
		}

		for (int i = 0; i < 20; i++)
		{
			if (i < hpBar(red.monsters[choiceNumber])) {
				std::cout << "■";
			}
			else if (i > hpBar(red.monsters[choiceNumber]))
				std::cout << "□";
			else
				std::cout << "□";
		}
		std::cout << endl;

		for (int i = 0; i < 4; i++) {
			std::cout << red.monsters[choiceNumber].skills[i].name;
			std::cout << " /  위력 : " << red.monsters[choiceNumber].skills[i].damage;
			std::cout << " << " << red.monsters[choiceNumber].skills[i].typeStr << " >> ";
			std::cout << "PP : " << red.monsters[choiceNumber].skills[i].balancePoint << " / " << red.monsters[choiceNumber].skills[i].maxPoint << endl;
		}

		if (_kbhit) {
			int inputKeyCode = _getch();
			if (inputKeyCode == 224) {
				inputKeyCode = _getch();

				if (inputKeyCode == 72) {
					choiceNumber++;
					if (choiceNumber > 5) {
						choiceNumber = 0;
					}
				}

				if (inputKeyCode == 80) {

					choiceNumber--;
					if (choiceNumber < 0) {
						choiceNumber = 5;
					}
				}

				continue;
			}

			if (inputKeyCode == 13)
			{
				if (choiceNumber == red.inbattle)
				{
					std::cout << "이미 꺼낸 포켓몬입니다." << endl;
					std::system("PAUSE>nul");
					continue;
				}

				else if (red.monsters[choiceNumber].isAlive == false)
				{
					std::cout << "기절한 포켓몬입니다." << endl;
					std::system("PAUSE>nul");
					continue;
				}

				else
				{
					if (red.inbattle < 6 && red.inbattle >= 0)
					{
						std::cout << red.monsters[choiceNumber].name << "를 불러들입니다." << endl;
						std::system("PAUSE>nul");
					}

					red.inbattle = choiceNumber;
					std::cout << red.monsters[red.inbattle].name << "를 꺼냅니다." << endl;
					std::system("PAUSE>nul");

					return;
				}
			}
		}
	}
}

void opening() {
	std::cout << "게임을 시작합니다." << endl;
	red.inbattle = 100;
	green.inbattle = 100;

	system("PAUSE>nul");
}

void greenSelectedPoke(Trainer* _tr)
{
	if (_tr->inbattle < 6 && _tr->inbattle >= 0) {
		std::cout << "상대방이 포켓몬을 불러 들였습니다!" << endl;
		system("PAUSE>nul");
	}

	int selRndPk;

	while (1)
	{
		selRndPk = rand() % memberNumber;
		//무작위로 고른 포켓몬이 지금 꺼낸 포켓몬이거나 기절한 포켓몬이면 다시 뽑음
		if ((_tr->inbattle == selRndPk) || (_tr->monsters[selRndPk].isAlive == false)) continue;
		else break;
	}

	_tr->inbattle = selRndPk;
	std::cout << "상대방이 " << _tr->monsters[selRndPk].name << "을 꺼냈습니다!" << endl;
	system("PAUSE>nul");
}

skill greenSelectSkill() {
	int selRndSkl;
	do { selRndSkl = rand() % 4; } while (green.monsters[green.inbattle].skills[selRndSkl].balancePoint == 0);
	green.monsters[green.inbattle].skills[selRndSkl].balancePoint--;

	return green.monsters[green.inbattle].skills[selRndSkl];
}

void skillList(int selNum) {
	for (int i = 0; i < 4; i++) {
		std::cout << " [ ";
		if (selNum == i)
		{
			std::cout << "☆";
		}
		else
		{
			std::cout << "  ";
		}

		std::cout << " ] " << red.monsters[red.inbattle].skills[i].name << endl;
		std::cout << " 위력 : " << red.monsters[red.inbattle].skills[i].damage;
		std::cout << " << " << red.monsters[red.inbattle].skills[i].typeStr << " >> ";
		std::cout << "PP : " << red.monsters[red.inbattle].skills[i].balancePoint << " / " << red.monsters[red.inbattle].skills[i].maxPoint << endl;
	}
}

void showScreen();
skill redSelectSkill() {
	int inputKeyCode;
	int choiceNumber = 0;

	while (1) {
		showScreen();
		std::cout << "[↑] 기술을 골라 주세요  [↓]" << endl;
		skillList(choiceNumber);


		if (_kbhit) {
			int inputKeyCode = _getch();
			if (inputKeyCode == 224) {
				inputKeyCode = _getch();

				if (inputKeyCode == 72) {
					choiceNumber--;
					if (choiceNumber < 0) {
						choiceNumber = 3;
					}

				}

				if (inputKeyCode == 80) {

					choiceNumber++;
					if (choiceNumber > 3) {
						choiceNumber = 0;
					}
				}

				continue;
			}

			if (inputKeyCode == 13)
			{
				if (red.monsters[red.inbattle].skills[0].balancePoint == 0)
				{
					std::cout << "기술 포인트가 부족합니다!" << endl;
					system("PAUSE>nul");
					continue;
				}

				else
				{
					red.monsters[red.inbattle].skills[choiceNumber].balancePoint--;
					return red.monsters[red.inbattle].skills[choiceNumber];
				}
			}
		}
	}
}


// 지금 체력 : 최대 체력 == 체력 게이지바의 길이 / 게이지바의 최대 길이
// 현재 체력 게이지바의 길이 = (최대 체력 * 게이지바의 최대 길이) / 최대 체력
int hpBar(Monster _monster) {
	return _monster.balanceHp * 20 / _monster.maxHp;
}

void showScreen() {
	system("cls");
	std::cout << "상대방의 포켓몬 : " << green.monsters[green.inbattle].name << endl;
	std::cout << "[ HP "<< green.monsters[green.inbattle].balanceHp << " / "<< green.monsters[green.inbattle].maxHp << "]";
	for (int i = 0; i < 20; i++)
	{
		if (i < hpBar(green.monsters[green.inbattle])) {
			std::cout << "■";
		}
		else if (i > hpBar(green.monsters[green.inbattle]))
			std::cout << "□";
		else
			std::cout << "□";
	}

	std::cout << endl << endl;

	std::cout << "나의 포켓몬 : " << red.monsters[red.inbattle].name << endl;
	std::cout << "[ HP "<< red.monsters[red.inbattle].balanceHp << " / "<< red.monsters[red.inbattle].maxHp << "]";

	for (int i = 0; i < 20; i++)
	{
		if (i < hpBar(red.monsters[red.inbattle])) {
			std::cout << "■";
		}
		else if (i > hpBar(red.monsters[red.inbattle]))
			std::cout << "□";
		else
			std::cout << "□";
	}
	std::cout << endl << endl;

}

int main()
{
	initializeMonster(red);
	initializeMonster(green);
	srand((unsigned)time(NULL));

	opening();
	selectedPoke();
	greenSelectedPoke(&green);


	while (1) {
		if (alivePoke(red) <
			1 || alivePoke(green) < 1) break; // 시합 종료

		//배틀 화면 system("cls"); ~
		showScreen();
		system("PAUSE>nul");

		//기술 선택
		skill redChoice = redSelectSkill();
		skill greenChoice = greenSelectSkill();

		//공격 순서 정하기
		Monster *attackOrder[2];
		skill *skillOrder[2];

		//기술 우선 순위로 정하기
		if (redChoice.spellSpeed > greenChoice.spellSpeed)
		{
			attackOrder[0] = &red.monsters[red.inbattle];
			attackOrder[1] = &green.monsters[green.inbattle];
			skillOrder[0] = &redChoice;
			skillOrder[1] = &greenChoice;
		}
		else if (redChoice.spellSpeed < greenChoice.spellSpeed)
		{
			attackOrder[0] = &green.monsters[green.inbattle];
			attackOrder[1] = &red.monsters[red.inbattle];
			skillOrder[0] = &greenChoice;
			skillOrder[1] = &redChoice;
		}

		//몬스터의 속도로 정하기
		else
		{
			if (red.monsters[red.inbattle].spe > green.monsters[green.inbattle].spe) {
				attackOrder[0] = &red.monsters[red.inbattle];
				attackOrder[1] = &green.monsters[green.inbattle];
				skillOrder[0] = &redChoice;
				skillOrder[1] = &greenChoice;
			}
			else if (red.monsters[red.inbattle].spe < green.monsters[green.inbattle].spe) {
				attackOrder[0] = &green.monsters[green.inbattle];
				attackOrder[1] = &red.monsters[red.inbattle];
				skillOrder[0] = &greenChoice;
				skillOrder[1] = &redChoice;
			}

			//상기한 조건이 같으면 무작위로 정하기
			else {
				int rnd = rand() % 2;
				if (rnd == 0) {
					attackOrder[0] = &red.monsters[red.inbattle];
					attackOrder[1] = &green.monsters[green.inbattle];
					skillOrder[0] = &redChoice;
					skillOrder[1] = &greenChoice;
				}
				else {
					attackOrder[0] = &green.monsters[green.inbattle];
					attackOrder[1] = &red.monsters[red.inbattle];
					skillOrder[0] = &greenChoice;
					skillOrder[1] = &redChoice;
				}
			}
		}

		// 선공
		showScreen();
		caculation(*attackOrder[0], *attackOrder[1], *skillOrder[0]);

		// 지닌 포켓몬이 전멸했나 확인
		if (alivePoke(red) < 1 || alivePoke(green) < 1) break;

		// 현재 포켓몬이 쓰러졌다면 교체 이벤트
		if (attackOrder[1]->isAlive == false) {
			if (attackOrder[1]->ownerName == red.name)
			{
				showScreen();
				selectedPoke();
			}
			else
			{
				showScreen();
				greenSelectedPoke(&green);
			}

			if (alivePoke(red) > 0 && alivePoke(green) > 0)
				continue;
		}

		// 후공
		showScreen();
		caculation(*attackOrder[1], *attackOrder[0], *skillOrder[1]);

		// 지닌 포켓몬이 전멸했나 확인
		if (alivePoke(red) < 1 || alivePoke(green) < 1) break;
		
		// 교체 이벤트
		if (attackOrder[0]->isAlive == false) {
			if (attackOrder[0]->ownerName == red.name)
			{

				selectedPoke();
			}
			else
			{

				showScreen();
				greenSelectedPoke(&green);
			}
			if (alivePoke(red) > 0 && alivePoke(green) > 0)
				continue;
		}

	}

	system("cls");
	if(alivePoke(red) > 0)
	std::cout << "승리하셨습니다!\n";

	else
	std::cout << "당신은 패배했습니다\n";

	system("PAUSE>nul");
}

   

 

 

 

블로그에 남기고자하는 부분인 여기다.

 

 

typeBonus 라는 이차원 배열을 포켓몬 상성표처럼 만들어 사용한다.

보기 쉽게 강제로 여백과 개행을 주었다.

 

 

동그라미 부분에 2를 넣고 세모 대신엔 0.5f 를, (이름은 반감이지만, 나누기 2보다 곱하기 0.5를 권장!)

엑스표시는 0으로 대신한다.

 

이를 기술 위력과 함께 계산하면,

 

int hpBar(Monster _monster);
void caculation(Monster& _attacker, Monster& _hitted, skill _skill) //공격자, 피격자, 기술
{
	float _typeBonus = 1;

	if (((int)(_skill.atkType) == (int)(_attacker.type_1)) || ((int)(_skill.atkType) == (int)(_attacker.type_2)))
	{
		_typeBonus *= 1.5f;
	}

	float effetive = (typeBonus[(int)(_skill.atkType)][(int)(_hitted.type_1)]) * (typeBonus[(int)(_skill.atkType)][(int)(_hitted.type_2)]);
	_typeBonus *= effetive;
}

 

 

이런 코드가 나온다.

 

첫번째 if문에서는 기술을 사용하는 포켓몬과 사용한 기술의 타입이 같으면 위력을 1.5 배 해준다.

 

if문 다음에는 만들었던 이차원 배열에서 1이나 2, 혹은 0이나 0.5 를 곱하여 그 위력을 증감시킨다.

 

예전엔 마냥 어렵다고 느꼈던 이차원 배열이었지만 이해를 하니

 

너무 유용한 개념같다.  

 

 

 

 

 

추가적으로 포인터에 대한 이해가 정말 어려웠다.

개념은 아는데 용도를 모르니

어느 상황에 써야하는지 모르고 남발하거나

아주 쓰지 않도록 코드를 짜야하는게

영 불편하고 찝찝했다...

 

하지만 같은 학원생의 도움으로 이해하게 되었는데,

아래는 공격의 우선권을 정하는 코드이다.

//기술 선택
		skill redChoice = redSelectSkill();
		skill greenChoice = greenSelectSkill();

		//공격 순서 정하기
		Monster *attackOrder[2];
		skill *skillOrder[2];

		//기술 우선 순위로 정하기
		if (redChoice.spellSpeed > greenChoice.spellSpeed)
		{
			attackOrder[0] = &red.monsters[red.inbattle];
			attackOrder[1] = &green.monsters[green.inbattle];
			skillOrder[0] = &redChoice;
			skillOrder[1] = &greenChoice;
		}
		else if (redChoice.spellSpeed < greenChoice.spellSpeed)
		{
			attackOrder[0] = &green.monsters[green.inbattle];
			attackOrder[1] = &red.monsters[red.inbattle];
			skillOrder[0] = &greenChoice;
			skillOrder[1] = &redChoice;
		}

		//몬스터의 속도로 정하기
		else
		{
			if (red.monsters[red.inbattle].spe > green.monsters[green.inbattle].spe) {
				attackOrder[0] = &red.monsters[red.inbattle];
				attackOrder[1] = &green.monsters[green.inbattle];
				skillOrder[0] = &redChoice;
				skillOrder[1] = &greenChoice;
			}
			else if (red.monsters[red.inbattle].spe < green.monsters[green.inbattle].spe) {
				attackOrder[0] = &green.monsters[green.inbattle];
				attackOrder[1] = &red.monsters[red.inbattle];
				skillOrder[0] = &greenChoice;
				skillOrder[1] = &redChoice;
			}

			//상기한 조건이 같으면 무작위로 정하기
			else {
				int rnd = rand() % 2;
				if (rnd == 0) {
					attackOrder[0] = &red.monsters[red.inbattle];
					attackOrder[1] = &green.monsters[green.inbattle];
					skillOrder[0] = &redChoice;
					skillOrder[1] = &greenChoice;
				}
				else {
					attackOrder[0] = &green.monsters[green.inbattle];
					attackOrder[1] = &red.monsters[red.inbattle];
					skillOrder[0] = &greenChoice;
					skillOrder[1] = &redChoice;
				}
			}
		}

 

공격의 우선처리를

선택한 기술의 우선도로 먼저 정해보고,

순서를 정하지 못하면 그 포켓몬의 속도로 결정했다.

그래도 안 되면 난수 생성으로 결정했다.

 

 

이때,

공격자와 피격자, 그리고 공격자의 사용 기술을 가지고

데미지 계산을 하는 caculation() 메소드를 보면, 

전투에 참가하는 포켓몬과 기술의 변수의 포인터를 매개변수로 정하였다.

 

void caculation(Monster& _attacker, Monster& _hitted, skill _skill) //공격자, 피격자, 기술
{
	float _typeBonus = 1;

	if (((int)(_skill.atkType) == (int)(_attacker.type_1)) || ((int)(_skill.atkType) == (int)(_attacker.type_2)))
	{
		_typeBonus *= 1.5f;
	}

	float effetive = (typeBonus[(int)(_skill.atkType)][(int)(_hitted.type_1)]) * (typeBonus[(int)(_skill.atkType)][(int)(_hitted.type_2)]);
	_typeBonus *= effetive;

	//~의 (기술명)!
	showScreen();
	std::cout << _attacker.name << "의 " << _skill.name << "!" << endl;

	system("PAUSE>nul");

	//공격 빗나감!
	if ((rand() & 100) > _skill.accuracy) {
		std::cout << "공격은 맞지 않았다!" << endl;
		system("PAUSE>nul");
	}

	else {
		if (effetive == 0)
		{
			std::cout << "효과는 없었다..." << endl;
			system("PAUSE>nul");
		}
		else if (effetive < 1)
		{
			std::cout << "효과가 별로인 것 같다..." << endl;
			system("PAUSE>nul");
		}
		else if (effetive > 1)
		{
			std::cout << "효과가 굉장했다!!" << endl;
			system("PAUSE>nul");
		}


		//물리 공격
		if (_skill.attackType == 0)
		{
			_hitted.balanceHp -= (int)(_typeBonus * (_skill.damage*_attacker.atk * 2.4f) / _hitted.def*0.2f);
			
		}

		//특수 공격
		else
		{
			_hitted.balanceHp -= (int)(_typeBonus * (_skill.damage * _attacker.spa * 2.4f) / _hitted.spd * 0.2f);
		}
	}
	
	if (_hitted.balanceHp <= 0)
	{
		_hitted.balanceHp = 0;
		_hitted.isAlive = false;
	}
	showScreen();

	if (_hitted.isAlive == false) {
		std::cout << _hitted.name << "은 쓰러졌다!" << endl;
		system("PAUSE>nul");
	}
}

 

 

참조 값에 대한 이해가 부족했던 처음엔

분명 제대로 포켓몬의 체력이 감소되는 코드를 작성했다 생각했지만

계산에 사용된 건 실제 포켓몬 구조체의 값이 아닌

매개변수로 복사 된 값 뿐이었던 것이다...!

 

 

이 문제를 해결하고 이해하는데 도움을 준 학우에게  너무 고마워서 그 날 점심밥을 사줬다...

독학의 장점은 모르겠지만 학원에서 같은 목표로 공부하는 사람들이 많이 있다는 건,

서로에게 좋은 영향을 줄 수 있는 점에서 이득인거 같다!


 

 

 

마음 같아선 위와 같이 아스키 아트로 포켓몬 이미지도 넣고 싶었는데,

다음 과제를 연달아 해야하니 여기까지만...

 

 

 

* 아래의 파일을 다운 받아 작업물을 확인할 수 있다.

20240513_class_06.exe
0.14MB

 

(교체 불가, 반동 구현 안 함, 기술 특수효과 없음, 텍스트 작업 미흡)

 


20240517_Pokemon_Made_By_CSharp.exe
0.02MB

C#으로 다시 만든 버전.

(교체 가능, 반동 구현, 기술 특수효과 일부 구현, 상태이상과 능력치 랭크변화 구현 X)

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함