Memisahkan logika / pembaruan dari render / kode gambar dalam satu utas menggunakan sleep

9

Saya pernah membaca bahwa kecepatan objek game tidak boleh terhalang oleh FPS tetapi harus berdasarkan waktu. Bagaimana saya bisa memisahkan kode pembaruan / menggambar untuk memaksimalkan kinerja tanpa membatasi kecepatan menggambar dan memberikan tingkat pembaruan logika konstan berdasarkan waktu?

Kode pseudo saya saat ini adalah sebagai berikut

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

Masalahnya adalah kode gambar menghalangi kinerja tingkat pembaruan (). Dan ia mengkonsumsi 100% cpu karena jika tidur dilemparkan, ia membuang kedua fungsi menggambar / logika.

Saya juga menggunakan SDL dan sepertinya tidak memiliki opsi vsync. Saya juga pernah mendengar tentang istilah yang tetap dan variabel loncatan waktu namun saya tidak yakin bagaimana hal itu dapat dilakukan dengan tidur ()

Oskenso Kashi
sumber
1
Anda tidak perlu membuang daya CPU 100% hanya untuk menunggu, tidur (0) di akhir pengulangan sementara jika ticksLaps () <100. OS akan segera kembali ke utas jika tidak ada utas lain yang ingin lari. Tapi tidak membuang daya CPU 100% lagi.
Maik Semder
Namun, solusi terbaik untuk pengaturan 1 utas adalah menggunakan vsync, jika Anda tidak dapat vsync, maka panggil sleep (0) dalam satu lingkaran hingga Anda mencapai frame rate target, kemudian perbarui dan menggambar
Maik Semder

Jawaban:

3

Dalam cuplikan kode Anda, sepertinya Anda mencoba menjalankan game dalam mode langkah waktu tetap dengan sibuk menunggu jika menggambar dan memperbarui kurang dari 15 ms (60fps). Ini mungkin dan Anda menebak dengan benar bahwa ini tidak dapat dilakukan dengan menggunakan panggilan tidur karena Anda tidak tahu persis berapa lama Anda akan tidur. Loop sibuk-menunggu adalah solusi yang baik.

Namun pertimbangkan kasus di mana pembaruan dan menggambar Anda melebihi 15 ms, Anda sekarang memiliki permainan menggambar dan memperbarui menjadi lambat. Sekarang Anda dapat melakukan dua hal: mendeteksi keadaan ini dan menjatuhkan bingkai (lewati menggambar dan langsung memperbarui hingga Anda kembali sinkron) namun jika komputer melambat maka komputer tidak akan pernah mengejar.

Solusi lain adalah membuat logika pembaruan Anda tidak tergantung waktu. Anda tidak perlu utas terpisah untuk ini, Anda hanya perlu merespek seberapa cepat hal-hal harus bergerak. Alih-alih 5pixels per tick, Anda harus menggunakan 50pixels per detik. Anda akan memerlukan penghitung waktu presisi tinggi untuk mencapai hal ini, dan semua logika pembaruan Anda harus dapat mengakses penghitung waktu untuk melihat berapa banyak waktu yang berlalu sejak pembaruan terakhir.

Pada dasarnya Anda pergi dari:

void UpdatePlayer()
 player.x += 10;

Untuk

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;
Roy T.
sumber
Jadi mesin saya akan selalu mengkonsumsi 100% dan tidak ada yang bisa saya lakukan?
Oskenso Kashi
1
Ini akan menghabiskan banyak siklus seperti yang dimungkinkan oleh penjadwal, tetapi itu bukan masalah karena Anda tidak bisa melakukan banyak hal lain saat bermain game :).
Roy T.
@Oskenso Namun, itu adalah masalah jika Anda menggunakan lebih dari 1 utas, maka utas utama tidak akan membiarkan yang lain berjalan sebanyak yang mereka bisa, membuang banyak daya komputasi dalam loop sementara, Anda benar-benar harus mempertimbangkan tidur
Maik Semder
@Maik Semder: apakah Anda punya solusi untuk tidur (x) tidak akurat? Setelah interval tidur berlalu, utas siap dijalankan. Tetapi utas yang siap tidak dijamin untuk segera berjalan. Terserah penjadwal. Saat Anda menggunakan dua utas, ada solusi lain, untuk itu lihat artikel yang luar biasa ini: altdevblogaday.com/2011/07/03/threading-and-your-game-loop
Roy T.
1
@Roy sleep (0) adalah solusinya. Ia segera kembali jika tidak ada utas lain yang ingin dijalankan ( Sleep WinAPI ) dan memberikan utas lain peluang untuk berjalan. Jika utas lainnya tidak akan memberikan utas utama kesempatan untuk berjalan sebagai balasannya, maka Anda memiliki masalah threading, tetapi memblokir segala sesuatu yang lain dengan tidak memanggil sleep pada awalnya menjadikannya lebih buruk dan bukan solusi. Kuncinya adalah untuk memanggil sleep (0) dan menguji waktu yang telah berlalu hingga Anda mencapai target frame rate Anda, sehingga Anda tidak membuang-buang CPU 100% hanya untuk menunggu.
Maik Semder