Cukup pilih satu frame rate dan pertahankan. Itulah yang dilakukan oleh banyak permainan old-school - mereka berjalan pada tingkat tetap 50 atau 60 FPS, biasanya disinkronkan dengan kecepatan refresh layar, dan hanya merancang logika permainan mereka untuk melakukan semua yang diperlukan dalam interval waktu tetap itu. Jika, karena suatu alasan, itu tidak terjadi, game hanya perlu melompati frame (atau mungkin crash), secara efektif memperlambat gambar dan fisika game menjadi setengah kecepatan.
Secara khusus, permainan bahwa fitur digunakan seperti deteksi tabrakan hardware sprite cukup banyak harus bekerja seperti ini, karena permainan logika mereka terkait erat dengan rendering, yang dilakukan di hardware pada tingkat bunga tetap.
Gunakan cap waktu variabel untuk fisika game Anda. Pada dasarnya, ini berarti menulis ulang lingkaran permainan Anda agar terlihat seperti ini:
long lastTime = System.currentTimeMillis();
while (isRunning) {
long time = System.currentTimeMillis();
float timestep = 0.001 * (time - lastTime); // in seconds
if (timestep <= 0 || timestep > 1.0) {
timestep = 0.001; // avoid absurd time steps
}
update(timestep);
draw();
// ... sleep until next frame ...
lastTime = time;
}
dan, di dalam update()
, menyesuaikan rumus fisika untuk memperhitungkan timestep variabel, misalnya seperti ini:
speed += timestep * acceleration;
position += timestep * (speed - 0.5 * timestep * acceleration);
Satu masalah dengan metode ini adalah sulit untuk menjaga fisika (kebanyakan) tidak bergantung pada timestep ; Anda benar-benar tidak ingin pemain jarak bisa melompat bergantung pada frame rate mereka. Rumus yang saya perlihatkan di atas berfungsi dengan baik untuk akselerasi konstan, mis. Di bawah gravitasi (dan yang ada di pos tertaut cukup baik meskipun akselerasi bervariasi dari waktu ke waktu), tetapi bahkan dengan rumus fisika yang paling sempurna, bekerja dengan mengapung kemungkinan besar akan menghasilkan sedikit "noise numerik" yang, khususnya, dapat membuat replay yang tepat menjadi tidak mungkin. Jika itu adalah sesuatu yang Anda pikir Anda inginkan, Anda mungkin ingin memilih metode lain.
Pisahkan pembaruan dan gambar langkah-langkah. Di sini, idenya adalah Anda memperbarui status permainan menggunakan stempel waktu tetap, tetapi jalankan berbagai pembaruan di antara setiap frame. Artinya, lingkaran game Anda mungkin terlihat seperti ini:
long lastTime = System.currentTimeMillis();
while (isRunning) {
long time = System.currentTimeMillis();
if (time - lastTime > 1000) {
lastTime = time; // we're too far behind, catch up
}
int updatesNeeded = (time - lastTime) / updateInterval;
for (int i = 0; i < updatesNeeded; i++) {
update();
lastTime += updateInterval;
}
draw();
// ... sleep until next frame ...
}
Untuk membuat gerakan yang dirasakan lebih lancar, Anda mungkin juga ingin agar draw()
metode Anda menginterpolasi hal-hal seperti posisi objek dengan lancar antara kondisi permainan sebelumnya dan berikutnya. Ini berarti Anda harus melewati offset interpolasi yang benar ke draw()
metode, misalnya seperti ini:
int remainder = (time - lastTime) % updateInterval;
draw( (float)remainder / updateInterval ); // scale to 0.0 - 1.0
Anda juga perlu memiliki update()
metode yang benar-benar menghitung keadaan permainan selangkah lebih maju (atau mungkin beberapa, jika Anda ingin melakukan interpolasi spline tingkat tinggi), dan menyimpannya pada posisi objek sebelumnya sebelum memperbaruinya, sehingga draw()
metode ini dapat menginterpolasi diantara mereka. (Mungkin juga untuk meramalkan posisi yang diprediksi berdasarkan kecepatan dan percepatan objek, tetapi ini bisa terlihat tersentak terutama jika objek bergerak dengan cara yang rumit, menyebabkan prediksi sering gagal.)
Salah satu keuntungan dari interpolasi adalah bahwa, untuk beberapa jenis gim, ini dapat memungkinkan Anda untuk mengurangi laju pembaruan logika gim secara signifikan, sambil tetap mempertahankan ilusi gerak yang halus. Misalnya, Anda mungkin dapat memperbarui status permainan Anda saja, katakanlah, 5 kali per detik, sementara masih menggambar 30 hingga 60 frame interpolasi per detik. Dalam melakukan hal ini, Anda mungkin juga ingin mempertimbangkan interleaving logika game Anda dengan gambar (yaitu memiliki parameter untuk update()
metode Anda yang memberitahukannya untuk menjalankan x % dari pembaruan penuh sebelum kembali), dan / atau menjalankan permainan fisika / logika dan kode rendering di utas terpisah (waspadalah terhadap gangguan sinkronisasi!).