2010-10-18 54 views
4

我目前有一些接近以下實現的基於物理的遊戲的FPS獨立遊戲循環。它在我測試過的每臺電腦上運行得非常好,在幀速下降時保持遊戲速度一致。然而,我將要移植到嵌入式設備上,這些嵌入式設備可能會在視頻方面更加艱難,我想知道它是否還會削減芥末。這是一個FPS獨立遊戲循環的良好實現嗎?

編輯:

對於這個問題,假設毫秒()返回該程序運行毫秒經過的時間。 msecs的實現在不同平臺上有所不同。這個循環也在不同的平臺上以不同的方式運行。

#define MSECS_PER_STEP 20 
int stepCount, stepSize; // these are not globals in the real source 

void loop() { 
    int i,j; 
    int iterations =0; 
    static int accumulator; // the accumulator holds extra msecs 
    static int lastMsec; 
    int deltatime = msec() - lastMsec; 
    lastMsec = msec(); 

    // deltatime should be the time since the last call to loop 
    if (deltatime != 0) { 
     // iterations determines the number of steps which are needed 
     iterations = deltatime/MSECS_PER_STEP; 

     // save any left over millisecs in the accumulator 
     accumulator += deltatime%MSECS_PER_STEP; 
    } 
    // when the accumulator has gained enough msecs for a step... 
    while (accumulator >= MSECS_PER_STEP) { 
     iterations++; 
     accumulator -= MSECS_PER_STEP; 
    } 
    handleInput(); // gathers user input from an event queue 
    for (j=0; j<iterations; j++) { 
     // here step count is a way of taking a more granular step 
     // without effecting the overall speed of the simulation (step size) 
     for (i=0; i<stepCount; i++) { 
      doStep(stepSize/(float) stepCount); // forwards the sim 
     } 
    } 
} 
+0

我知道我可能不應該在整數中存儲時間的東西,但這不是真的我在這裏問 – 2010-10-18 16:08:01

回答

6

我只是有一些意見。首先是你沒有足夠的評論。有些地方不清楚你想要做什麼,所以很難說是否有更好的方法來做到這一點,但是當我來找他們時,我會指出這些。首先,雖然:

#define MSECS_PER_STEP 20 
int stepCount, stepSize; // these are not globals in the real source 

void loop() { 
    int i,j; 
    int iterations =0; 

    static int accumulator; // the accumulator holds extra msecs 
    static int lastMsec; 

這些不會被初始化爲任何東西。可能會變成0,但你應該初始化它們。另外,不要聲明它們是靜態的,你可能想要考慮將它們放入一個結構中,通過引用將它們傳遞給loop

int deltatime = msec() - lastMsec; 

由於lastMsec沒有(初始化,並可能爲0),這可能開始作爲一個大的增量。

lastMsec = msec(); 

該行與最後一行一樣,調用msec。這可能意味着「當前時間」,並且這些調用非常接近,以至於兩次調用的返回值可能都是相同的,這可能也是您所期望的,但仍可以調用該函數兩次。您應該將這些行更改爲int now = msec();int deltatime = now - lastMsec;lastMsec = now;以避免兩次調用此函數。當前獲取函數的時間通常比您想象的要高得多。

if (deltatime != 0) { 
     iterations = deltatime/MSECS_PER_STEP; 
     accumulator += deltatime%MSECS_PER_STEP; 
    } 

你應該在這裏有一個評論,說這是什麼一樣,以及上面 ,說什麼變量意在表示評論。

while (accumulator >= MSECS_PER_STEP) { 
     iterations++; 
     accumulator -= MSECS_PER_STEP; 
    } 

此循環需要註釋。它也不需要在那裏。看起來它可能已被替換爲iterations += accumulator/MSECS_PER_STEP;accumulator %= MSECS_PER_STEP;。在任何有硬件分區的機器上,分區和模數的運行時間應該比循環更短,更一致。

handleInput(); // gathers user input from an event queue 

    for (j=0; j<iterations; j++) { 
     for (i=0; i<stepCount; i++) { 
      doStep(stepSize/(float) stepCount); // forwards the sim 
     } 
    } 

環路獨立輸入的步驟將使得如果它不執行緩慢,並得到後面的比賽沒有反應的效果。至少看起來,如果遊戲得到落後,所有的輸入都會開始疊加起來並且一起執行,並且所有的遊戲中的時間都會在一個塊中傳遞。這是失敗的優雅方式。

此外,我可以猜到j循環(外循環)的含義,但內循環我不太清楚。此外,傳遞給doStep函數的值 - 這意味着什麼。

} 

這是最後一個花括號。我認爲它看起來很孤獨。

我不知道發生了什麼,只要不管你打電話給你的loop函數,這可能是你無法控制的,這可能決定了這個函數的功能以及它的外觀,但如果不是,我希望你會重新考慮結構。我相信一個更好的方法是創建一個重複調用的函數,但當時只有一個事件(在相對較短的時間內定期發佈)。這些事件可以是用戶輸入事件或計時器事件。用戶輸入事件只是設置事件以對下一個計時器事件作出反應。 (當你沒有任何事件來處理你的睡眠時)

你應該總是假定每個計時器事件都以相同的週期進行處理,儘管如果處理過程可能會有一些漂移。這裏你可能會注意到的主要怪異情況是,如果遊戲在處理計時器事件上落後,然後再次趕上游戲中的時間可能會減慢(低於實時),然後加快(到實時),並且然後慢下來(到實時)。

的方式來處理這包括只允許一個計時器事件是在事件隊列在同一時間,這將導致時間出現放緩(以下實時),然後加快備份(真實時間)沒有超級速度區間。

另一種方法來做到這一點,它的功能與您所擁有的功能類似,將處理每個計時器事件的最後一步是排隊下一個計時器事件(請注意,其他人不應發送計時器事件{除了第一個},如果這是你選擇實施遊戲的方式)。這意味着要消除定時器事件之間的固定時間間隔,並且還會限制程序休眠的能力,因爲至少在每次檢查事件隊列時都會有一個要處理的計時器事件。

+0

夢幻般的答案 - 這裏有很多想法。我會添加一些評論。 – 2010-10-18 20:02:06

+1

靜態意味着它們爲零。 – 2010-10-18 22:16:34