본문 바로가기
2D 프로그래밍 심화 수업(일부비공개)

[주말]11일차 - 화살표게임 풀이

by MY블로그 2023. 4. 22.

화살표게임


우선 엔진 먼저 생성 한다.

헤더를 묶어둘 stdafx 헤더 만들기. (미리컴파일된 헤더)

iostream random chrono conio.h Window.h (STL) 

Timer.h Math.h (헤더)

 

시간클래스(Math)에

private구간에 static 정적으로 랜덤엔진생성. 스태틱변수들은 외부에서 초기화 시켜줘야되기때문에

public 구간에 const int , const float 랜덤함수 생성 최대 최소값 랜덤설정

int = int distribution, float = real distribution

 

타이머클래스(Timer)에

시간측정을위한 Stopwatch 클래스 생성

Stopwatch 클래스 private 구간에 프로그램이 실행되고 경과되고있는 시간을 생성 시켜주고

public 구간에 생성자 소멸자(default) 로 생성

Start 함수 생성

const float 으로 TimeSec함수 TimeMs함수 만들기

Start함수에 현재시간 가져오는 시작시간 변수 만들기

TimeSec함수에 서 현재시간-시작시간 을 만들어 경과시간을 알수있는 변수만들어서 반환으로 float형 으로 /1000 한 초단위 반환 시켜주기

TimeMs함수에 는 위에 TimeSec 부분을 복사해서 붙여준다 단, /1000은 하지 않도록한다. 밀리세컨드이기때문에.


엔진생성 완료후 게임 만들기 시작.

Stopwatch current_time; 을 불러오고

enum class 로 화살표 방향 만들기 상하좌우 (아스키코드값으로)

Arrow answer[10] 배열 만들기. > 배열갯수는 매크로 디파인값으로 만들어도 ok.

 

시작 함수 만들기.

시작함수 내에서 프로그램실행 이름설정 크기설정 콘솔설정 콘솔커서설정 지정 가능. stdafx에 Window.h헤더추가필요.

프로그램 설정후 

system("pause"); 로 일시정지 만들어주기

 

초기화 함수 만들기.

초기화 함수초 시작시간 받아오고

전역변수에 내가 적중시킨 화살표 카운트변수 만들어서 0으로 초기화해주고

함수내에서 포문으로 화살표갯수만큼 포문을 돌려준다.

포문내에서 화살표는 랜덤값으로 0~3 지정되게 만들어주고

스위치로 화살표에대한 방향을 넣어준다.


불형으로 게임이 계속되고있는지 끝났는지에대해 업데이트 함수 생성

업데이트 함수내에서 키입력에 대한 함수를 넣는다.

if(_kbhit()) 조건문을 만들어주고

인트형 키 변수를 만들어서 key = _getch(); 입력받기 해주고

조건문으로 if(key == 224) 일떄의 조건문을 만들어서

key = _getch(); 입력받기 해주고 게임 을 판단 해주는 조건들을 만든다. (상세코드참조)

정답이라면 카운트를 증가시키고

정답이아니라면 적중을 0으로 만들어준다.

 

디파인값으로 시간과 관련된 값을 만들어주고

시간관련된디파인값을 조건문에넣어 시간이 초과될 경우 게임종료가되는 조건문을 만든다.

종료조건이 맞는다면 return false 해주고

반복문은 종료조건에 들어가지않을때 항상 return true를 반환해 준다.


렌더 함수를 만든다.

렌더는 항상 system("cls"); 로 화면정리를 해주고

시간 출력.

시간은 포문을 돌려주는데 조건은 0부터 디파인값으로 정의된 시간값만큼 돌아가는 조건이다.

포문내부의 조건문은 진행시간에따라 시간을 표시하는 문자를 출력시킨다.

 

시간출력후

화살표를 출력시켜주는데 화살표는 0~화살표 갯수만큼 포문으로 

내가입력한 정답값보다 작거나 같은조건으로 스위치문으로 출력시켜준다.

화살표방향에따라 화살표문자 출력 상하좌우

else 조건에 정답맞춘 자리는 별표 문자 출력시켜주고

 

가장아래쪽에 남은횟수 출력시켜준다.


메인에 시작함수 리셋함수 넣어주고

반복문조건에 업데이트를 넣어주고 반복문 내부에 렌더를 넣어준다.

 

cpp.h파일

// stdafx.h
#pragma once

#include <iostream>
#include <random>
#include <chrono>
#include <conio.h>
#include <Windows.h>

#include "Timer.h"
#include "Math.h"

// stdafx.cpp
#include "stdafx.h"
// Timer.h

#pragma once
class Timer
{
};

class Stopwatch
{
public:
	Stopwatch();
	~Stopwatch() = default;

	void Start();

