[WinApi] float, 엔진(입력키와 엔진에서의 시간)

라멘커비·2024년 1월 25일
0

WinApi

목록 보기
4/32
post-thumbnail

float 간단 설명

  • float 4바이트 실수
  • double 8바이트 실수

double이 더 정밀하다. 다이렉트에서는 float를 많이 사용하기 때문에 float을 사용할 것이다. 언리얼은 double을 사용했다.

float를 표현하는 비트에는 지수부와 가수부가 있다. 그 사이에 소수점이 있는데 소수점의 위치가 고정되어 있지 않다.

부동 소수점(떠돌이 소수점) : 소수점의 위치를 고정하지 않는 방식이다.

float은 0을 제외하고 절대로 정확할 수 없다. 정확한 값을 비교해야 하는 연산에서는 사용하지 않는 것이 좋다.

float Value = 1.1f;
if(Value == 1.1f){ // 안될 수 있음.
}
float Value = 1.0f;
Value += 1.1f;	// Value가 2.1이 아님

대부분의 게임 연산에서 코드에서의 연산은 실수를 동반한 연산을 통해서 현실에 근사하게 연산하고
모니터에 출력할 때는 모니터는 정확하게 정수로 이루어진 공간이기 때문에 정수로 변환해서 출력하게 된다.


오늘도 엔진

🍏EngineInput

어떤 입력 키에 대한 Down, Press, Up, Free의 상태와 상태를 확인할 수 있는 함수(IsDown 등), 상태를 바꿀 수 있는 함수(KeyCheck)를 가질 수 있도록 EngineInput을 만들었다.

코드

  • EngineInput.h
#pragma once
// 키보드를 제어해주는 건 OS일것이기 때문에
// 입력에 대한 함수도 당연히 OS가 우리에게 제공해야 합니다.
#include <Windows.h>
#include <map>
#include <EngineBase\EngineDebug.h>

// AllStateClass
// 설명 :
class EngineInput
{
	friend class InputInitCreator;

private:
	class /*EngineInput::*/EngineKey
	{
		friend EngineInput;

	public:
		bool Down = false; // 누른 순간
		bool Press = false; // 계속 누르면
		bool Up = false; // 떼어진 순간
		bool Free = true; // 누르지 않으면

		float PressTime = 0.0f;

		int Key = -1; // VK_LBUTTON

		void KeyCheck();

		EngineKey()
		{

		}

		EngineKey(int _Key)
			: Key(_Key)
		{

		}
	};

public:
	// constrcuter destructer
	EngineInput();
	~EngineInput();

	// delete Function
	EngineInput(const EngineInput& _Other) = delete;
	EngineInput(EngineInput&& _Other) noexcept = delete;
	EngineInput& operator=(const EngineInput& _Other) = delete;
	EngineInput& operator=(EngineInput&& _Other) noexcept = delete;

	static bool IsDown(int _Key)
	{
		if (false == AllKeys.contains(_Key))
		{
			MsgBoxAssert("입력설정이 존재하지 않는 키 입니다");
		}

		return AllKeys[_Key].Down;
	}

	static bool IsPress(int _Key)
	{
		if (false == AllKeys.contains(_Key))
		{
			MsgBoxAssert("입력설정이 존재하지 않는 키 입니다");
		}

		return AllKeys[_Key].Press;
	}

	static bool IsUp(int _Key)
	{
		if (false == AllKeys.contains(_Key))
		{
			MsgBoxAssert("입력설정이 존재하지 않는 키 입니다");
		}

		return AllKeys[_Key].Up;
	}

	static bool IsFree(int _Key)
	{
		if (false == AllKeys.contains(_Key))
		{
			MsgBoxAssert("입력설정이 존재하지 않는 키 입니다");
		}

		return AllKeys[_Key].Free;
	}

	static void KeyCheckTick(float _DeltaTime);

protected:
	//              'A'  상태가 어때?
	static std::map<int, EngineKey> AllKeys;

