Làm thế nào tôi có thể thực hiện trọng lực?


Câu trả lời:


52

Như những người khác đã lưu ý trong các nhận xét, phương pháp tích hợp Euler cơ bản được mô tả trong câu trả lời của tenpn bị một số vấn đề:

  • Ngay cả đối với chuyển động đơn giản, như nhảy đạn đạo dưới trọng lực không đổi, nó gây ra lỗi hệ thống.

  • Lỗi phụ thuộc vào dấu thời gian, nghĩa là việc thay đổi dấu thời gian sẽ thay đổi quỹ đạo đối tượng theo cách có hệ thống mà người chơi có thể nhận thấy nếu trò chơi sử dụng dấu thời gian thay đổi. Ngay cả đối với các trò chơi có dấu thời gian vật lý cố định, việc thay đổi dấu thời gian trong quá trình phát triển có thể ảnh hưởng đáng kể đến vật lý trò chơi như khoảng cách mà vật thể được phóng với một lực nhất định sẽ bay, có khả năng phá vỡ các mức thiết kế trước đó.

  • Nó không bảo tồn năng lượng, ngay cả khi vật lý cơ bản nên. Cụ thể, các vật thể nên dao động đều đặn (ví dụ: con lắc, lò xo, các hành tinh quay quanh, v.v.) có thể tích lũy đều đặn năng lượng cho đến khi toàn bộ hệ thống nổ tung.

May mắn thay, không khó để thay thế tích hợp Euler bằng một thứ gần như đơn giản, nhưng không có vấn đề nào trong số này - cụ thể là tích hợp đối xứng bậc hai như tích hợp bước nhảy vọt hoặc phương pháp Verlet vận tốc liên quan chặt chẽ . Cụ thể, nơi tích hợp Euler cơ bản cập nhật vận tốc và vị trí như:

gia tốc = lực (thời gian, vị trí) / khối lượng;
thời gian + = dấu thời gian;
vị trí + = dấu thời gian * vận tốc;
vận tốc + = dấu thời gian * gia tốc;

phương pháp Verlet vận tốc thực hiện như thế này:

gia tốc = lực (thời gian, vị trí) / khối lượng;
thời gian + = dấu thời gian;
vị trí + = dấu thời gian * ( vận tốc + dấu thời gian * gia tốc / 2) ;
newAccelencies = lực lượng (thời gian, vị trí) / khối lượng; 
vận tốc + = timestep * ( tăng tốc + newAccelencies) / 2 ;

Nếu bạn có nhiều đối tượng tương tác, bạn nên cập nhật tất cả các vị trí của chúng trước khi tính toán lại các lực và cập nhật vận tốc. Gia tốc mới sau đó có thể được lưu và sử dụng để cập nhật vị trí trên dấu thời gian tiếp theo, giảm số lượng cuộc gọi force()xuống còn một (trên mỗi đối tượng) trên mỗi dấu thời gian, giống như với phương thức Euler.

Ngoài ra, nếu gia tốc thường không đổi (như trọng lực trong khi nhảy đạn đạo), chúng ta có thể đơn giản hóa những điều trên thành:

thời gian + = dấu thời gian;
vị trí + = dấu thời gian * ( vận tốc + dấu thời gian * gia tốc / 2) ;
vận tốc + = dấu thời gian * gia tốc;

trong đó thuật ngữ phụ in đậm là thay đổi duy nhất so với tích hợp Euler cơ bản.

So với tích hợp Euler, các phương thức Verlet và bước nhảy vọt có một số thuộc tính đẹp:

  • Để tăng tốc liên tục, dù sao họ cũng cho kết quả chính xác (dù sao cũng có lỗi vòng tròn trôi nổi), có nghĩa là quỹ đạo nhảy đạn đạo vẫn giữ nguyên ngay cả khi dấu thời gian bị thay đổi.

  • Chúng là các tích hợp bậc hai, có nghĩa là, ngay cả khi tăng tốc khác nhau, lỗi tích hợp trung bình chỉ tỷ lệ với bình phương của dấu thời gian. Điều này có thể cho phép các dấu thời gian lớn hơn mà không ảnh hưởng đến độ chính xác.

  • Chúng là đối xứng , có nghĩa là chúng bảo tồn năng lượng nếu vật lý cơ bản làm (ít nhất là miễn là dấu thời gian là không đổi). Đặc biệt, điều này có nghĩa là bạn sẽ không nhận được những thứ như các hành tinh tự phát bay ra khỏi quỹ đạo của chúng, hoặc các vật thể gắn liền với nhau bằng lò xo dần dần lắc lư cho đến khi toàn bộ vật thể nổ tung.