	const float GetElapsedTimeSec();
	const float GetElapsedTimeMs();

private:
	std::chrono::time_point<std::chrono::high_resolution_clock> start_time;
};

//Timer.cpp

#include "stdafx.h"
#include "Timer.h"

Stopwatch::Stopwatch()
{
	Start();
}

void Stopwatch::Start()
{
	start_time = std::chrono::high_resolution_clock::now();
}

const float Stopwatch::GetElapsedTimeSec()
{
	std::chrono::duration<double, std::milli> ms = std::chrono::high_resolution_clock::now() - start_time;
	return static_cast<float>(ms.count() / 1000.0);
}

const float Stopwatch::GetElapsedTimeMs()
{
	std::chrono::duration<double, std::milli> ms = std::chrono::high_resolution_clock::now() - start_time;
	return static_cast<float>(ms.count());
}
// Math.h

#pragma once
class Math
{
public:
	static const int Random(const int& min, const int& max);
	static const float Random(const float& min, const float& max);

private:
	static std::random_device rd;
	static std::mt19937 mt;
};

// Math.cpp

#include "stdafx.h"
#include "Math.h"

std::random_device Math::rd;
std::mt19937 Math::mt(rd());

const int Math::Random(const int& min, const int& max)
{
	return std::uniform_int_distribution<int>{min, max}(mt);
}

const float Math::Random(const float& min, const float& max)
{
	return std::uniform_real_distribution<float>{min, max}(mt);
}
// Main.cpp

#include "stdafx.h"

#define ARROWNUM 10
#define SUCCESS 5
#define TIMELIMIT 10

void Start();
void Reset();
bool Update();
void Render();

Stopwatch current_time;

int curSuccess = 0;
int curArrow = 0;

enum class Arrow
{
	UP = 72,
	LEFT = 75,
	RIGHT = 77,
	DOWN = 80
};

Arrow answer[ARROWNUM];

int main()
{
	Start();
	Reset();

	while (Update())
	{
		Render();
	}

	return 0;
}

void Start()
{
	system("title TypingGame");
	system("mode con:cols=60 lines=30");

	{
		HANDLE hconsole;
		CONSOLE_CURSOR_INFO ConsoleCursor;

		hconsole = GetStdHandle(STD_OUTPUT_HANDLE);

		ConsoleCursor.bVisible = 0;
		ConsoleCursor.dwSize = 1;

		SetConsoleCursorInfo(hconsole, &ConsoleCursor);
	}

	std::cout << "화살표 타이핑 게임" << std::endl;

	system("pause");
	system("cls");
}

void Reset()
{
	current_time.Start();
	curArrow = 0;

	for(int i = 0; i < ARROWNUM; i++)
	{
		int arrow = Math::Random(0, 3);

		switch (arrow)
		{
		case 0:
		{
			answer[i] = Arrow::UP;
			break;
		}
		case 1:
		{
			answer[i] = Arrow::DOWN;
			break;
		}
		case 2:
		{
			answer[i] = Arrow::RIGHT;
			break;
		}
		case 3:
		{
			answer[i] = Arrow::LEFT;
			break;
		}
		default: break;
		}
	}

}

bool Update()
{
	if (_kbhit())
	{
		int key;

		key = _getch();

		if (key == 224)
		{
			key = _getch();

			if (answer[curArrow] == static_cast<Arrow>(key))
			{
				++curArrow;

				if (ARROWNUM <= curArrow)
				{
					Reset();
					++curSuccess;

					if (SUCCESS <= curSuccess)
					{
						std::cout << "게임 승리!!" << std::endl;
						return false;
					}
				}
			}
			else
			{
				curArrow = 0;
			}
		}
	}

	if (TIMELIMIT <= current_time.GetElapsedTimeSec())
	{
		std::cout << "게임 종료" << std::endl;
		return false;
	}

	return true;
}

void Render()
{
	system("cls");

	std::cout << "TIME : ";

	for (int i = 0; i < TIMELIMIT; i++)
	{
		if (current_time.GetElapsedTimeSec() < i)
			std::cout << "■";
	}

	std::cout << std::endl;

	for (int i = 0; i < ARROWNUM; i++)
	{
		if (curArrow <= i)
		{
			switch (answer[i])
			{
			case Arrow::UP:
			{
				std::cout << "↑";
				break;
			}
			case Arrow::DOWN:
			{
				std::cout << "↓";
				break;
			}
			case Arrow::RIGHT:
			{
				std::cout << "→";
				break;
			}
			case Arrow::LEFT:
			{
				std::cout << "←";
				break;
			}
			}
		}
		else
		{
			std::cout << "☆";
		}
	}
	std::cout << std::endl;

	std::cout << "남은 횟수 : " << SUCCESS - curSuccess << std::endl;
}

 

댓글