EbitengineにおけるFPS (Frames Per Second)とTPS (Ticks Per Second)の意味と挙動の違いをまとめます。
はじめに
Ebitengineにおけるゲーム更新の概念として、
- FPS (Frames Per Second)
- TPS (Ticks Per Second)
の2つがあります。
当初これらの違いが分からず戸惑ったので、挙動と違いを備忘録にまとめます。
EbitengineにおけるFPSとTPSの違い
概要
まずFPSとTPSの特徴を整理します。
FPS | TPS | |
---|---|---|
概要 | 1秒間の描画処理更新頻度 | 1秒間のゲームロジック更新頻度 |
実装 | Draw() がFPSに基づいて呼ばれる |
Update() がTPSに基づいて呼ばれる |
制御 | 実行環境のディスプレイ設定によってFPSが決まる | Ebitengine側での設定値(例:SetTPS() )によってTPSが決まる |
FPSはDraw()
呼び出し頻度を指す描画処理、TPSはUpdate()
呼び出し頻度を指すゲームロジック更新、と捉えると理解しやすいです。
また重要な特徴として、FPSはプレイヤーの実行環境(ディスプレイのリフレッシュレート設定)によって決まる値であり、Ebitengine側で制御するものではない、という点があります。
TPS/Update()
の方はEbitengine側で制御する値(デフォルト60tps/SetTPS()
で設定可能)なので、処理の呼び出し頻度に影響するゲームロジックは基本的にUpdate()
で実装することになります。
検証
では、実際にFPSとTPSを制御し、それに基づいたDraw()
/Update()
の呼び出し回数を調べてみます。
下記のような簡単な検証コードを用意しました。
これを用いて、TPS設定とディスプレイリフレッシュレートの各条件下における、TPS/FPSの取得値およびUpdate()
/Draw()
の呼び出し回数を調べてみます。
package main import ( "log" "time" "github.com/hajimehoshi/ebiten/v2" ) const ( TPS = 60 ) type Game struct { updateCount int drawCount int perSec time.Time } func (g *Game) Update() error { now := time.Now() g.updateCount++ // Debug print per sec if now.Sub(g.perSec) >= time.Second { log.Printf("TPS: %.2f, FPS: %.2f", ebiten.ActualTPS(), ebiten.ActualFPS()) log.Printf("Update() was called in this sec: %d times", g.updateCount) log.Printf("Draw() was called in this sec: %d times\n\n", g.drawCount) g.updateCount = 0 g.drawCount = 0 g.perSec = now } return nil } func (g *Game) Draw(screen *ebiten.Image) { g.drawCount++ } func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { return 640, 480 } func main() { game := &Game{ perSec: time.Now(), } ebiten.SetWindowSize(640, 480) ebiten.SetWindowTitle("Show FPS and TPS with Update()/Draw()") ebiten.SetTPS(TPS) if err := ebiten.RunGame(game); err != nil { log.Fatal(err) } }
TPS60, ディスプレイリフレッシュレート60
まずはTPSを60に設定し、ディスプレイリフレッシュレートを60に設定した場合の結果がこちら:
2024/11/25 14:41:54 TPS: 59.92, FPS: 59.92 2024/11/25 14:41:54 Update() was called in this sec: 60 times 2024/11/25 14:41:54 Draw() was called in this sec: 60 times 2024/11/25 14:41:55 TPS: 60.09, FPS: 60.09 2024/11/25 14:41:55 Update() was called in this sec: 61 times 2024/11/25 14:41:55 Draw() was called in this sec: 61 times 2024/11/25 14:41:56 TPS: 59.98, FPS: 59.98 2024/11/25 14:41:56 Update() was called in this sec: 61 times 2024/11/25 14:41:56 Draw() was called in this sec: 61 times
TPS、FPSともに60を指し、Update()
とDraw()
も両方60回/秒の頻度で呼ばれていることが分かります。
TPS100, ディスプレイリフレッシュレート60
次にTPSを100に設定し、ディスプレイリフレッシュレートを変わらず60に設定した場合の結果がこちら:
2024/11/25 14:46:47 TPS: 100.32, FPS: 60.00 2024/11/25 14:46:47 Update() was called in this sec: 100 times 2024/11/25 14:46:47 Draw() was called in this sec: 60 times 2024/11/25 14:46:48 TPS: 100.41, FPS: 60.05 2024/11/25 14:46:48 Update() was called in this sec: 100 times 2024/11/25 14:46:48 Draw() was called in this sec: 60 times 2024/11/25 14:46:49 TPS: 99.92, FPS: 59.95 2024/11/25 14:46:49 Update() was called in this sec: 100 times 2024/11/25 14:46:49 Draw() was called in this sec: 60 times
TPS値は100になり、Update()
も1秒間に100回呼び出されていることがわかります。
一方でディスプレイリフレッシュレートは60から変更していないので、取得されたFPS値およびDraw()
の呼び出し回数は依然60/秒のまま変わりません。
TPS60, ディスプレイリフレッシュレート50
では最後に、TPSは60のまま、ディスプレイリフレッシュレートを50に変更してみます。
2024/11/25 15:04:59 TPS: 60.00, FPS: 50.00 2024/11/25 15:04:59 Update() was called in this sec: 60 times 2024/11/25 15:04:59 Draw() was called in this sec: 50 times 2024/11/25 15:05:00 TPS: 59.86, FPS: 50.05 2024/11/25 15:05:00 Update() was called in this sec: 61 times 2024/11/25 15:05:00 Draw() was called in this sec: 51 times 2024/11/25 15:05:01 TPS: 59.94, FPS: 49.95 2024/11/25 15:05:01 Update() was called in this sec: 60 times 2024/11/25 15:05:01 Draw() was called in this sec: 50 times
FPS値およびDraw()
の呼び出し回数が50/秒になりました。
プレイヤー実行環境のディスプレイ設定によって、FPS値とDraw()
呼び出し頻度が左右されていることがわかります。
一方、TPS値/Update()
呼び出し頻度は、FPS値に関係なく設定値60のままです。
以上、先述したFPS/TPSの特性が今回の検証によっても再現されました。
おわりに
今回はEbitengineにおけるTPSとFPSの特徴を整理し、その裏付けを取る簡単な検証をしました。
個人的に雰囲気でしか理解していない概念だったので、良い勉強になりました。
どなたかの参考になれば幸いです。
[関連記事]