Tuy nhiên, phương pháp Verlet / leapfrog vận tốc gần như đơn giản và nhanh chóng như tích hợp Euler cơ bản, và chắc chắn đơn giản hơn nhiều so với các giải pháp thay thế như tích hợp Runge-Kutta bậc bốn (nói chung, trong khi nói chung là một tích hợp rất đẹp, thiếu tính chất đối xứng và yêu cầu bốn đánh giá của force()chức năng mỗi bước thời gian). Vì vậy, tôi rất muốn giới thiệu chúng cho bất kỳ ai viết bất kỳ loại mã vật lý trò chơi nào, ngay cả khi việc này đơn giản như nhảy từ nền tảng này sang nền tảng khác.


Chỉnh sửa: Mặc dù đạo hàm chính thức của phương pháp Verlet vận tốc chỉ có giá trị khi các lực độc lập với vận tốc, trong thực tế, bạn có thể sử dụng nó tốt ngay cả với các lực phụ thuộc vận tốc như lực kéo của chất lỏng . Để có kết quả tốt nhất, bạn nên sử dụng giá trị gia tốc ban đầu để ước tính vận tốc mới cho cuộc gọi thứ hai force(), như sau:

gia tốc = lực (thời gian, vị trí, vận tốc) / khối lượng;
thời gian + = dấu thời gian;
vị trí + = dấu thời gian * ( vận tốc + dấu thời gian * gia tốc / 2) ;
vận tốc + = dấu thời gian * gia tốc;
newAccelencies = lực (thời gian, vị trí, vận tốc) / khối lượng; 
vận tốc + = dấu thời gian * (newAccelencies - gia tốc) / 2 ;

Tôi không chắc liệu biến thể đặc biệt này của phương pháp Verlet vận tốc có một tên cụ thể hay không, nhưng tôi đã thử nó và nó dường như hoạt động rất tốt. Nó không hoàn toàn chính xác như Runge-Kutta theo thứ tự (như người ta mong đợi từ phương pháp bậc hai), nhưng nó tốt hơn nhiều so với Euler hoặc Verlet vận tốc ngây thơ mà không cần ước tính vận tốc trung gian, và nó vẫn giữ được đặc tính đối xứng của bình thường Vận tốc Verlet cho các lực bảo thủ, không phụ thuộc vào vận tốc.

Chỉnh sửa 2: Một thuật toán rất giống nhau được mô tả bởi Groot & Warren ( J. Chem. Phys. 1997) , mặc dù, đọc giữa các dòng, có vẻ như họ đã hy sinh một số độ chính xác để tăng thêm tốc độ bằng cách lưunewAcceleration giá trị được tính bằng vận tốc ước tính và tái sử dụng nó như là dấu accelerationthời gian tiếp theo. Họ cũng giới thiệu một tham số 0 ≤ bước sóng ≤ 1 mà được nhân với accelerationtrong dự toán vận tốc ban đầu; đối với một số lý do, họ khuyên λ = 0,5, mặc dù tất cả của tôi kiểm tra cho thấy rằng λ= 1 (đó là hiệu quả những gì tôi sử dụng ở trên) hoạt động tốt hoặc tốt hơn, có hoặc không có tái sử dụng gia tốc. Có lẽ nó có một cái gì đó để làm với thực tế là lực lượng của họ bao gồm một thành phần chuyển động Brown ngẫu nhiên.


Velocity Verlet thật tuyệt nhưng nó không thể có tiềm năng phụ thuộc bởi vận tốc, do đó ma sát không thể được thực hiện. Tôi nghĩ rằng Runge-Kutta 2 là tốt nhất cho mục đích của tôi;)
Pizzirani Leonardo

1
@PizziraniLeonardo: Bạn có thể sử dụng (một biến thể) Verlet vận tốc tốt ngay cả đối với các lực phụ thuộc vào vận tốc; xem chỉnh sửa của tôi ở trên.
Ilmari Karonen

