Engine (2.) - Timing

Dobrý den, do nového roku jsem si dal takové předsevzetí že bych rád na těchto stránkách alespoň jednou za měsíc uveřejnil nový článek. Jelikož si myslím že kdybych zde uveřejnil kompletní zdrojáky mého enginu bylo by to stejně pro většinu lidí "nestravitelné" rozhodl jsem se psát jednotlivé články postupně od základních tříd až po složitější celky. Dnes jsem se rozhodl popsat třídu která se stará o časování ve scéně, třídu CTimer.

Můj časovač se skládá ze 2 souborů, timer.h a timer.cpp. Při návrhu jsem se snažil aby obsahoval metody Pause, Reset, GetActualTime a GetElapsedTime. Jelikož je využíván v aplikacích kde je potřeba měřit čas mnohokrát za sekundu, zvolil jsem časovač založený na Windows API funkci GetTickCount(). Tato funkce vrací 32 bitovou hodnotu která udává počet milisekund od startu systému. 32 bitů by pro naše účely mělo stačit protože k přetečení by došlo až za 49,7 den. Tak dlouhý UpTime nelze u obyčejného počítače očekávat... Zde je hlavičkový soubor časovače timer.h:

//-----------------------------------------------------------
// MODUL:	CTimer.h
// PROJECT:	3D engine
// AUTHORS:	Roman Schulz, rschulz@centrum.cz
//-----------------------------------------------------------


#ifndef _CTimer_H_
#define _CTimer_H_

#include 

class CTimer
{
	public:
		CTimer();
		~CTimer();

		void Reset();
		void SetPaused(bool Paused);
		bool GetPaused();

		bool Update();

		unsigned long int GetElapsedTime();
		unsigned long int GetActualTime();
		unsigned long int GetRealTime();
		int GetFPS();
		
	protected:
		unsigned long int m_ElapsedTime;	// Cas mezi poslednimi dvema updaty
		unsigned long int m_ActualTime;		// Cas posledniho updatu
		unsigned long int m_StartTime;		// Cas spusteni casovace
		bool m_Paused;			// Pozastaveno
		int m_FPS;				// Pocet snimku za sekundu
};

#endif

Tak se pusťme do podrobnějšího popisu jednotlivých metod. Nejdřív začnu konstruktorem a destruktorem. K tomu není téměř co dodat. Pouze to, že v konstruktoru inicializuji členské proměnné třídy Timer a v metodě Reset uložím čas spuštění časovače. To proto aby časovač počítal od nuly.

//-----------------------------------------------------------
// MODUL:	CTimer.cpp
// PROJECT:	3D engine
// AUTHORS:	Roman Schulz, rschulz@centrum.cz
// DESCRIPTION: 
//-----------------------------------------------------------

#include "CTimer.h"

//-----------------------------------------------------------
// Name: constructor()
// Desc:
//-----------------------------------------------------------
CTimer::CTimer():
	m_ActualTime(0),
	m_ElapsedTime(0),
	m_StartTime(0),
	m_FPS(0)
{
	Reset();
}


//-----------------------------------------------------------
// Name: destructor()
// Desc:
//-----------------------------------------------------------
CTimer::~CTimer()
{
}


//-----------------------------------------------------------
// Name: Reset()
// Desc: Vyresetuje casovac
//-----------------------------------------------------------
void CTimer::Reset()
{
	// Ulozime cas resetu
	m_StartTime = GetTickCount();
	m_ActualTime = 0;
	Update();
}

Další dvě metody nastaví a ukončí pauzu. Význam proměnné m_Paused bude vysvětlen níže.

//-----------------------------------------------------------
// Name: GetPaused()
// Desc: Vrati zda je casovac pozastaven
//-----------------------------------------------------------
bool CTimer::GetPaused()
{
	return m_Paused;
}


//-----------------------------------------------------------
// Name: SetPaused()
// Desc: Vrati zda je casovac pozastaven
//-----------------------------------------------------------
void CTimer::SetPaused(bool Paused)
{
	m_Paused = Paused;
}

Tak a nyní přichází na řadu srdce časovače. Tím je metoda Update, která zjistí nový čas a vypočítá čas uplynutý od poslední aktualizace. Nejdřív zjistím jaký čas uplynul od startu počítače a od této hodnoty odečtu hodnotu m_StartTime, tím dostanu aktuální čas od posledního resetu časovače. Pokud není nastavena pauza tak vypočtu uplynutý čas od poslední aktualizace a navíc vypočtu aktuální hodnotu počtu snímků za sekundu. Pokud je časovač pozastaven tak jen zvetším proměnnou m_StartTime tak aby po ukončení pauzy vše sedělo tak jak má:

