Yerçekimini nasıl uygulayabilirim?


Yanıtlar:


52

Yorumlarda diğerlerinin de belirttiği gibi, tenpn'in cevabında açıklanan temel Euler entegrasyon yöntemi birkaç problemden muzdarip:

  • Basit hareketlerde bile, sabit yerçekimi altında balistik atlama gibi, sistematik bir hata ortaya çıkarır.

  • Hata, zaman adımına bağlıdır, yani zaman adımını değiştirmenin, oyunun değişken bir zaman adımı kullanıyorsa, oyuncu yörüngelerini fark edebileceği sistematik bir şekilde nesne yörüngelerini değiştirmesidir. Sabit bir fizik zaman çizelgesine sahip oyunlar için bile, geliştirme sırasındaki zaman çizelgesini değiştirmek, belirli bir kuvvetle başlatılan bir nesnenin uçması ve potansiyel olarak önceden tasarlanmış seviyeleri kırması gibi oyun fiziğini belirgin şekilde etkileyebilir.

  • Temel fizik olsa bile, enerji tasarrufu yapmaz. Özellikle, sürekli salınması gereken nesneler (örneğin sarkaçlar, yaylar, yörüngede dönen gezegenler, vb.), Tüm sistem parçalanıncaya kadar sürekli olarak enerji biriktirebilir.

Neyse ki, Euler entegrasyonunu neredeyse basit olan bir şeyle değiştirmek zor olmasa da , bu sorunların hiçbiri bulunmuyor - özellikle, birinciliğin entegrasyonu veya yakından ilişkili hız Verlet metodu gibi ikinci dereceden bir sempatik entegratör . Özellikle, temel Euler entegrasyonunun hızı ve konumu şu şekilde güncellediği durumlarda:

hızlanma = kuvvet (zaman, pozisyon) / kütle;
süre + = timestep;
pozisyon + = timestep * hız;
hız + = timestep * ivmelenme;

hız Verlet yöntemi şöyle yapar:

hızlanma = kuvvet (zaman, pozisyon) / kütle;
süre + = timestep;
pozisyon + = timestep * ( hız + timestep * hızlanma / 2) ;
newAcceleration = kuvvet (zaman, konum) / kütle; 
hız + = timestep * ( hızlanma + yeni Hızlanma) / 2 ;

Birden fazla etkileşime giren nesneniz varsa , kuvvetleri yeniden hesaplamadan ve hızları güncellemeden önce tüm konumlarını güncellemelisiniz. Ardından, yeni hızlanma (lar) kaydedilebilir ve bir sonraki zaman adımındaki pozisyonları güncellemek için kullanılabilir; böylece çağrı sayısı force(), Euler yönteminde olduğu gibi, her zaman adımı için bir taneye (nesne başına) indirgenir.

Ayrıca, hızlanma normalde sabitse (balistik atlama sırasındaki yerçekimi gibi), yukarıdakileri sadeleştirerek yapabiliriz:

süre + = timestep;
pozisyon + = timestep * ( hız + timestep * hızlanma / 2) ;
hız + = timestep * ivmelenme;

Kalın yazılmış ekstra terim, temel Euler entegrasyonuyla karşılaştırıldığında tek değişikliktir.

Euler entegrasyonuyla karşılaştırıldığında, hız Verlet ve birdirbir yöntemlerin birçok hoş özelliği vardır:

  • Sürekli hızlanma için, kesin sonuçlar verir (yine de kayan nokta yuvarlama hataları), yani balistik atlama yörüngelerinin zaman aşımı değişse bile aynı kalması anlamına gelir.

  • Bunlar, ikinci dereceden bütünleştiricilerdir, yani değişken hızlanmada bile, ortalama bütünleştirme hatası yalnızca zaman çizgisinin karesiyle orantılıdır. Bu, doğruluktan ödün vermeden daha büyük zaman adımlarına izin verebilir.

  • Onlar sempatiktir , yani temel fizik yaparsa (en azından zaman aralığı sabit olduğu sürece) enerji tasarrufu sağladıkları anlamına gelir. Bu, özellikle gezegenlerin kendi yörüngelerinde kendiliğinden uçan uçları, ya da her şey patlayana kadar yavaş yavaş sallanan yaylarla birbirine tutturulmuş nesneler alamayacağınız anlamına gelir.