	int Value;

private:
	static void InputInit();
};
  • EngineInput.cpp
#include "EngineInput.h"

std::map<int, EngineInput::EngineKey> EngineInput::AllKeys;

void EngineInput::EngineKey::KeyCheck()
{
	// 이 키가 눌렸다는 거죠?
	// if (0 != GetAsyncKeyState('A'))
	// A키가 눌렸다면
	if (0 != GetAsyncKeyState(Key))
	{
		if (true == Free)
		{
			// 이전까지 이 키는 눌리고 있지 않았다
			Down = true;
			Press = true;
			Up = false;
			Free = false;
		}
		else if(true == Down)
		{
			// 이전까지 이 키는 눌리고 있었다.
			Down = false;
			Press = true;
			Up = false;
			Free = false;
		}
	}
	else
	{
		if (true == Press)
		{
			// 이전까지 이 키는 눌리고 있었다.
			Down = false;
			Press = false;
			Up = true;
			Free = false;
		}
		else if(true == Up)
		{
			// 이전까지 이 키는 안눌리고 있었고 앞으로도 안눌릴거다.
			Down = false;
			Press = false;
			Up = false;
			Free = true;
		}

	}
}

EngineInput::EngineInput()
{
}

EngineInput::~EngineInput()
{
}

void EngineInput::InputInit()
{
	AllKeys[VK_LBUTTON] = EngineKey(VK_LBUTTON);
	AllKeys[VK_RBUTTON] = EngineKey(VK_RBUTTON);
	AllKeys[VK_CANCEL] = EngineKey(VK_CANCEL);
	AllKeys[VK_MBUTTON] = EngineKey(VK_MBUTTON);
	AllKeys[VK_BACK] = EngineKey(VK_BACK);
	AllKeys[VK_TAB] = EngineKey(VK_TAB);
	AllKeys[VK_CLEAR] = EngineKey(VK_CLEAR);
	AllKeys[VK_RETURN] = EngineKey(VK_RETURN);
	AllKeys[VK_SHIFT] = EngineKey(VK_SHIFT);
	AllKeys[VK_LSHIFT] = EngineKey(VK_LSHIFT);
	AllKeys[VK_CONTROL] = EngineKey(VK_CONTROL);
	AllKeys[VK_MENU] = EngineKey(VK_MENU);
	AllKeys[VK_PAUSE] = EngineKey(VK_PAUSE);
	AllKeys[VK_CAPITAL] = EngineKey(VK_CAPITAL);
	AllKeys[VK_KANA] = EngineKey(VK_KANA);
	AllKeys[VK_HANGEUL] = EngineKey(VK_HANGEUL);
	AllKeys[VK_HANGUL] = EngineKey(VK_HANGUL);
	AllKeys[VK_IME_ON] = EngineKey(VK_IME_ON);
	AllKeys[VK_JUNJA] = EngineKey(VK_JUNJA);
	AllKeys[VK_FINAL] = EngineKey(VK_FINAL);
	AllKeys[VK_HANJA] = EngineKey(VK_HANJA);
	AllKeys[VK_KANJI] = EngineKey(VK_KANJI);
	AllKeys[VK_IME_OFF] = EngineKey(VK_IME_OFF);
	AllKeys[VK_ESCAPE] = EngineKey(VK_ESCAPE);
	AllKeys[VK_CONVERT] = EngineKey(VK_CONVERT);
	AllKeys[VK_NONCONVERT] = EngineKey(VK_NONCONVERT);
	AllKeys[VK_ACCEPT] = EngineKey(VK_ACCEPT);
	AllKeys[VK_MODECHANGE] = EngineKey(VK_MODECHANGE);
	AllKeys[VK_SPACE] = EngineKey(VK_SPACE);
	AllKeys[VK_PRIOR] = EngineKey(VK_PRIOR);
	AllKeys[VK_NEXT] = EngineKey(VK_NEXT);
	AllKeys[VK_END] = EngineKey(VK_END);
	AllKeys[VK_HOME] = EngineKey(VK_HOME);
	AllKeys[VK_LEFT] = EngineKey(VK_LEFT);
	AllKeys[VK_UP] = EngineKey(VK_UP);
	AllKeys[VK_RIGHT] = EngineKey(VK_RIGHT);
	AllKeys[VK_DOWN] = EngineKey(VK_DOWN);
	AllKeys[VK_SELECT] = EngineKey(VK_SELECT);
	AllKeys[VK_PRINT] = EngineKey(VK_PRINT);
	AllKeys[VK_EXECUTE] = EngineKey(VK_EXECUTE);
	AllKeys[VK_SNAPSHOT] = EngineKey(VK_SNAPSHOT);
	AllKeys[VK_INSERT] = EngineKey(VK_INSERT);
	AllKeys[VK_DELETE] = EngineKey(VK_DELETE);
	AllKeys[VK_HELP] = EngineKey(VK_HELP);
	AllKeys[VK_LWIN] = EngineKey(VK_LWIN);
	AllKeys[VK_RWIN] = EngineKey(VK_RWIN);
	AllKeys[VK_APPS] = EngineKey(VK_APPS);
	AllKeys[VK_SLEEP] = EngineKey(VK_SLEEP);
	AllKeys[VK_NUMPAD0] = EngineKey(VK_NUMPAD0);
	AllKeys[VK_NUMPAD1] = EngineKey(VK_NUMPAD1);
	AllKeys[VK_NUMPAD2] = EngineKey(VK_NUMPAD2);
	AllKeys[VK_NUMPAD3] = EngineKey(VK_NUMPAD3);
	AllKeys[VK_NUMPAD4] = EngineKey(VK_NUMPAD4);
	AllKeys[VK_NUMPAD5] = EngineKey(VK_NUMPAD5);
	AllKeys[VK_NUMPAD6] = EngineKey(VK_NUMPAD6);
	AllKeys[VK_NUMPAD7] = EngineKey(VK_NUMPAD7);
	AllKeys[VK_NUMPAD8] = EngineKey(VK_NUMPAD8);
	AllKeys[VK_NUMPAD9] = EngineKey(VK_NUMPAD9);
	AllKeys[VK_MULTIPLY] = EngineKey(VK_MULTIPLY);
	AllKeys[VK_ADD] = EngineKey(VK_ADD);
	AllKeys[VK_SEPARATOR] = EngineKey(VK_SEPARATOR);
	AllKeys[VK_SUBTRACT] = EngineKey(VK_SUBTRACT);
	AllKeys[VK_DECIMAL] = EngineKey(VK_DECIMAL);
	AllKeys[VK_DIVIDE] = EngineKey(VK_DIVIDE);
	AllKeys[VK_F1] = EngineKey(VK_F1);
	AllKeys[VK_F2] = EngineKey(VK_F2);
	AllKeys[VK_F3] = EngineKey(VK_F3);
	AllKeys[VK_F4] = EngineKey(VK_F4);
	AllKeys[VK_F5] = EngineKey(VK_F5);
	AllKeys[VK_F6] = EngineKey(VK_F6);
	AllKeys[VK_F7] = EngineKey(VK_F7);
	AllKeys[VK_F8] = EngineKey(VK_F8);
	AllKeys[VK_F9] = EngineKey(VK_F9);
	AllKeys[VK_F10] = EngineKey(VK_F10);
	AllKeys[VK_F11] = EngineKey(VK_F11);
	AllKeys[VK_F12] = EngineKey(VK_F12);
	AllKeys[VK_F13] = EngineKey(VK_F13);
	AllKeys[VK_F14] = EngineKey(VK_F14);
	AllKeys[VK_F15] = EngineKey(VK_F15);
	AllKeys[VK_F16] = EngineKey(VK_F16);
	AllKeys[VK_F17] = EngineKey(VK_F17);
	AllKeys[VK_F18] = EngineKey(VK_F18);
	AllKeys[VK_F19] = EngineKey(VK_F19);
	AllKeys[VK_F20] = EngineKey(VK_F20);
	AllKeys[VK_F21] = EngineKey(VK_F21);
	AllKeys[VK_F22] = EngineKey(VK_F22);
	AllKeys[VK_F23] = EngineKey(VK_F23);
	AllKeys[VK_F24] = EngineKey(VK_F24);
	AllKeys['-'] = EngineKey(VK_OEM_MINUS);
	AllKeys['+'] = EngineKey(VK_OEM_PLUS);
	AllKeys[VK_OEM_4] = EngineKey(VK_OEM_4);
	AllKeys[VK_OEM_6] = EngineKey(VK_OEM_6);

	for (int i = 'A'; i <= 'Z'; i++)
	{
		AllKeys[i] = EngineKey(i);
	}

	for (int i = '0'; i <= '9'; i++)
	{
		AllKeys[i] = EngineKey(i);
	}

}

