SDL程式設計入門(23)高階定時器

血色v殘陽發表於2020-12-22

高階定時器

現在我們已經做了一個用SDL做的基本計時器,是時候做一個可以啟動/停止/暫停的計時器了。

//The application time based timer
class LTimer
{
    public:
        //Initializes variables
        LTimer();

        //各種時鐘操作
        void start();
        void stop();
        void pause();
        void unpause();

        //獲取定時器的時間
        Uint32 getTicks();

        //檢查定時器的狀態
        bool isStarted();
        bool isPaused();

    private:
        //定時器開始時的時間
        Uint32 mStartTicks;

        //定時器暫停時的時間
        Uint32 mPausedTicks;

        //定時器狀態
        bool mPaused;
        bool mStarted;
};

對於這些新功能,我們將建立一個定時器類。 它具有啟動/停止/暫停/取消暫停計時器並檢查其狀態的所有基本功能。在資料成員方面,我們有像之前一樣的開始時間,有一個變數來儲存暫停時的時間,還有狀態標誌來跟蹤定時器是在執行還是暫停。

LTimer::LTimer()
{
    //Initialize the variables
    mStartTicks = 0;
    mPausedTicks = 0;

    mPaused = false;
    mStarted = false;
}

我們的建構函式初始化內部資料成員。

void LTimer::start()
{
    //Start the timer
    mStarted = true;

    //Unpause the timer
    mPaused = false;

    //Get the current clock time
    mStartTicks = SDL_GetTicks();
    mPausedTicks = 0;
}

start函式設定啟動和暫停標誌,獲取定時器的啟動時間,並將暫停時間初始化為0,對於這個定時器,如果我們想重新啟動它,只需再次呼叫start即可。由於我們可以在定時器暫停和/或執行的情況下啟動它,所以我們應該確保清除暫停的資料。

void LTimer::stop()
{
    //Stop the timer
    mStarted = false;

    //Unpause the timer
    mPaused = false;

    //Clear tick variables
    mStartTicks = 0;
    mPausedTicks = 0;
}

stop函式基本上重新初始化了所有的變數。

void LTimer::pause()
{
    //If the timer is running and isn't already paused
    if( mStarted && !mPaused )
    {
        //Pause the timer
        mPaused = true;

        //Calculate the paused ticks
        mPausedTicks = SDL_GetTicks() - mStartTicks;
        mStartTicks = 0;
    }
}

暫停時,我們要檢查定時器是否在執行,因為暫停一個沒有啟動的定時器是沒有意義的。如果定時器正在執行,我們設定暫停標誌,將定時器暫停的時間儲存在mPausedTicks中,並重新設定啟動時間。

void LTimer::unpause()
{
    //If the timer is running and paused
    if( mStarted && mPaused )
    {
        //Unpause the timer
        mPaused = false;

        //Reset the starting ticks
        mStartTicks = SDL_GetTicks() - mPausedTicks;

        //Reset the paused ticks
        mPausedTicks = 0;
    }
}

因此,當我們取消暫停定時器時,要確保定時器正在執行和暫停,因為我們不能取消一個停止或執行的定時器。我們將暫停標誌設定為false,並設定新的啟動時間。

比如說,如果你在SDL_GetTicks()報告5000毫秒時啟動定時器,然後在10000毫秒時暫停。這意味著暫停時的相對時間是5000ms。如果我們要在SDL_GetTicks為20000時取消暫停,則新的開始時間將為20000-5000ms或15000ms。 這樣,相對時間仍將與當前SDL_GetTicks時間相距5000ms。

Uint32 LTimer::getTicks()
{
    //The actual timer time
    Uint32 time = 0;

    //If the timer is running
    if( mStarted )
    {
        //If the timer is paused
        if( mPaused )
        {
            //Return the number of ticks when the timer was paused
            time = mPausedTicks;
        }
        else
        {
            //Return the current time minus the start time
            time = SDL_GetTicks() - mStartTicks;
        }
    }

    return time;
}

獲取時間是有點棘手的,因為我們的定時器可以執行、暫停或停止。如果定時器是停止的,我們只是返回初始的0值。如果定時器暫停,我們返回暫停時儲存的時間。如果定時器正在執行而不是暫停,我們返回相對於它開始時的時間。

bool LTimer::isStarted()
{
    //Timer is running and paused or unpaused
    return mStarted;
}

bool LTimer::isPaused()
{
    //Timer is running and paused
    return mPaused && mStarted;
}

在這裡,我們有一些訪問函式來檢查定時器的狀態。

//Main loop flag
bool quit = false;

//Event handler
SDL_Event e;

//Set text color as black
SDL_Color textColor = { 0, 0, 0, 255 };

//The application timer
LTimer timer;

//In memory text stream
std::stringstream timeText;

在進入主迴圈之前,我們宣告一個定時器物件和一個字串流來將時間值變成文字。

else if( e.type == SDL_KEYDOWN )
{
	//Start/stop
	if( e.key.keysym.sym == SDLK_s )
	{
		if( timer.isStarted() )
		{
			timer.stop();
		}
		else
		{
			timer.start();
		}
	}
	//Pause/unpause
	else if( e.key.keysym.sym == SDLK_p )
	{
		if( timer.isPaused() )
		{
			timer.unpause();
		}
		else
		{
			timer.pause();
		}
	}
}

當我們按s鍵時,我們檢查定時器是否啟動。如果啟動了,我們就停止它。如果沒有,我們就啟動它。當我們按p鍵時,我們檢查定時器是否暫停。如果是,我們就取消暫停。否則,我們將暫停它。

//Set text to be rendered
timeText.str( "" );
timeText << "Seconds since start time " << ( timer.getTicks() / 1000.f ) ; 

//Render text
if( !gTimeTextTexture.loadFromRenderedText( timeText.str().c_str(), textColor ) )
{
	printf( "Unable to render time texture!\n" );
}

//Clear screen
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer );

//Render textures
gStartPromptTexture.render( ( SCREEN_WIDTH - gStartPromptTexture.getWidth() ) / 2, 0 );
gPausePromptTexture.render( ( SCREEN_WIDTH - gPausePromptTexture.getWidth() ) / 2, gStartPromptTexture.getHeight() );
gTimeTextTexture.render( ( SCREEN_WIDTH - gTimeTextTexture.getWidth() ) / 2, ( SCREEN_HEIGHT - gTimeTextTexture.getHeight() ) / 2 );

//Update screen
SDL_RenderPresent( gRenderer );

在渲染之前,我們將當前時間寫入一個字串流。我們之所以將其除以1000,是因為我們要的是秒,而每秒有1000毫秒。

之後我們將文字渲染成紋理,最後將所有的紋理繪製到螢幕上。

這裡下載本教程的媒體和原始碼。

原文連結

關注我的公眾號:程式設計之路從0到1
程式設計之路從0到1

相關文章