Bagaimana cara menghindari "efek tangga" dalam gerak pixel art?

21

Saya membuat sprite pada koordinat piksel yang tepat untuk menghindari efek kabur yang disebabkan oleh antialiasing (sprite adalah pixel-art dan akan terlihat mengerikan jika difilter). Namun, karena pergerakan objek melibatkan kecepatan variabel, gravitasi, dan interaksi fisik, lintasan dihitung dengan presisi subpixel.

Pada kecepatan screenspace yang cukup besar (lebih besar dari 2 atau 3 piksel) ini bekerja dengan sangat baik. Namun, ketika kecepatan kecil, efek tangga yang nyata dapat muncul, terutama di sepanjang garis diagonal. Ini bukan masalah lagi pada kecepatan screenspace yang sangat lambat (v << 1 pixel per detik) jadi saya hanya mencari solusi untuk nilai kecepatan menengah.

Di sebelah kiri adalah lintasan diplot untuk kecepatan besar, diperoleh dengan pembulatan koordinat objek yang sederhana. Di tengah Anda dapat melihat apa yang terjadi ketika kecepatan menjadi lebih kecil, dan efek tangga yang saya bicarakan. Di sebelah kanan, lokus lintasan yang ingin saya dapatkan.

koordinat piksel untuk lintasan objek

Saya tertarik pada gagasan algoritme untuk memfilter lintasan untuk meminimalkan aliasing, sambil mempertahankan perilaku asli pada kecepatan besar dan kecil. Saya memiliki akses ke ,t, posisi dan kecepatan instan, serta jumlah nilai sebelumnya yang berubah-ubah, tetapi karena ini adalah simulasi waktu nyata, saya tidak tahu tentang nilai-nilai masa depan (meskipun jika perlu, estimasi dapat diekstrapolasi dengan asumsi tertentu) . Perhatikan bahwa karena simulasi fisika, perubahan arah mendadak juga dapat terjadi.

sam hocevar
sumber

Jawaban:

18

Inilah garis besar cepat, dari atas kepala saya, dari suatu algoritma yang seharusnya bekerja dengan cukup baik.

  1. Pertama, hitung arah objek yang bergerak, dan periksa apakah lebih dekat ke horizontal atau vertikal.
  2. Jika arahnya lebih dekat ke vertikal (horizontal), sesuaikan posisi objek di sepanjang vektor arah ke pusat baris piksel terdekat (kolom).
  3. Membulatkan posisi ke tengah piksel terdekat.

Dalam pseudocode:

if ( abs(velocity.x) > abs(velocity.y) ) {
    x = round(position.x);
    y = round(position.y + (x - position.x) * velocity.y / velocity.x);
} else {
    y = round(position.y);
    x = round(position.x + (y - position.y) * velocity.x / velocity.y);
}

Sunting: Yap, teruji, bekerja cukup baik.

Ilmari Karonen
sumber
+1, ini bekerja dengan sangat baik! Saya perhatikan lompatan mundur aneh dengan gerakan memutar pada kecepatan lambat, karena penyesuaian dapat dilakukan ke arah yang berlawanan dengan vektor kecepatan (yang biasanya OK, tetapi tidak dengan lekukan lintasan kecil). Itu bisa diselesaikan dengan mengalikannya velocity.y / velocity.xdengan faktor koreksi yang sebanding dengan kecepatannya.
sam hocevar
@ Sam: Maksud Anda radius belok kecil (= kelengkungan tinggi), bukan? Itu memang bisa menyebabkan masalah dengan ekstrapolasi linier pada kecepatan rendah. (Pada dasarnya, ini berfungsi selama kecepatan kuadrat per akselerasi jauh lebih besar dari 1 piksel.) Salah satu solusi yang mungkin (klugey) mungkin dengan mengingat posisi bulat terakhir dan menggunakannya kembali jika lebih dekat ke posisi sebenarnya daripada yang baru dihitung. (Seseorang juga dapat mencoba ekstrapolasi tingkat tinggi, tetapi formula menjadi sangat buruk.)
Ilmari Karonen
Memang, maksudku radius kecil. Salahku. Dan terima kasih untuk petunjuk tambahan; kinerja tidak penting di sana, jadi saya mampu meningkatkan kualitas.
sam hocevar
3

Tidak banyak yang dapat Anda lakukan untuk dunia berbasis fisika umum. Jika semua objek Anda bergerak di sepanjang garis atau lingkaran tertentu, Anda bisa melakukan sesuatu. Tetapi Anda beroperasi di bawah fisika aktual. Objeknya adalah tempat fisika meletakkannya; Anda hanya menggambar perkiraan berbasis pixel dari lokasi itu.

Biasanya ini adalah sesuatu yang harus Anda terima jika Anda ingin tetap menggunakan koordinat piksel. Seharusnya tidak terlalu terlihat kecuali Anda menampilkan pada resolusi yang sangat kecil (kurang dari 640x480, meskipun itu tergantung pada resolusi dan ukuran asli layar).

Nicol Bolas
sumber
Bahkan pada resolusi tinggi, rendering ditingkatkan (tetangga terdekat) untuk meningkatkan tampilan oldschool. Ini adalah keputusan arahan artistik.
sam hocevar
@SamHocevar: Jika Anda ingin "penampilan oldschool", kenapa tidak Anda ingin penuh "oldschool penampilan"? Mengapa menaiki tangga, yang akan dimiliki oleh game "oldschool" apa pun, bukan bagian dari keseluruhan efek yang ingin Anda capai?
Nicol Bolas
Saya tidak berpikir permainan oldschool yang layak akan menerapkan gerakan diagonal yang memiliki efek tangga, karena itu akan terlihat seperti omong kosong. Tidak terlihat seperti omong kosong adalah bagian utama dari efek oldschool yang ingin saya capai :-)
sam hocevar
@SamHocevar: Sebagian besar game jadul adalah game aksi, dan karenanya tidak bergerak cukup lambat untuk menyadarinya. Mereka juga cenderung tidak bergerak di sepanjang kurva. Permainan khususnya yang saya pikirkan adalah Solar Jetman, yang sangat memiliki efek ini ketika bergerak lambat. Memang, kamera selalu berpusat pada Anda, sehingga Anda memperhatikannya dalam pergerakan dunia, tetapi sangat banyak di sana.
Nicol Bolas
3

Saat gerakan tertunda tegak lurus dengan gerakan terakhir (dalam ruang layar), abaikan saja dan gunakan koordinat layar terakhir. Jika itu menyebabkan gagap yang seburuk tangga, Anda dapat mencoba memindahkan jumlah dari gerakan yang tertunda dan terakhir.

Saya pikir masalahnya terletak pada v <sqrt (2). v> sqrt (2) harus selalu bergerak setidaknya diagonal penuh, menghindari efek tangga. Mungkin berguna untuk pemangkasan yang membutuhkan perbandingan gerakan sebelumnya.

mghicks
sumber
+1 untuk menunjukkan batas atas untuk saran v. Ilmari lebih detail tetapi Anda memberikan informasi yang bermanfaat.
sam hocevar