Momentum dan urutan masalah pembaruan di mesin fisika saya

22

masukkan deskripsi gambar di sini

Pertanyaan ini adalah pertanyaan "tindak lanjut" dari pertanyaan saya sebelumnya, mengenai deteksi dan resolusi tabrakan, yang dapat Anda temukan di sini .


Jika Anda tidak ingin membaca pertanyaan sebelumnya, inilah uraian singkat tentang cara kerja mesin fisika saya:

Setiap entitas fisik disimpan dalam kelas yang disebut SSSPBody.

Hanya AABB yang didukung.

Setiap SSSPBody disimpan dalam kelas yang disebut SSSPWorld, yang memperbarui setiap tubuh dan menangani gravitasi.

Setiap frame, SSSPWorld memperbarui setiap orang.

Setiap badan yang diperbarui mencari benda-benda terdekat dalam hash spasial, memeriksa apakah mereka perlu mendeteksi tabrakan dengan mereka. Jika ya, mereka meminta acara "tabrakan" dan memeriksa apakah mereka perlu menyelesaikan tabrakan dengan mereka. Jika ya, mereka menghitung vektor penetrasi dan tumpang tindih terarah, kemudian mengubah posisi mereka untuk menyelesaikan penetrasi.

Ketika sebuah tubuh bertabrakan dengan yang lain, ia mentransfer kecepatannya ke yang lain hanya dengan mengatur kecepatan tubuh ke miliknya.

Kecepatan benda ditetapkan ke 0 bila posisi tidak berubah dari bingkai terakhir. Jika juga bertabrakan dengan benda yang bergerak (seperti lift atau platform yang bergerak), ia menghitung perbedaan gerakan lift untuk melihat apakah benda tersebut belum bergerak dari posisi terakhirnya.

Juga, tubuh memanggil peristiwa "hancur" ketika semua sudut AABB tumpang tindih sesuatu dalam bingkai.

Ini adalah kode sumber LENGKAP game saya. Ini dibagi dalam tiga proyek. SFMLStart adalah perpustakaan sederhana yang menangani input, menggambar, dan memperbarui entitas. SFMLStartPhysics adalah yang paling penting, di mana kelas SSSPBody dan SSSPWorld berada. PlatformerPhysicsTest adalah proyek game, berisi semua logika game.

Dan ini adalah metode "pembaruan" di kelas SSSPBody, berkomentar dan disederhanakan. Anda dapat melihat ini hanya jika Anda tidak ingin melihat keseluruhan proyek SFMLStartSimplePhysics. (Dan bahkan jika Anda melakukannya, Anda masih harus melihat ini sejak berkomentar.)


Gif menunjukkan dua masalah.

  1. Jika badan ditempatkan dalam urutan yang berbeda, hasil yang berbeda terjadi. Peti di sebelah kiri identik dengan peti di sebelah kanan, hanya ditempatkan dalam urutan terbalik (di editor).
  2. Kedua peti harus didorong ke bagian atas layar. Dalam situasi di sebelah kiri, tidak ada peti yang didorong. Di sebelah kanan, hanya satu dari mereka. Kedua situasi itu tidak disengaja.

Masalah pertama: urutan pembaruan

Ini cukup mudah dimengerti. Dalam situasi di sebelah kiri, peti paling atas diperbarui sebelum yang lain. Bahkan jika peti di bagian bawah "mentransfer" kecepatan ke yang lain, ia harus menunggu frame berikutnya untuk bergerak. Karena tidak bergerak, kecepatan peti bawah diatur ke 0.

Saya tidak tahu bagaimana cara memperbaikinya. Saya lebih suka solusi untuk tidak bergantung pada "menyortir" daftar pembaruan, karena saya merasa saya melakukan sesuatu yang salah di seluruh desain mesin fisika.

Bagaimana mesin fisika utama (Box2D, Bullet, Chipmunk) menangani urutan pembaruan?


Masalah kedua: hanya satu peti yang didorong ke langit-langit