Yine de, Verlet / leapfrog hızı, temel Euler entegrasyonu kadar basit ve hızlıdır ve dördüncü dereceli Runge-Kutta entegrasyonu gibi alternatiflerden çok daha basittir (genel olarak çok güzel bir entegratör olsa da, sempatik özellikten yoksundur ve dört değerlendirme gerektirir) bir force()zaman adım başına fonksiyonu). Bu nedenle, bir platformdan diğerine atlamak kadar basit olsa bile, herhangi bir tür oyun fizik kodu yazan herkese şiddetle tavsiye ediyorum.


Düzenleme: Hızın biçimsel türetilmesi Verlet yönteminin yalnızca kuvvetler hızdan bağımsız olduğunda geçerlidir, pratikte sıvı sürüklemesi gibi hıza bağlı kuvvetlerle bile gayet iyi kullanabilirsiniz . En iyi sonucu elde etmek için, ikinci aramanın yeni hızını tahmin etmek için ilk hızlanma değerini kullanmalısınız force():

hızlanma = kuvvet (zaman, konum, hız) / kütle;
süre + = timestep;
pozisyon + = timestep * ( hız + timestep * hızlanma / 2) ;
hız + = timestep * ivmelenme;
Yeni Hızlanma = kuvvet (zaman, pozisyon, hız) / kütle; 
hız + = timestep * (yeni Hızlanma - hızlanma) / 2 ;

Hızın bu belirli değişkeninin Verlet yönteminin belirli bir adı olup olmadığından emin değilim, ancak test ettim ve çok iyi çalışıyor gibi görünüyor. Bu, ikinci dereceden Runge-Kutta kadar kesin değildir (ikinci dereceden bir yöntemden beklendiği gibi), ancak orta hız tahmini olmadan Euler veya naif hız Verlet'ten çok daha iyidir ve normalin sempatik özelliğini korur Korunumlu, hıza bağlı olmayan kuvvetler için hız verleti

Düzenleme 2: Çok benzer bir algoritma, örneğin Groot & Warren ( J. Chem. Phys. 1997) tarafından tarif edilmiştir , ancak satırlar arasında okunurken newAcceleration, tahmini hız kullanılarak hesaplanan değeri kaydederek ekstra hız için bazı doğruluktan ödün verdikleri görülmektedir. ve bir accelerationsonraki zaman aşımı için tekrar kullanılması. Ayrıca , ilk hız tahmini ile çarpılan 0 ≤ λ ≤ 1 parametresini de tanıtırlar acceleration; nedense onlar tavsiye λ bütün halde = 0.5 benim testler düşündürmektedir Â= 1 (yukarıda kullandığım etkili) ivme yeniden kullanımıyla birlikte veya olmadan, iyi veya daha iyi çalışır. Belki de güçlerinin stokastik bir Brownian hareket bileşeni içermesi gerçeğiyle ilgisi var.


Hız Verleti güzel ama hıza bağlı bir potansiyele sahip olmadığı için sürtünme uygulanamıyor. Runge-Kutta 2'nin amacım için en iyisi olduğunu düşünüyorum;)
Pizzirani Leonardo

1
@PizziraniLeonardo: Hıza bağlı kuvvetler için bile (bir varyant) hız Verletini kullanabilirsiniz; yukarıdaki düzenlememe bakın.
Ilmari Karonen

1
Edebiyat bu Velocity Verlet yorumuna farklı bir isim vermez. Fire.nist.gov/bfrlpubs/build99/PDF/b99014.pdf belgesinde de belirtildiği gibi, bir tahmin edici-düzeltici stratejisine dayanır .
teodron