1
Văn học không cho cách giải thích này của Velocity Verlet một tên khác. Nó dựa trên chiến lược dự đoán-sửa lỗi, như đã nêu trong bài báo này.nist.gov/bfrlpub/build99/PDF/b99014.pdf .
teodron

3
@ Unit978: Điều đó phụ thuộc vào trò chơi, và cụ thể là mô hình vật lý mà nó triển khai. Các force(time, position, velocity)trong câu trả lời của tôi ở trên chỉ là viết tắt cho "lực tác động lên một đối tượng tại positiondi chuyển ở velocitytại time". Thông thường, lực sẽ phụ thuộc vào những thứ như vật rơi tự do hay ngồi trên một bề mặt rắn, cho dù có bất kỳ vật nào khác ở gần đó tác dụng lực lên nó, tốc độ di chuyển trên bề mặt (ma sát) và / hoặc qua chất lỏng hoặc khí (kéo), v.v.
Ilmari Karonen 23/11/14

1
Đây là một câu trả lời tuyệt vời, nhưng nó không đầy đủ mà không nói về bước thời gian cố định ( gafferongames.com/game-physics/fix-your-timestep ). Tôi sẽ thêm một câu trả lời riêng, nhưng hầu hết mọi người dừng lại ở câu trả lời được chấp nhận, đặc biệt là khi nó có nhiều phiếu nhất với mức chênh lệch lớn như vậy, như trường hợp ở đây. Tôi nghĩ rằng cộng đồng được phục vụ tốt hơn bằng cách tăng cường điều này.
Jibb Smart

13

Mỗi vòng lặp cập nhật của trò chơi của bạn, hãy làm điều này:

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

velocity.y += gravity;

Chẳng hạn, trong một platformer, một khi bạn nhảy trọng lực sẽ được kích hoạt (collidingBelow cho bạn biết liệu có mặt đất ngay bên dưới bạn hay không) và một khi bạn chạm đất thì nó sẽ bị vô hiệu hóa.

Bên cạnh đó, để thực hiện các bước nhảy, sau đó làm điều này:

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

Và khá rõ ràng, trong vòng cập nhật, bạn cũng phải cập nhật vị trí của mình:

position += velocity;

6
Ý anh là gì? Chỉ cần chọn giá trị trọng lực của riêng bạn và vì nó thay đổi vận tốc của bạn, không chỉ vị trí của bạn, nó trông tự nhiên.
Pecant

1
Tôi không thích tắt trọng lực bao giờ. Tôi nghĩ rằng trọng lực nên không đổi. Điều nên thay đổi (imho) là khả năng nhảy của bạn.
ultifinitus

2
Nếu nó có ích, hãy nghĩ về nó như 'rơi' chứ không thực sự là 'trọng lực'. Toàn bộ chức năng kiểm soát xem vật có rơi hay không do trọng lực. Trọng lực tự nó tồn tại giống như [chèn giá trị trọng lực ở đây]. Vì vậy, theo nghĩa đó, trọng lực không đổi, bạn chỉ không sử dụng nó cho bất cứ điều gì trừ khi vật thể ở trên không.
Jason Pineo

2
Mã này phụ thuộc vào tốc độ khung hình, điều này không tuyệt vời, nhưng nếu bạn có một bản cập nhật liên tục thì bạn sẽ cười.
tenpn

1
-1, xin lỗi. Thêm vận tốc và trọng lực, hoặc vị trí và vận tốc, điều đó không có ý nghĩa. Tôi hiểu các phím tắt bạn đang làm, nhưng nó sai. Tôi sẽ đánh bất kỳ sinh viên hoặc thực tập sinh hoặc đồng nghiệp nào làm điều đó với cluebat lớn nhất mà tôi có thể tìm thấy. Sự nhất quán của các đơn vị không thành vấn đề.
sam hocevar

8

Một tích hợp vật lý độc lập tốc độ khung hình * thích hợp

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;

Tinh chỉnh trọng lựcConstant, MovementConstant và massConstant cho đến khi cảm thấy đúng. Đó là một điều trực quan và có thể mất một thời gian để có được cảm giác tuyệt vời.

Thật dễ dàng để mở rộng vectơ lực lượng để thêm lối chơi mới - ví dụ: thêm một lực từ bất kỳ vụ nổ gần đó hoặc hướng tới các lỗ đen.

