Bagaimana saya bisa menerapkan gravitasi? Bukan untuk bahasa tertentu, hanya kodesemu ...
Bagaimana saya bisa menerapkan gravitasi? Bukan untuk bahasa tertentu, hanya kodesemu ...
Jawaban:
Seperti yang orang lain catat di komentar, metode integrasi Euler dasar yang dijelaskan dalam jawaban tenpn menderita beberapa masalah:
Bahkan untuk gerakan sederhana, seperti melompat balistik di bawah gravitasi konstan, itu menimbulkan kesalahan sistematis.
Kesalahan tergantung pada timestep, yang berarti bahwa mengubah timestep mengubah lintasan objek secara sistematis yang mungkin diperhatikan oleh pemain jika permainan menggunakan timestep variabel. Bahkan untuk game dengan catatan waktu fisika tetap, mengubah catatan waktu selama pengembangan dapat secara nyata mempengaruhi fisika permainan seperti jarak benda yang diluncurkan dengan kekuatan tertentu akan terbang, berpotensi melanggar level yang dirancang sebelumnya.
Itu tidak menghemat energi, bahkan jika fisika yang mendasarinya seharusnya. Khususnya, objek yang harus berosilasi dengan mantap (mis. Pendulum, pegas, planet yang mengorbit, dll.) Dapat terus mengakumulasi energi hingga seluruh sistem meledak.
Untungnya, tidak sulit untuk menggantikan integrasi Euler dengan sesuatu yang hampir sesederhana itu, namun tidak memiliki masalah ini - khususnya, integrator symplectic orde dua seperti integrasi leapfrog atau metode Verlet kecepatan yang berkaitan erat . Secara khusus, ketika integrasi Euler dasar memperbarui kecepatan dan posisi sebagai:
akselerasi = gaya (waktu, posisi) / massa; waktu + = timestep; position + = timestep * velocity; kecepatan + = percepatan waktu * akselerasi;
metode velocity Verlet melakukannya seperti ini:
akselerasi = gaya (waktu, posisi) / massa; waktu + = timestep; posisi + = timestep * ( kecepatan + timestep * akselerasi / 2) ; newAcceleration = force (waktu, posisi) / massa; kecepatan + = tanda waktu * ( akselerasi + akselerasi baru) / 2 ;
Jika Anda memiliki beberapa objek yang berinteraksi, Anda harus memperbarui semua posisi mereka sebelum menghitung ulang kekuatan dan memperbarui kecepatan. Akselerasi baru kemudian dapat disimpan dan digunakan untuk memperbarui posisi di timestep berikutnya, mengurangi jumlah panggilan force()
menjadi satu (per objek) per timestep, seperti halnya dengan metode Euler.
Juga, jika akselerasi biasanya konstan (seperti gravitasi selama lompatan balistik), kita dapat menyederhanakan hal di atas menjadi hanya:
waktu + = timestep; posisi + = timestep * ( kecepatan + timestep * akselerasi / 2) ; kecepatan + = percepatan waktu * akselerasi;
di mana istilah ekstra dalam huruf tebal adalah satu-satunya perubahan dibandingkan dengan integrasi Euler dasar.
Dibandingkan dengan integrasi Euler, metode Verlet dan leapfrog kecepatan memiliki beberapa properti bagus:
Untuk akselerasi yang konstan, mereka memberikan hasil yang pasti (kesalahan pembulatan titik mengambang), yang berarti bahwa lompatan lompatan balistik tetap sama bahkan jika catatan waktu diubah.
Mereka adalah integrator orde kedua, yang berarti bahwa, bahkan dengan percepatan yang bervariasi, kesalahan integrasi rata-rata hanya sebanding dengan kuadrat dari catatan waktu. Ini dapat memungkinkan langkah waktu yang lebih besar tanpa mengurangi akurasi.
Mereka symplectic , artinya mereka menghemat energi jika fisika yang mendasarinya melakukan (setidaknya selama timestep konstan). Secara khusus, ini berarti Anda tidak akan mendapatkan benda-benda seperti planet yang terbang spontan keluar dari orbitnya, atau benda-benda yang saling menempel dengan pegas berangsur-angsur bergoyang semakin banyak sampai semuanya meledak.
Namun metode kecepatan Verlet / leapfrog hampir sesederhana dan secepat integrasi Euler dasar, dan tentu saja jauh lebih sederhana daripada alternatif seperti integrasi Runge-Kutta urutan keempat (yang, meskipun umumnya integrator yang sangat bagus, tidak memiliki properti simpplektik dan memerlukan empat evaluasi dari force()
fungsi per langkah waktu). Dengan demikian, saya akan sangat menyarankan mereka untuk siapa pun yang menulis segala jenis kode fisika gim, meskipun sesederhana melompat dari satu platform ke platform lainnya.
Sunting: Walaupun derivasi formal dari metode Verlet kecepatan hanya valid ketika gaya tidak bergantung pada kecepatan, dalam praktiknya Anda dapat menggunakannya dengan baik bahkan dengan gaya yang bergantung pada kecepatan seperti tarik fluida . Untuk hasil terbaik, Anda harus menggunakan nilai akselerasi awal untuk memperkirakan kecepatan baru untuk panggilan kedua force()
, seperti ini:
akselerasi = gaya (waktu, posisi, kecepatan) / massa; waktu + = timestep; posisi + = timestep * ( kecepatan + timestep * akselerasi / 2) ; kecepatan + = percepatan waktu * akselerasi; newAcceleration = gaya (waktu, posisi, kecepatan) / massa; velocity + = timestep * (newAcceleration - acceleration) / 2 ;
Saya tidak yakin apakah varian khusus metode vellet Verlet ini memiliki nama tertentu, tetapi saya telah mengujinya dan tampaknya berfungsi dengan sangat baik. Ini tidak seakurat Runge-Kutta orde-orde (seperti yang diharapkan dari metode orde kedua), tetapi jauh lebih baik daripada Euler atau kecepatan naif Verlet tanpa perkiraan kecepatan menengah, dan masih mempertahankan sifat simptektik normal. velocity Verlet untuk gaya konservatif, tidak bergantung pada kecepatan.
Sunting 2: Algoritma yang sangat mirip dijelaskan misalnya oleh Groot & Warren ( J. Chem. Phys. 1997) , meskipun, membaca yang tersirat, tampaknya mereka mengorbankan beberapa akurasi untuk kecepatan ekstra dengan menyimpan newAcceleration
nilai yang dihitung menggunakan kecepatan yang diperkirakan dan menggunakannya kembali sebagai acceleration
untuk catatan waktu berikutnya. Mereka juga memperkenalkan parameter 0 ≤ λ ≤ 1 yang dikalikan dengan acceleration
dalam perkiraan kecepatan awal; untuk beberapa alasan, mereka merekomendasikan λ = 0,5, meskipun semua tes saya menyarankan λ= 1 (yang secara efektif apa yang saya gunakan di atas) bekerja dengan baik atau lebih baik, dengan atau tanpa penggunaan kembali percepatan. Mungkin itu ada hubungannya dengan fakta bahwa pasukan mereka termasuk komponen gerak Brown stochastic.
force(time, position, velocity)
dalam jawaban saya di atas hanya singkatan untuk "gaya yang bekerja pada suatu benda di position
bergerak di velocity
di time
". Biasanya, gaya akan bergantung pada hal-hal seperti apakah benda itu jatuh bebas atau duduk di permukaan yang kokoh, apakah benda lain di sekitarnya mengerahkan kekuatan padanya, seberapa cepat benda itu bergerak di atas permukaan (gesekan) dan / atau melalui cairan atau gas (seret), dll
Setiap loop pembaruan game Anda, lakukan ini:
if (collidingBelow())
gravity = 0;
else gravity = [insert gravity value here];
velocity.y += gravity;
Misalnya, di platformer, setelah Anda melompat gravitasi akan diaktifkan (collidingBelow memberi tahu Anda apakah ada tanah tepat di bawah Anda) dan begitu Anda menyentuh tanah itu akan dinonaktifkan.
Selain itu, untuk menerapkan lompatan, maka lakukan ini:
if (pressingJumpButton() && collidingBelow())
velocity.y = [insert jump speed here]; // the jump speed should be negative
Dan cukup jelas, di loop pembaruan Anda juga harus memperbarui posisi Anda:
position += velocity;
Integrasi fisika newtonian frame-rate * yang tepat:
Vector forces = 0.0f;
// gravity
forces += down * m_gravityConstant; // 9.8m/s/s on earth
// left/right movement
forces += right * m_movementConstant * controlInput; // where input is scaled -1..1
// add other forces in for taste - usual suspects include air resistence
// proportional to the square of velocity, against the direction of movement.
// this has the effect of capping max speed.
Vector acceleration = forces / m_massConstant;
m_velocity += acceleration * timeStep;
m_position += velocity * timeStep;
Tweak gravityConstant, movementConstant dan massConstant hingga terasa benar. Ini adalah hal yang intuitif dan perlu beberapa saat untuk merasa luar biasa.
Sangat mudah untuk memperluas vektor gaya untuk menambah gameplay baru - misalnya menambahkan kekuatan menjauh dari ledakan di dekatnya, atau menuju lubang hitam.
* Sunting: hasil ini akan salah seiring waktu, tetapi mungkin "cukup baik" untuk kesetiaan atau bakat Anda. Lihat tautan ini http://lol.zoy.org/blog/2011/12/14/understanding-motion-in-games untuk info lebih lanjut.
position += velocity * timestep
atas dengan position += (velocity - acceleration * timestep / 2) * timestep
(di mana velocity - acceleration * timestep / 2
hanya rata-rata dari kecepatan lama dan baru). Secara khusus, integrator ini memberikan hasil yang tepat jika akselerasi konstan, karena biasanya untuk gravitasi. Untuk akurasi yang lebih baik di bawah berbagai akselerasi, Anda dapat menambahkan koreksi serupa ke pembaruan kecepatan untuk mendapatkan integrasi kecepatan Verlet .
Jika Anda ingin menerapkan gravitasi pada skala yang sedikit lebih besar, Anda dapat menggunakan jenis perhitungan ini setiap loop:
for each object in the scene
for each other_object in the scene not equal to object
if object.mass * other_object.mass / object.distanceSquaredBetweenCenterOfMasses(other_object) < epsilon
abort the calculation for this pair
if object.mass is much, much bigger than other_object.mass
abort the calculation for this pair
force = gravitational_constant
* object.mass * other_object.mass
/ object.distanceSquaredBetweenCenterOfMasses(other_object)
object.addForceAtCenterOfMass(force * object.normalizedDirectionalVectorTo(other_object))
end for loop
end for loop
Untuk skala yang lebih besar (galaksi), gravitasi saja tidak cukup untuk membuat gerakan "nyata". Interaksi sistem bintang pada taraf signifikan dan sangat terlihat didikte oleh persamaan Navier-Stokes untuk dinamika fluida, dan Anda harus menjaga kecepatan cahaya yang terbatas - dan dengan demikian, gravitasi - dalam pikiran juga.
Kode yang disediakan oleh Ilmari Karonen hampir benar, tetapi ada sedikit kesalahan. Anda sebenarnya menghitung akselerasi 2 kali per centang, ini tidak mengikuti persamaan buku teks.
acceleration = force(time, position) / mass; // Here
time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
newAcceleration = force(time, position) / mass;
velocity += timestep * (acceleration + newAcceleration) / 2;
Mod berikut ini benar:
time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
oldAcceletation = acceleration; // Store it
acceleration = force(time, position) / mass;
velocity += timestep * (acceleration + oldAcceleration) / 2;
Tepuk tangan'
Penjawab Pecant mengabaikan kerangka waktu, dan itu membuat perilaku fisika Anda berbeda dari waktu ke waktu.
Jika Anda akan membuat gim yang sangat sederhana, Anda dapat membuat mesin fisika kecil Anda sendiri - menetapkan massa dan semua jenis parameter fisika untuk setiap objek yang bergerak, dan melakukan deteksi tabrakan, kemudian perbarui posisi dan kecepatannya setiap frame. Untuk mempercepat kemajuan ini, Anda perlu menyederhanakan tabrakan, mengurangi panggilan deteksi tabrakan, dll. Dalam kebanyakan kasus, itu menyebalkan.
Lebih baik menggunakan mesin fisika seperti fisix, ODE dan peluru. Semua dari mereka akan stabil dan cukup efisien untuk Anda.