3
@ Unit978: Oyuna ve özellikle de uyguladığı fizik modeline bağlı. force(time, position, velocity)Sadece "bir nesne üzerinde hareket eden gücü için steno yukarıda cevabım positionhareket velocityde time". Tipik olarak, kuvvet, nesnenin serbest düşme halinde mi yoksa katı bir yüzeyde mi oturduğuna, yakındaki herhangi bir nesnenin üzerine bir kuvvet uygulayıp uygulamadığına, bir yüzey üzerinde (sürtünme) ve / veya bir sıvının içinde ne kadar hızlı hareket ettiği gibi şeylere bağlı olacaktır. veya gaz (sürükleme) vb.
Ilmari Karonen

1
Bu harika bir cevap, ancak sabit zaman adımından bahsetmeden tamamlanmadı ( gafferongames.com/game-physics/fix-your-timestep ). Ayrı bir cevap ekleyecektim, ancak çoğu kişi, özellikle de burada olduğu gibi, bu kadar yüksek bir marjla en fazla oyu aldığında, kabul edilen cevapta durur. Bence topluluğun bunu güçlendirerek daha iyi hizmet edebileceğini düşünüyorum.
Jibb Smart,

13

Oyununuzun her güncelleme döngüsü, bunu yapın:

if (collidingBelow())
    gravity = 0;
else gravity = [insert gravity value here];

velocity.y += gravity;

Örneğin, bir platformda, bir kez zıpladığınızda yerçekimi etkinleşir (collidingBelow size altınızda zemin olup olmadığını söyler) ve yere çarptığınızda devre dışı bırakılır.

Bunun yanında, atlayışları uygulamak için, sonra şunu yapın:

if (pressingJumpButton() && collidingBelow())
    velocity.y = [insert jump speed here]; // the jump speed should be negative

Ve açıkçası, güncelleme döngüsünde konumunuzu da güncellemeniz gerekir:

position += velocity;

6
Ne demek istiyorsun? Sadece kendi yerçekimi değerinizi seçin ve hızınızı değiştirdiğinden, sadece konumunuzu değil, doğal görünür.
Pecant

1
Şimdiye kadar yerçekimini kapatmaktan hoşlanmıyorum. Yerçekiminin sabit olması gerektiğini düşünüyorum. Değişmesi gereken şey (imho) zıplama yeteneğin.
ultifinitus

2
Eğer yardımı olacaksa, aslında 'yerçekimi' yerine 'düşmek' olarak düşünün. Bir bütün olarak işlev, nesnenin yerçekimi nedeniyle düşüp düşmediğini kontrol eder. Yerçekiminin kendisi de [burada yerçekimi değerini buraya yerleştirin] olduğu gibi var olur. Yani bu anlamda, yerçekimi olan nesne havadan olmadıkça sadece herhangi bir şey için kullanmayın, sabiti.
Jason Pineo

2
Bu kod kare hızına bağlıdır, bu harika değildir, ancak sürekli bir güncellemeniz varsa o zaman gülüyorsunuz.
Tenpn

1
-1, özür dilerim. Sadece anlam ifade etmeyen hız ve yerçekimi veya konum ve hız eklemek. Yaptığın kısayolu anlıyorum ama yanlış. Bulabildiğim en büyük ipucu ile bunu yapan her öğrenciye, stajyere veya meslektaşıma isabet edebilirim. Birimlerin tutarlılığı önemlidir.
sam hocevar

8

Uygun bir kare hızı bağımsız * newtonian fizik entegrasyonu:

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 yerçekimi Sabit, hareket Sabit ve kütle Sabit, doğru hissedene kadar. Bu sezgisel bir şeydir ve harika hissetmek için biraz zaman alabilir.

Yeni bir oyun eklemek için kuvvetler vektörünü genişletmek kolaydır - örneğin yakındaki bir patlamaya veya kara deliklere bir kuvvet ekler.

* düzenleme: Bu sonuçlar zamanla yanlış olacak, ancak sadakat veya yetenek için "yeterince iyi" olabilir. Daha fazla bilgi için bu bağlantıya http://lol.zoy.org/blog/2011/12/14/understanding-motion-in-games bakın .