* chỉnh sửa: những kết quả này sẽ sai theo thời gian, nhưng có thể "đủ tốt" cho sự trung thực hoặc năng khiếu của bạn. Xem liên kết này http://lol.zoy.org/blog/2011/12/14/under Hiểu-motion-in-trò chơi để biết thêm.


4
Đừng sử dụng tích hợp Euler. Xem bài viết này của Glenn Fiedler giải thích các vấn đề và giải pháp tốt hơn tôi có thể. :)
Martin Sojka

1
Tôi hiểu làm thế nào Euler không chính xác theo thời gian, nhưng tôi nghĩ có những tình huống không thực sự quan trọng. Miễn là các quy tắc phù hợp với tất cả mọi người, và nó "cảm thấy" đúng, thì tốt thôi. Và nếu bạn chỉ đang tìm hiểu về phyiscs, nó rất dễ nhớ và dễ hiểu.
tenpn

... Liên kết tốt mặc dù. ;)
tenpn

4
Bạn có thể khắc phục hầu hết các vấn đề với tích hợp Euler chỉ bằng cách thay thế position += velocity * timestepở trên bằng position += (velocity - acceleration * timestep / 2) * timestep( velocity - acceleration * timestep / 2đơn giản là trung bình của vận tốc cũ và mới). Cụ thể, bộ tích hợp này cho kết quả chính xác nếu gia tốc không đổi, vì nó thường dành cho trọng lực. Để có độ chính xác tốt hơn khi tăng tốc khác nhau, bạn có thể thêm một hiệu chỉnh tương tự vào bản cập nhật vận tốc để có được tích hợp Verlet vận tốc .
Ilmari Karonen

Lập luận của bạn có ý nghĩa, và sự không chính xác thường không phải là một vấn đề lớn. Nhưng bạn không nên khẳng định đây là một sự tích hợp độc lập với tốc độ khung hình chính xác, bởi vì nó không phải là (độc lập với tốc độ khung hình).
sam hocevar

3

Nếu bạn muốn thực hiện trọng lực ở quy mô lớn hơn một chút, bạn có thể sử dụng loại tính toán này cho mỗi vòng lặp:

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

Đối với quy mô thậm chí lớn hơn (thiên hà), chỉ riêng lực hấp dẫn sẽ không đủ để tạo ra chuyển động "thực". Sự tương tác của các hệ sao là ở một mức độ đáng kể và rất rõ ràng được quy định bởi các phương trình Navier-Stokes cho động lực học chất lỏng, và bạn sẽ phải giữ tốc độ hữu hạn của ánh sáng - và do đó, cả về trọng lực - trong tâm trí.


1

Mã được cung cấp bởi Ilmari Karonen là gần như chính xác, nhưng có một chút trục trặc. Bạn thực sự tính toán gia tốc 2 lần trên mỗi tích tắc, điều này không tuân theo các phương trình sách giáo khoa.

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

Các mod sau đây là chính xác:

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

Chúc mừng


Tôi nghĩ bạn đã sai, vì khả năng tăng tốc phụ thuộc vào vận tốc
siêu

-4

Người trả lời của Pecant đã bỏ qua thời gian khung và điều đó làm cho hành vi vật lý của bạn khác đi theo thời gian.

Nếu bạn định làm một trò chơi rất đơn giản, bạn có thể tạo ra công cụ vật lý nhỏ của riêng mình - gán khối lượng và tất cả các loại thông số vật lý cho mọi vật thể chuyển động, và phát hiện va chạm, sau đó cập nhật vị trí và vận tốc của chúng theo từng khung. Để đẩy nhanh tiến độ này, bạn cần đơn giản hóa lưới va chạm, giảm các cuộc gọi phát hiện va chạm, v.v. Trong hầu hết các trường hợp, đó là một nỗi đau.

Tốt hơn là sử dụng công cụ vật lý như vật lý, ODE và đạn. Bất kỳ trong số họ sẽ ổn định và hiệu quả đủ cho bạn.

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

http://bONSphysics.org/wordpress/


4
-1 Trả lời không hữu ích mà không trả lời câu hỏi.
doppelgreener

4
tốt, nếu bạn muốn điều chỉnh nó theo thời gian, bạn có thể chỉ cần điều chỉnh tốc độ theo thời gian đã trôi qua từ bản cập nhật cuối cùng ().
Pecant
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.