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.
- 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).
- 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.
Jawaban:
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:
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:
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:
acceleration = [Complicated formulas]
velocity += acceleration
position += velocity
sumber
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:
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.
sumber
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
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:
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:
Tempat yang baik (dan jelas) untuk memulai adalah dengan hukum gerak Newton .
sumber