4
Euler entegrasyonunu kullanmayın. Sorunları ve çözümleri benden daha iyi açıklayan Glenn Fiedler'in bu makalesine bakın . :)
Martin Sojka

1
Euler'in zaman içinde nasıl yanlış olduğunu anlıyorum, ancak bunun gerçekten önemli olmadığı senaryolar olduğunu düşünüyorum. Kurallar herkes için tutarlı olduğu ve “doğru” hissettiği sürece, sorun değil. Ve sadece phyiscs hakkında öğreniyorsanız, hatırlamak ve implmenet çok kolaydır.
tenpn

... olsa iyi bağlantı. ;)
tenpn

4
Euler entegrasyonuyla ilgili sorunların çoğunu, sadece position += velocity * timestepyukarıdakilerle değiştirerek düzeltebilirsiniz position += (velocity - acceleration * timestep / 2) * timestep( velocity - acceleration * timestep / 2eski ve yeni hızların ortalamasıdır). Özellikle, bu entegratör, genellikle yerçekimi için olduğu gibi, hızlanma sabitse kesin sonuçlar verir. Değişken hızlanma altında daha iyi doğruluk için, hız Verlet entegrasyonunu elde etmek için hız güncellemesine benzer bir düzeltme ekleyebilirsiniz .
Ilmari Karonen

Argümanlarınız mantıklı ve yanlışlık çoğu zaman önemli değil. Ancak bunun “uygun kare hızından bağımsız” bir entegrasyon olduğunu iddia etmemelisiniz, çünkü sadece (kare hızından bağımsız) değil.
sam hocevar

3

Yerçekimini biraz daha büyük bir ölçekte uygulamak istiyorsanız, her döngüde bu tür bir hesaplama kullanabilirsiniz:

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

Daha büyük (galaktik) ölçekler için bile, yerçekimi tek başına “gerçek” hareketi oluşturmak için yeterli olmaz. Yıldız sistemlerinin etkileşimi, sıvı dinamikleri için Navier-Stokes denklemlerinin belirttiği önemli ve çok görünür bir düzeydedir ve sonlu ışık hızını - ve dolayısıyla yerçekimini de göz önünde bulundurmanız gerekir.


1

Ilmari Karonen tarafından sağlanan kod neredeyse doğrudur, ancak hafif bir aksaklık vardır. Aslında, hızlanmayı kene başına 2 defa hesaplıyorsunuz, bu ders kitabı denklemlerini takip etmiyor.

acceleration = force(time, position) / mass; // Here
time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
newAcceleration = force(time, position) / mass;
velocity += timestep * (acceleration + newAcceleration) / 2;

Aşağıdaki mod doğrudur:

time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
oldAcceletation = acceleration; // Store it
acceleration = force(time, position) / mass;
velocity += timestep * (acceleration + oldAcceleration) / 2;

Şerefe


İvme hızına bağlıdır gibi ben, sen yanlış düşünüyorsun
süper

-4

Pecant'ın cevabı çerçeve zamanını görmezden geldi ve bu fizik davranışınızı zaman zaman farklı yapıyor.

Çok basit bir oyun yapacaksanız, kendi küçük fizik motorunuzu oluşturabilirsiniz - her hareketli nesne için kütle ve her türlü fizik parametresi atayabilir ve çarpışma algılamasını yapabilir, ardından her karedeki konumlarını ve hızlarını güncelleyebilirsiniz. Bu ilerlemeyi hızlandırmak için, çarpışma ağını basitleştirmeniz, çarpışma algılama çağrılarını vb. Azaltmanız gerekir. Çoğu durumda, bu bir acıdır.

Physix, ODE ve mermi gibi fizik motoru kullanmak daha iyidir. Bunlardan herhangi biri sizin için yeterince kararlı ve verimli olacak.

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

http://bulletphysics.org/wordpress/


4
-1 Soruyu cevaplamayan yardımcı olmayan yanıt.
doppelgreener

4
Eh, zaman için ayarlamak istiyorsanız, sadece son güncellemeden () geçen süreye göre hızı ölçeklendirebilirsiniz.
11:11
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.