Saya belum mengerti mengapa ini terjadi. Apa yang dilakukan entitas "pegas" adalah mengatur kecepatan tubuh menjadi -4000 dan menempatkannya kembali di atas pegas itu sendiri. Bahkan jika saya menonaktifkan kode pemosisian ulang, masalah masih terjadi.

Ide saya adalah ketika peti bawah bertabrakan dengan peti atas, kecepatannya diatur ke 0. Saya tidak yakin mengapa ini terjadi.


Meskipun ada kemungkinan terlihat seperti seseorang yang menyerah pada masalah pertama, saya memposting seluruh kode sumber proyek di atas. Saya tidak punya apa-apa untuk membuktikannya, tetapi percayalah, saya berusaha keras untuk memperbaikinya, tetapi saya tidak bisa menemukan solusi dan saya tidak punya pengalaman sebelumnya dengan fisika dan tabrakan. Saya sudah mencoba menyelesaikan dua masalah ini selama lebih dari seminggu dan sekarang saya putus asa.

Saya tidak berpikir saya dapat menemukan solusi sendiri tanpa menghilangkan banyak fitur dari permainan (transfer kecepatan dan pegas, misalnya).

Terima kasih banyak untuk waktu yang dihabiskan untuk membaca pertanyaan ini, dan terima kasih lebih banyak lagi jika Anda bahkan mencoba mencari solusi atau saran.

Vittorio Romeo
sumber
Setiap kali Anda menumpuk kotak, Anda dapat menggabungkan fisika mereka sehingga mereka dianggap sebagai objek tunggal?
CiscoIPPhone

Jawaban:

12

Sebenarnya, urutan masalah pembaruan cukup umum untuk mesin fisika impuls normal, Anda tidak bisa hanya menunda menerapkan gaya seperti yang disarankan Vigil, Anda akan berakhir merusak pelestarian energi ketika sebuah objek bertabrakan secara bersamaan dengan 2 lainnya. Biasanya mereka berhasil membuat sesuatu yang tampak sangat nyata, meskipun urutan pembaruan yang berbeda akan menghasilkan hasil yang sangat berbeda.

Dalam kasus apa pun, untuk tujuan Anda, ada cukup banyak cegukan dalam sistem impuls yang saya sarankan Anda membangun model semi-spring.

Ide dasarnya adalah bahwa alih-alih mencoba menyelesaikan tabrakan dalam satu langkah Anda menerapkan gaya ke objek bertabrakan, gaya ini harus setara dengan jumlah tumpang tindih antara objek, ini sebanding dengan bagaimana benda nyata selama tabrakan mengubah mereka memindahkan energi ke deformasi dan kemudian kembali ke gerakan, hal yang hebat tentang sistem ini adalah memungkinkan kekuatan untuk melakukan perjalanan melalui suatu objek tanpa objek itu harus memantul ke depan dan ke belakang, dan itu dapat dilakukan secara lengkap dengan urutan pembaruan yang independen.

Agar objek terhenti daripada terpental tanpa batas Anda harus menerapkan beberapa bentuk peredam, Anda dapat sangat mempengaruhi gaya dan nuansa permainan Anda tergantung pada bagaimana Anda melakukannya, tetapi pendekatan yang sangat mendasar adalah dengan menerapkan kekuatan pada dua objek menyentuh yang setara dengan gerakan internal mereka, Anda dapat memilih untuk menerapkannya hanya ketika mereka bergerak satu sama lain, atau juga ketika mereka bergerak menjauh satu sama lain, yang terakhir dapat digunakan untuk sepenuhnya mencegah benda memantul kembali ketika mereka menyentuh tanah, tetapi juga akan membuat mereka sedikit lengket.

Anda juga dapat membuat efek gesekan dengan mengerem objek ke arah tegak lurus tabrakan, jumlah pengereman harus setara dengan jumlah tumpang tindih.

Anda dapat menyiasati konsep massa dengan cukup mudah dengan membuat semua benda memiliki massa yang sama, dan benda tak bergerak akan bekerja seperti memiliki massa tak terbatas jika Anda mengabaikan pengesahannya.

Beberapa kode semu, kalau-kalau kode di atas tidak cukup jelas:

