2011-12-18 68 views
0

我試圖複製Sonic physics engine的邏輯,這是寫在一個固定時間步進系統(60 FPS),在一個可變的時間步長(Slick2D,準確地說)。可變步時間和重力/摩擦

在原來按下跳轉按鈕時,玩家的velocity.y設置爲-6.5,並且每個勾號0.21875被添加到velocity.y以模擬重力。

每次我調用邏輯更新時,都會傳遞一個時間增量參數,指定已經通過了多少毫秒。如果更多millis已經超過了我的預期,那麼我重複更新邏輯,傳遞一個「內三角洲」,如果我們正在處理目標幀的「剩餘部分」,最多爲1或更小。

E.g.如果我們期望一個幀需要16ms,並且確實是需要16ms,則該循環將迭代一次並且將thisMiniTick作爲1傳遞。如果該增量不是16ms但是40ms,則該循環將執行三次,最後是0.5。

我錯誤地認爲,在每個內部更新循環中,我可以做velocity.y += (gravity * thisMiniTickRelative),但這不起作用。在較快的幀速率下,不會引起足夠的重力,導致較高的跳躍,而在較慢的幀率下,跳躍較低(儘管沒有明顯的地方)。

有沒有辦法做到這一點,幾乎適用於所有framerate,或者我必須求助於設置delta的上限和下限?

的「內更新」循環:

float timeRemaining = delta/1000f; 
    while(timeRemaining > 0) 
    { 
     float thisMiniTick = Math.min(timeRemaining, 1f/FRAMES_PER_SECOND); 
     float thisMiniTickRelative = thisMiniTick/(1f/FRAMES_PER_SECOND); 

     updateInput(container, game, thisMiniTickRelative); 
     if (playerAirState) 
     { 
      playerVelocity.y += (GRAVITY * thisMiniTickRelative); 
     } 
     clampPlayerVelocity(); 
     playerPosition.add(playerVelocity); 
     doCollisions(); 
     timeRemaining -= thisMiniTick; 
    } 

回答

2

不要把它想成「訴諸設置一個上限和下限爲delta」。您的應用程序和線程受制於您的應用程序的操作系統調度時間,系統的所有其他需求以及您需要注意的事項。這種挑戰在個人電腦遊戲領域與我們從單任務操作系統轉向多任務操作系統的日子一樣古老。

使用Slick,您可以(也應該)將您的邏輯更新從渲染更新中斷開,這就是爲什麼delta值會在您的應用程序中傳遞的原因。通過使用.setMinimumLogicUpdateInterval and .setMaximumUpdateInterval方法執行此操作。

在我已經開展的項目上,包括一個在Slick中的項目中,我發現每秒30-60個邏輯更新(更新之間爲30.3毫秒到16.6毫秒)中的任何內容都很好,並且可以爲您提供所需的平滑度從你的運動,物理和碰撞計算。

字面上看就是手段是什麼,每秒範圍30-60邏輯更新,要做到以下幾點:

container.setMinimumLogicUpdateInterval(16); // max 60 logic updates per second 
container.setMaximumLogicUpdateInterval(31); // min 30 logic updates per second 

而且,這是一個常見的錯誤,試圖計算timeRemaing值,但你不想這樣做。你只是想增加多少移動,多少時間過去。如果30毫秒已經過去了,那大概是1/33秒,所以你應該把你的遊戲對象移動1秒33秒。

float timeElapsed = delta/1000f; 

playerVelocity.y += (GRAVITY * timeElapsed); 

利用以上規定設置上限/下限,你肯定timeElapsed永遠是0.030.06之間的值。如果您的遊戲陷入停滯狀態並且幀頻變慢,那麼您的邏輯更新仍然不會超出這些範圍。相反,整個遊戲似乎會放慢速度(就像它應該,就像在屏幕上顯示太多的舊世嘉那樣),但碰撞和物理計算仍然可以按預期工作。

+0

感謝您的詳細解答,我很欣慰地看到沒有一些我無法想象的神奇解決方案。你爲什麼會認爲timeRemaining方法是一個錯誤?我原本的速度已經乘以時間流逝,但這意味着碰撞代碼需要大量重寫,因爲玩家可以在一個嘀嗒聲中行進超過一整塊的瓦片,從而「跳過」瓦片。 – 2011-12-19 08:07:43

+0

是的,我可能誇大了它,但我猜我在哪裏看到'timeremaining'出現錯誤的時候,人們試圖使用這個值來計算他們還有多少時間做其他事情,比如人工智能計算等等。這並不是說你不能使用它,只是通過各種方法來解決這個問題,把所有東西都加入到delta值中會給你提供最可靠的結果。 – jefflunt 2011-12-19 14:12:37

+0

對於因碰撞而移動速度過快的玩家,有幾種方法可以解決這個問題。一個是在玩家穿過的整個空間進行碰撞計算(如果玩家移動了3個方塊,那麼你需要計算出任何碰撞發生的碰撞並將其反彈回來;另一個更簡單方法是增加邏輯更新的粒度(調整最小/最大邏輯更新間隔),或者讓播放器移動得更慢 – jefflunt 2011-12-19 14:14:38