//-----------------------------------------------------------
// Name: Update()
// Desc: Zjisti pocet updatu za sekundu a uplynute casy
//-----------------------------------------------------------
bool CTimer::Update()
{
	// Urci cas od resetu v sekundach krome doby pauzy
	unsigned long int RealTime = GetTickCount() - m_StartTime;

	if (!m_Paused)
	{
		// Zde vypocteme cas uplynuly od posledniho volani Update v sekundach
		m_ElapsedTime = RealTime - m_ActualTime ;

		// Ulozime cas posledniho updatovani
		m_ActualTime = RealTime;

		// Vypocita pocet updatu za sekundu
		if (m_ElapsedTime > 0)
		{
			m_FPS = (int)(1000.0f/m_ElapsedTime);
		}
	}
	else
	{
		m_ElapsedTime = 0;
		m_FPS = 0;

		// Posune zacatek o dobu pauzy
		m_StartTime += RealTime - m_ActualTime;
	}

	return true;
}

Zde jsou 3 metody které vrátí hodnoty času vypočítané v metodě Update a metoda GetRealTime která vrátí aktuální čas. Rozdíl je v tom, že Update volám jedenkrát za vykreslení celého snímku a pak metody GetActualTime() nebo GetElapsedTime() vrací čas tohoto snímku. V případě že chci zjistit čas nezávisle na aktualizaci volám metodu GetRealTime().

//-----------------------------------------------------------
// Name: GetElapsedTime()
// Desc: Vrati cas uplynuty od posledniho volani updatu
//-----------------------------------------------------------
unsigned long int CTimer::GetElapsedTime()
{
	return m_ElapsedTime;
}


//-----------------------------------------------------------
// Name: GetActualTime()
// Desc: Vrati cas posledniho volani funkce update
//-----------------------------------------------------------
unsigned long int CTimer::GetActualTime()
{
	return m_ActualTime;
}


//-----------------------------------------------------------
// Name: GetFPS()
// Desc: Vrati pocet vykreslenych snimku za sekundu
//-----------------------------------------------------------
int CTimer::GetFPS()
{
	return m_FPS;
}

//-----------------------------------------------------------
// Name: GetRealTime()
// Desc: Zjisti aktualni cas
//-----------------------------------------------------------
unsigned long int CTimer::GetRealTime()
{
	if (m_Paused)
	{
		return m_ActualTime;
	}
	else
	{
		return GetTickCount() - m_StartTime;
	}
}

A tím jsem třídu časovače snad alespoň trochu srozumitelně popsal. Ještě bych rád uvedl příklad použití tohoto časovače. Nejedná se o moc smysluplný program ale naznačuje jak tuto třídu používám já. Můžete si vytvořit ve scéně tolik časovačů kolik potřebujete (aby každý počítal čas něčeho jiného), ale já si myslím že stačí pouze jediný.

int main()
{
	CTimer Timer;
	Timer.Reset();
	...

	while (true)
	{
		Timer.Update();

		if (Input.KeyDown(ARROW_FRONT)) Camera.Move(Timer.GetElapsedTime());
		...

		Car.SetPos(0, 0, Timer.GetActualTime());
	}
	return 0;
}

Závěrem bych ještě rád dodal že je tato třída pro některé lidi "primitivní" ale podle zkušeností od lidí z okolí se ještě stále vyskytují tací, kteří pohyb objektů ve scéně řeší tím způsobem že před vykreslením každého snímku zvyššují některou souřadnici o pevně danou hodnotu. Tento způsob je absolutně nevhodný protože nezaručuje plynulou animaci. Při malém počtu vykreslených snímků za sekundu (FPS) je pohyb pomalý a při zvýšení FPS (například na jiném počítači nebo v jiné části scény) se pohyb zrychlí. Tomu se zabrání použitím tohoto nebo i jiného časovače. Ještě bych dodal že tento časovač má rozlišení 1 ms. Kdyby někdo potřeboval jemnější rozlišení časovače, měl by se podívat po QueryPerformanceTimer a QueryPerformanceFrequency. Podrobný popis těchto a většiny ostatních funkcí je k dispozici v nápovědě MSDN. V příštích dílech tohoto mini seriálu bych se rád podíval na třídu která se stará o rozhrání DirectInput, Profiller kódu nebo třeba identifikaci procesoru CPU. To záleží na Vás, čtenáři:-)

Vyšlo 21.03.2005, v blogu: 0 1 2 3 4 5 6 7 8

Děkuji, že jste se rozhodl(a) přečíst tento článek. Budu rád i za komentář. Pokud Vás tento článek zaujal a rádi byste jej doporučili ostatním, podpořte mně prosím tím, že věnujete minutku svého času a uděláte mi reklamu na linkuj.cz, vybrali.sme.sk či jagg.cz. Přeji příjemné čtení

Poslední články

Diskuse k blogu

Zatím nikdo nevložil komentář. Chcete být první? Přidání příspěvku
©PC-guru.cz 2000-2008 | Optimalizováno pro 1024*768