//Presuming that you have done collision checks between two objects and now have  
//numbers for how much they overlap in each direction.
overlapX
overlapY
if(overlapX<overlapY){ //Do collision in direction X
    if(obj1.X>obj2.X){
        swap(obj1,obj2)
    }
    //Spring effect:
    obj1.addXvelocity-=overlapX*0.1 //Constant, the lower this is set the softer the  
                                    //collision will be.
    obj2.addXvelocity+=overlapX*0.1
    //Dampener effect:
    velocityDifference=obj2.Xvelocity-obj1.Xvelocity
    //velocityDifference=min(velocityDifference,0) //Uncomment to only dampen when  
                                                   //objects move towards each other.
    obj1.addXvelocity+=velocityDifference*0.1 //Constant, higher for more dampening.
    obj2.addXvelocity-=velocityDifference*0.1
    //Friction effect:
    if(obj1.Yvelocity>obj2.Yvelocity){
        swap(obj1,obj2)
    }
    friction=overlapX*0.01
    if(2*friction>obj2.Yvelocity-obj1.Yvelocity){
        obj1.addYvelocity+=(obj2.Yvelocity-obj1.Yvelocity)/2
        obj2.addYvelocity-=(obj2.Yvelocity-obj1.Yvelocity)/2
    }
    else{
        obj1.addYvelocity+=friction
        obj2.addYvelocity-=friction
    }
}
else{ //Do collision in direction Y

}

Titik sifat addXvelocity dan addYvelocity adalah bahwa ini ditambahkan ke kecepatan objek mereka setelah semua penanganan tabrakan dilakukan.

Sunting:
Anda dapat melakukan hal-hal dalam urutan berikut, di mana setiap peluru harus dilakukan pada semua elemen sebelum yang berikutnya dilakukan:

  • Mendeteksi tabrakan, mereka dapat diselesaikan segera setelah mereka terdeteksi.
  • Tambahkan nilai addVelocity ke nilai kecepatan, tambahkan gravitasi Yvelocity, reset nilai addVelocity ke 0, pindahkan objek sesuai dengan kecepatannya.
  • Jadikan adegan itu.

Juga, saya menyadari bahwa hal-hal berikut mungkin tidak sepenuhnya jelas dalam posting awal saya, di bawah pengaruh benda-benda gravitasi akan tumpang tindih ketika beristirahat di atas satu sama lain, ini menunjukkan bahwa kotak tabrakan mereka harus sedikit lebih tinggi daripada representasi grafis mereka untuk menghindari tumpang tindih secara visual. Masalah ini akan lebih kecil jika fisika dijalankan pada tingkat pembaruan yang lebih tinggi. Saya sarankan Anda mencoba berjalan pada 120Hz untuk kompromi yang masuk akal antara waktu CPU dan akurasi fisika.

Sunting2:
Aliran mesin fisika yang sangat mendasar:

  • Tabrakan dan gravitasi menghasilkan gaya / akselerasi. acceleration = [Complicated formulas]
  • Gaya / akselerasi ditambahkan ke kecepatan. velocity += acceleration
  • Kecepatan ditambahkan ke posisi. position += velocity
aaaaaaaaaaaa
sumber
Terlihat bagus, tidak pernah memikirkan musim semi massal untuk platformer. Acungan jempol untuk sesuatu yang mencerahkan :)
EnoughTea
Saya akan mencoba menerapkan ini dalam beberapa jam, ketika saya kembali ke rumah. Haruskah saya memindahkan (Posisi + = Kecepatan) tubuh secara bersamaan kemudian memeriksa tabrakan, atau memindahkan dan memeriksa tabrakan satu per satu? [Juga, apakah saya harus memodifikasi posisi secara manual untuk menyelesaikan tabrakan? Atau apakah mengubah kecepatan akan mengatasinya?]
Vittorio Romeo
Saya tidak sepenuhnya yakin bagaimana menafsirkan pertanyaan pertama Anda. Resolusi tabrakan akan mengubah kecepatan, dan dengan demikian hanya secara tidak langsung mempengaruhi posisi.
aaaaaaaaaaaa
Faktanya adalah bahwa saya memindahkan entitas dengan mengatur kecepatannya secara manual ke nilai tertentu. Untuk mengatasi tumpang tindih, saya menghapus jarak tumpang tindih dari posisi mereka. Jika saya menggunakan metode Anda, apakah saya harus memindahkan entitas dengan menggunakan kekuatan atau sesuatu yang lain? Saya belum pernah melakukan itu sebelumnya.
Vittorio Romeo
Secara teknis, ya Anda harus menggunakan kekuatan, dalam kode saya itu disederhanakan sedikit dengan semua benda yang memiliki bobot 1, dan karenanya memaksa sama dengan akselerasi.
aaaaaaaaaaaa
14