void EngineInput::KeyCheckTick(float _DeltaTime)
{
	for (std::pair<const int, EngineKey>& Key : AllKeys)
	{
		EngineKey& CurKey = Key.second;

		CurKey.KeyCheck();
	}
}

class InputInitCreator
{
public:
	InputInitCreator()
	{
		EngineInput::InputInit();
	}
};

InputInitCreator CreateValue = InputInitCreator();

InputInitCreator 클래스에 대하여

EngineInput::InputInit()에서는 AllKeys에 들어갈 키들을 만든다, 즉 게임에 쓰일 키들을 생성한다. InputInitCreator라는 클래스를 만들고 생성자에 EngineInput::InputInit()을 넣어주었고, 전역 객체를 만들어줌으로써 생성자를 실행시켰다.
→ 한마디로 EngineInput::InputInit()을 일부러 실행시켜줄 필요 없이 실행시키기 위한 클래스라는 말이다.

🍏EngineTime

_DeltaTime : 이전 프레임에서 지금 실행된 프레임까지의 사이 시간이다.
그 DeltaTime을 구하는 방법 -> EngineTime(Level0)
#include <chrono> : 최신 시간 재는 std 헤더.

CPU 내부에 하드웨어적으로 시간을 카운트하는 부분이 존재한다. 초 단위가 아니고 그냥 똑딱똑딱(?) 카운트한다. 근데 그게 초당 오차가 거의 없을 정도로 일정하게 센다.

컴퓨터켰다 0
1초에 100만
2초에 200만
3초에 300만
4초에 400만 1
5초에 500만
...
이런 느낌

그리고 윈도우는 지금까지 얼마나 셌는지 알려주는 함수를 제공한다. 초당 얼마를 셀 수 있는지를 알려주는 함수도 제공한다. : 각각 QueryPerformanceCount, QueryPerformanceFrequency

DeltaTime 구하는 방법

int Count = 초당 얼마나 셀 수 있는지
int PrevTime = 얼마나 셌어?

~~~ 한 프레임 지나감 ~~~

int CurTime = 얼마나 셌어?

float DeltaTime = (CurTime - PrevTime) / Count;	// 한 프레임당 지나는 시간을 알 수 있다.

이 방식을 활용해서 EngineTime에서 기능을 만든다. QueryPerformanceCount, QueryPerformanceFrequency를 활용해서 만들면 된다.

그리고 DeltaTime을 이용해서 레벨과 액터의 Tick에서 활용하여
플레이어가 1초에 100만큼 이동하는 등의 일정한 이동을 만들어낼 수 있다. (1프레임당 1씩 움직인다면 컴퓨터 성능에 따라 게임 속 능력치가 달라짐)

profile
일단 시작해보자

0개의 댓글