Bagaimana saya bisa menerapkan gravitasi?

Jawaban:

52

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 newAccelerationnilai yang dihitung menggunakan kecepatan yang diperkirakan dan menggunakannya kembali sebagai accelerationuntuk catatan waktu berikutnya. Mereka juga memperkenalkan parameter 0 ≤ λ ≤ 1 yang dikalikan dengan accelerationdalam 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.

Ilmari Karonen
sumber
Velocity Verlet itu bagus tetapi tidak dapat memiliki potensi bergantung pada kecepatan, sehingga gesekan tidak dapat diimplementasikan. Saya pikir Runge-Kutta 2 adalah yang terbaik untuk tujuan saya;)
Pizzirani Leonardo
1
@PizziraniLeonardo: Anda dapat menggunakan (varian) vellet Verlet dengan baik bahkan untuk gaya yang bergantung pada kecepatan; lihat hasil edit saya di atas.
Ilmari Karonen
1
Sastra tidak memberikan penafsiran Velocity Verlet nama yang berbeda. Itu bergantung pada strategi prediktor-korektor, sebagaimana juga dinyatakan dalam makalah ini fire.nist.gov/bfrlpubs/build99/PDF/b99014.pdf .
teodron
3
@ Unit978: Itu tergantung pada permainan, dan khususnya pada model fisika yang diterapkannya. The force(time, position, velocity)dalam jawaban saya di atas hanya singkatan untuk "gaya yang bekerja pada suatu benda di positionbergerak di velocitydi 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
Ilmari Karonen
1
Ini adalah jawaban yang bagus, tetapi tidak lengkap tanpa membicarakan langkah waktu yang telah ditentukan ( gafferongames.com/game-physics/fix-your-timestep ). Saya akan menambahkan jawaban yang terpisah, tetapi kebanyakan orang berhenti pada jawaban yang diterima, terutama ketika itu memiliki suara terbanyak dengan selisih yang besar, seperti halnya di sini. Saya pikir komunitas lebih baik dilayani dengan menambah yang ini.
Jibb Smart
13

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;
Kemiri
sumber
6
Maksud kamu apa? Pilih saja nilai gravitasi Anda dan karena itu mengubah kecepatan Anda, bukan hanya posisi Anda, itu terlihat alami.
Pecant
1
Saya tidak suka mematikan gravitasi. Saya pikir gravitasi harus konstan. Hal yang harus diubah (imho) adalah kemampuan Anda untuk melompat.
ultifinitus
2
Jika itu membantu, anggap itu sebagai 'jatuh' daripada benar-benar 'gravitasi'. Fungsi secara keseluruhan mengontrol apakah benda itu jatuh karena gravitasi. Gravitasi itu sendiri ada seperti itu [masukkan nilai gravitasi di sini]. Jadi dalam pengertian itu, gravitasi adalah konstan, Anda hanya tidak menggunakannya untuk apa pun kecuali benda itu mengudara.
Jason Pineo
2
Kode ini bergantung pada kecepatan bingkai, yang tidak terlalu bagus, tetapi jika Anda memiliki pembaruan konstan maka Anda tertawa.
tenpn
1
-1, maaf. Menambahkan kecepatan dan gravitasi, atau posisi dan kecepatan, itu tidak masuk akal. Saya mengerti jalan pintas yang Anda lakukan, tapi itu salah. Saya akan memukul siswa atau trainee atau kolega mana pun yang melakukan hal itu dengan cluebat terbesar yang bisa saya temukan. Konsistensi unit memang penting.
sam hocevar
8

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.

tenpn
sumber
4
Jangan gunakan integrasi Euler. Lihat artikel ini oleh Glenn Fiedler yang menjelaskan masalah dan solusi lebih baik daripada yang saya bisa. :)
Martin Sojka
1
Saya mengerti bagaimana Euler tidak akurat dari waktu ke waktu, tetapi saya pikir ada skenario di mana itu tidak terlalu penting. Selama aturannya konsisten untuk semua orang, dan "rasanya" benar, tidak apa-apa. Dan jika Anda baru belajar tentang phyis, sangat mudah diingat dan ditanamkan.
tenpn
... tautan bagus. ;)
tenpn
4
Anda dapat memperbaiki sebagian besar masalah dengan integrasi Euler hanya dengan mengganti di position += velocity * timestepatas dengan position += (velocity - acceleration * timestep / 2) * timestep(di mana velocity - acceleration * timestep / 2hanya 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 .
Ilmari Karonen
Argumen Anda masuk akal, dan ketidaktepatan seringkali bukan masalah besar. Tetapi Anda tidak boleh mengklaim itu adalah integrasi "frame-rate independent", karena memang tidak (framerate independent).
sam hocevar
3

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.

Martin Sojka
sumber
1

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'

Setan
sumber
Saya pikir Anda salah, karena akselerasi tergantung pada kecepatan
super
-4

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.

http://www.nvidia.com/object/physx_new.html

http://bulletphysics.org/wordpress/

Raymond
sumber
4
-1 Tanggapan tidak membantu yang tidak menjawab pertanyaan.
doppelgreener
4
baik, jika Anda ingin menyesuaikannya untuk waktu, Anda hanya dapat skala kecepatan dengan waktu yang berlalu dari pembaruan terakhir ().
Pecant