Yah, Anda jelas bukan seseorang yang mudah menyerah, Anda benar-benar manusia besi, saya akan melemparkan tangan saya jauh lebih awal, karena proyek ini memiliki kemiripan yang kuat dengan hutan rumput laut :)

Pertama-tama, posisi dan kecepatan diatur di semua tempat, dari sudut pandang subsistem fisika, itu adalah resep untuk bencana. Juga, ketika mengubah hal-hal yang tidak terpisahkan oleh berbagai subsistem, buat metode pribadi seperti "ChangeVelocityByPhysicsEngine", "ChangeVelocityBySpring", "LimitVelocity", "LimitVelocity", "TransferVelocity" atau sesuatu seperti itu. Ini akan menambah kemampuan memeriksa perubahan yang dibuat oleh bagian logika tertentu dan memberikan makna tambahan pada perubahan kecepatan ini. Dengan begitu debugging akan lebih mudah.

Masalah pertama.

Ke pertanyaan itu sendiri. Sekarang Anda hanya menerapkan perbaikan posisi dan kecepatan "sambil berjalan" dalam urutan tampilan dan logika permainan. Itu tidak akan berhasil untuk interaksi yang kompleks tanpa dengan susah payah mengkodekan fisika dari setiap hal yang kompleks. Mesin fisika yang terpisah tidak diperlukan saat itu.

Untuk melakukan interaksi yang kompleks tanpa peretasan, Anda perlu menambahkan langkah tambahan antara mendeteksi tabrakan berdasarkan posisi yang diubah oleh kecepatan awal dan perubahan posisi akhir berdasarkan "kecepatan setelah". Saya membayangkan akan seperti ini:

  • mengintegrasikan kecepatan menggunakan semua gaya yang bekerja pada benda (Anda menerapkan perbaikan kecepatan secara langsung sekarang, tinggalkan perhitungan kecepatan untuk mesin fisika Anda dan gunakan kekuatan untuk memindahkan sesuatu sebagai gantinya) , kemudian gunakan kecepatan baru untuk mengintegrasikan posisi.
  • mendeteksi tabrakan, lalu mengembalikan kecepatan dan posisi,
  • kemudian memproses tabrakan (menggunakan impuls tanpa pembaruan posisi langsung, ofc, hanya kecepatan yang diubah sampai langkah terakhir)
  • mengintegrasikan kecepatan baru lagi dan memproses semua tabrakan menggunakan impuls lagi, kecuali sekarang tabrakan tidak elastis.
  • membuat integrasi akhir posisi menggunakan kecepatan yang dihasilkan.

Hal-hal tambahan mungkin muncul, seperti berurusan dengan menyentak, penolakan untuk menumpuk ketika FPS kecil, atau hal-hal lain seperti itu, bersiaplah :)

Masalah kedua

Kecepatan vertikal kedua krat "bobot mati" itu tidak pernah berubah dari nol. Anehnya, dalam loop Pembaruan PhysSpring Anda menetapkan kecepatan, tetapi dalam loop Pembaruan PhysCrate itu sudah nol. Mungkin saja menemukan garis di mana kecepatan salah, tetapi saya berhenti men-debug di sini karena ini situasi "Menuai Apa yang Anda Jahit". Sudah waktunya untuk berhenti coding dan mulai memikirkan kembali semuanya ketika debugging menjadi sulit. Tetapi jika sampai pada titik di mana tidak mungkin bahkan bagi pembuat kode untuk memahami apa yang terjadi dalam kode, maka basis kode Anda sudah mati tanpa Anda sadari :)

