Làm thế nào tôi có thể thực hiện trọng lực? Không dành cho một ngôn ngữ cụ thể, chỉ là mã giả ...
Làm thế nào tôi có thể thực hiện trọng lực? Không dành cho một ngôn ngữ cụ thể, chỉ là mã giả ...
Câu trả lời:
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 acceleration
thờ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 acceleration
trong 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.
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 position
di chuyển ở velocity
tạ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.
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;
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.
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 .
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í.
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
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.