이전 구현
https://jartlife.tistory.com/64
이전 구현은 위 글에서 볼 수 있다.
중첩 클래스가 가독성을 좀 심하게 나쁘게 했다.
그리고 타이머 클래스가 지고 있는 책임이 너무 무거운 것 같다.
게임의 기능을 추가하려고 그 복잡한 클래스 중첩 구조를 추적해서 update()와 render() 함수를 건드리기란..
별도의 클래스를 추가하는데 timer.h 파일을 매번 건드려야 한다는 것도 심각한 설계 미스다.
타이머를 다시 하나로 통합하였고, 타이머가 지지 않아도 될 책임은 분리하였다.
또한 타이머를 정적 클래스로 사용하지 않고 여러 개의 인스턴스 생성이 가능하도록 바꾸었다.
타이머를 단 한 개밖에 안 만들더라도, 특정 지역의 객체로 만들어 전역 접근이 불가능하게 할 필요가 있었다.
개요
- class timer
: 윈도우API에서 시간 관련 기능을 담당하는 클래스입니다.
- const UINT timer_id
: 타이머끼리 구분하는 id입니다. - float clock
: 해당 타이머를 이용할 때 사용할 단위 시간입니다.
예를 들어, 한 애니메이션의 프레임 전환은 5 * clock 마다 이루어질 수 있습니다.
타이머 자체적으론 이용하지 않지만, 타이머에 의존하는 클래스들은 이 변수를 이용하여 쉽게 배속 효과 같은 기능을 구현할 수 있을 것입니다. - void alarm( const float ms_delay, std::function<void()>&& to_do )
: ms_delay 뒤에 to_do 를 실행합니다.
일정 시간 뒤에 동작이 이루어지도록 하고 싶을 때 사용합니다. - void setFPS( const float fps )
: FPS( Frames Per Second )를 설정합니다. - void update()
: 윈도우의 타이머에서 timer_id 의 타이머가 불릴 때마다 update()를 직접 호출해야 합니다.
윈도우 타이머에게 책임을 부여하여 타이머 클래스 내부에 게임의 세부사항을 구현할 필요가 없습니다.
따라서 타이머 객체를 손쉽게 game과 scene과 같은 클래스의 멤버로 만들 수 있습니다. - const float getlag() const
: 현재 지연 정도를 반환합니다.
지연이 심하면 몇몇 작업을 생략해야 할 것입니다. - const float getobjFPS() const
: 설정된 FPS를 반환합니다. - const float getcurFPS() const
: 현재 FPS를 반환합니다.
설정된 FPS는 높더라도, 성능의 한계로 실제 FPS는 높지 않을 수 있습니다 - const float get_ms_per_frame() const
: 프레임 당 시간을 반환합니다.
- const UINT timer_id
코드
#ifndef _timer
#define _timer
#ifdef max
#undef max // c 매크로 max를 제거하기 위함
#endif
#include <chrono>
#include <iostream>
#include "TMP.h"
#include <algorithm>
#include <queue>
#include <numeric>
#include <deque>
#include <functional>
#include <utility>
class timer
{
private:
struct delayed
{
const bool operator>( const delayed& other )
{
return ms_delay > other.ms_delay;
}
float ms_delay;
std::function< void() > to_do;
};
public:
float clock;
const UINT timer_id;
void alarm( float ms_delay, std::function< void() >&& to_do )
{
alarms.push( delayed{ ms_delay, to_do } );
}
void setFPS( const float fps )
{
objfps = fps;
curfps = fps;
ms_per_frame = 1000 / fps;
KillTimer( hWnd, timer_id );
SetTimer( hWnd, timer_id, static_cast< UINT >( ms_per_frame ), nullptr );
}
void update()
{
using namespace std::chrono;
static system_clock::time_point last_tp = system_clock::now();
auto cur_tp = system_clock::now();
auto interval = duration_cast< nanoseconds >( cur_tp - last_tp ).count()
/ static_cast< float >( nanoseconds::period::den / milliseconds::period::den );
prevent_overflow();
process_alarm();
update_curfps( interval );
ms_time += interval;
lag = std::max( lag + interval - ms_time, 0.f );
last_tp = cur_tp;
}
const float getlag() const
{
return lag;
}
const float getobjFPS() const
{
return objfps;
}
const float getcurFPS() const
{
return curfps;
}
const float get_ms_per_frame() const
{
return ms_per_frame;
}
timer( HWND hWnd, const UINT timer_id, const float fps, const float clock = 10.f ) : ms_time{ 0 }, lag{ 0 },
hWnd{ hWnd }, timer_id{ timer_id }, clock{ clock }, objfps{ fps }, curfps{ fps }, ms_per_frame{ 1000 / fps }
{
SetTimer( hWnd, timer_id, static_cast< UINT >( ms_per_frame ), nullptr );
}
~timer()
{
KillTimer( hWnd, timer_id );
}
// 복사는 이용되지 않을 것이라 가정해 구현하지 않았다.
// timer는 id 정보를 들고 있으므로, 후에 구현한다면 독립된 id를 보장하는 복사 동작이 필요하다.
timer( const timer& other ) = default;
timer& operator=( const timer& ) = default;
private:
float lag;
float ms_per_frame;
float curfps;
float objfps;
float ms_time;
std::priority_queue< delayed, std::vector< delayed >, std::greater<> > alarms;
HWND hWnd;
void prevent_overflow()
{
static constexpr float limit = 10'000'000.f;
if ( ms_time > limit )
{
ms_time -= limit;
sub_delays( limit );
}
}
void sub_delays( const float val )
{
decltype( alarms ) temp;
while ( !alarms.empty() )
{
auto a = alarms.top();
a.ms_delay -= val;
temp.push( std::move( a ) );
alarms.pop();
}
alarms = std::move( temp );
}
void process_alarm()
{
while ( is_there_an_alarm_on_time() )
{
alarms.top().to_do();
alarms.pop();
}
}
const bool is_there_an_alarm_on_time()
{
if ( alarms.empty() )
{
return false;
}
return alarms.top().ms_delay < ms_time;
}
void update_curfps( const float interval )
{
curfps = 1000 / interval;
}
};
#endif
|
cs |
'Project > Win32API로 던그리드 모작' 카테고리의 다른 글
fmod를 이용한 게임 사운드 프로그래밍 (0) | 2021.11.06 |
---|---|
게임 클래스와 씬 클래스 추가 (0) | 2021.11.06 |
게임 타이머 구현 (0) | 2021.09.21 |
git repository 생성 (0) | 2021.09.17 |
현재 적용할 메모리 풀 (0) | 2021.09.15 |