Masalah ketiga

Saya pikir ada sesuatu yang tidak beres ketika Anda harus membuat ulang sebagian Farseer untuk membuat platformer berbasis ubin yang sederhana. Secara pribadi, saya akan menganggap mesin Anda saat ini sebagai pengalaman yang luar biasa, dan kemudian membuangnya sepenuhnya untuk fisika berbasis ubin yang lebih sederhana dan langsung. Sambil melakukan itu, akan lebih bijaksana untuk mengambil hal-hal seperti Debug. Masukkan dan mungkin bahkan, oh horor, unit test, karena akan mungkin untuk menangkap hal-hal yang tidak terduga sebelumnya.

Cukup
sumber
Saya menyukai perbandingan "hutan rumput laut" itu.
Den
Sebenarnya, saya agak malu menggunakan kata-kata seperti itu, tetapi saya merasa jika itu menghasilkan satu atau dua refactoring, maka itu akan dibenarkan.
EnoughTea
Dengan hanya satu tes pada t tidak akan selalu ada kemungkinan bahwa ini terjadi? Saya akan membayangkan bahwa Anda perlu mengintegrasikan kecepatan di t dan kemudian memeriksa tabrakan di t +1 sebelum menetapkan kecepatan ke 0?
Jonathan Connell
Yap, kami mendeteksi tabrakan di depan setelah mengintegrasikan keadaan awal ke depan dari t ke t + dt menggunakan Runge-Kutta atau sesuatu.
EnoughTea
"mengintegrasikan kecepatan menggunakan semua kekuatan yang bekerja pada tubuh" "tinggalkan perhitungan kecepatan ke mesin fisika Anda" - Saya mengerti apa yang Anda coba katakan, tetapi saya tidak tahu bagaimana melakukan ini. Apakah ada contoh / artikel yang bisa Anda tunjukkan?
Vittorio Romeo
7

Ketika sebuah tubuh bertabrakan dengan yang lain, ia mentransfer kecepatannya ke yang lain hanya dengan mengatur kecepatan tubuh ke miliknya.

Masalah Anda adalah bahwa ini pada dasarnya adalah asumsi yang salah tentang gerak, sehingga apa yang Anda dapatkan tidak menyerupai gerak karena Anda terbiasa dengannya.

Ketika sebuah tubuh bertabrakan dengan yang lain, momentum dipertahankan. Memikirkan ini sebagai "A hits B" versus "B hits A" adalah dengan menerapkan kata kerja transitif ke situasi intransitif. A dan B bertabrakan; momentum yang dihasilkan harus sama dengan momentum awal. Artinya, jika A dan B memiliki massa yang sama, mereka sekarang bepergian dengan rata-rata kecepatan aslinya.

Anda juga mungkin akan membutuhkan tabrakan dan pemecah iteratif, atau Anda akan mengalami masalah stabilitas. Anda mungkin harus membaca beberapa presentasi GDC Erin Catto.


sumber
2
Mereka hanya akan mendapatkan rata-rata kecepatan asli jika tumbukan benar-benar tidak elastis, misalnya A dan B adalah potongan-potongan adonan.
Mikael Öhman
"Dengan hanya mengatur kecepatan tubuh ke miliknya sendiri". Pernyataan seperti inilah yang menjelaskan mengapa itu tidak berfungsi. Secara umum saya selalu menemukan bahwa orang yang tidak berpengalaman menulis sistem fisika tanpa memahami prinsip-prinsip dasar yang terlibat. Anda tidak pernah 'hanya mengatur kecepatan', atau 'cukup ...'. Setiap modifikasi properti tubuh harus merupakan aplikasi langsung dari hukum dinamika; termasuk konservasi momentum, energi, dll. Ya akan selalu ada faktor fudge untuk mengimbangi ketidakstabilan, tetapi pada titik mana pun Anda tidak dapat secara ajaib mengubah kecepatan tubuh.
MrCranky
Paling mudah untuk mengasumsikan tubuh tidak elastis ketika mencoba untuk membuat mesin berjalan di tempat pertama, semakin rumit semakin baik untuk pemecahan masalah.
Patrick Hughes
4

Saya pikir Anda telah melakukan upaya yang sangat mulia, tetapi tampaknya ada masalah mendasar dengan bagaimana kode disusun. Seperti yang lain telah menyarankan, mungkin membantu untuk memisahkan operasi menjadi bagian-bagian yang bijaksana, misalnya:

  1. Fase luas : Loop melalui semua objek - lakukan tes cepat (misalnya, AABB) untuk menentukan objek yang mungkin bertabrakan - buang yang tidak.
  2. Fase sempit : Loop melalui semua objek bertabrakan - menghitung vektor penetrasi untuk tabrakan (misalnya, menggunakan SAT).
  3. Respon tabrakan : Loop melalui daftar vektor tabrakan - menghitung vektor gaya berdasarkan massa, kemudian gunakan ini untuk menghitung vektor percepatan.
  4. Integrasi : Loop melalui semua vektor percepatan dan posisi integrasi (dan rotasi jika diperlukan).
  5. Rendering : Ulangi semua posisi yang dihitung dan render setiap objek.

Dengan memisahkan fase, semua objek diperbarui secara bertahap dalam sinkronisasi dan Anda tidak akan memiliki dependensi urutan yang saat ini Anda perjuangkan. Kode ini juga pada umumnya ternyata lebih sederhana dan lebih mudah untuk diubah. Masing-masing fase ini cukup umum, dan seringkali mungkin untuk mengganti algoritma yang lebih baik setelah Anda memiliki sistem yang berfungsi.

Yang mengatakan, masing-masing bagian ini adalah ilmu itu sendiri, dan dapat menghabiskan banyak waktu untuk mencari solusi yang optimal. Mungkin lebih baik untuk memulai dengan beberapa algoritma yang paling umum digunakan:

  • Deteksi tabrakan fase luas : hashing spasial .
  • Deteksi tabrakan fase sempit : Untuk fisika ubin sederhana, Anda cukup menerapkan tes persimpangan Kotak Aligned Bounding (AABB). Untuk bentuk yang lebih rumit, Anda dapat menggunakan Teorema Sumbu Pemisah . Algoritma apa pun yang Anda gunakan, harus mengembalikan arah dan kedalaman persimpangan antara dua objek (disebut vektor penetrasi).
  • Respon tabrakan : Gunakan Proyeksi untuk menyelesaikan antar penetrasi.
  • Integrasi : Integrator adalah penentu terbesar stabilitas dan kecepatan engine. Dua opsi populer adalah integrasi Verlet (cepat tetapi sederhana) atau RK4 (akurat namun lambat). Menggunakan integrasi verlet dapat menghasilkan desain yang sangat sederhana karena sebagian besar perilaku fisik (bouncing, rotasi) hanya bekerja tanpa terlalu banyak usaha. Salah satu referensi terbaik yang saya lihat untuk mempelajari integrasi RK4 adalah seri Glen Fiedler tentang fisika untuk game .

Tempat yang baik (dan jelas) untuk memulai adalah dengan hukum gerak Newton .

lukevanin
sumber
Terima kasih balasannya. Bagaimana cara mentransfer kecepatan antar tubuh? Apakah itu terjadi selama fase integrasi?
Vittorio Romeo
Dalam arti tertentu, ya. Transfer kecepatan dimulai dengan fase respons tabrakan. Saat itulah Anda menghitung gaya yang bekerja pada tubuh. Force diterjemahkan menjadi akselerasi menggunakan rumus akselerasi = gaya / massa. Akselerasi digunakan pada fase integrasi untuk menghitung kecepatan, yang kemudian digunakan untuk menghitung posisi. Keakuratan fase integrasi menentukan seberapa akurat kecepatan (dan selanjutnya posisi) berubah seiring waktu.
Luke Van In