중력을 어떻게 구현할 수 있습니까? 특정 언어가 아닌 의사 코드 ...
중력을 어떻게 구현할 수 있습니까? 특정 언어가 아닌 의사 코드 ...
답변:
다른 사람들이 의견에서 언급했듯이 tenpn의 답변에 설명 된 기본 오일러 통합 방법 에는 몇 가지 문제가 있습니다.
일정한 중력 하에서 탄도 점프와 같은 간단한 동작의 경우에도 체계적인 오류가 발생합니다.
오류는 타임 스텝에 따라 다릅니다. 즉, 타임 스텝을 변경하면 게임이 가변 타임 스텝을 사용하는 경우 플레이어가 알 수있는 체계적인 방식으로 오브젝트 궤적을 변경합니다. 물리 시간 단계가 고정 된 게임의 경우에도 개발 중 시간 단계를 변경하면 주어진 힘으로 발사 된 물체가 비행하는 거리와 같이 게임 물리에 현저한 영향을 미쳐 이전에 설계된 수준을 파괴 할 수 있습니다.
기본 물리학이 필요하더라도 에너지를 절약하지 못합니다. 특히 꾸준히 진동 해야하는 물체 (예 : 진자, 샘, 궤도 행성 등)는 전체 시스템이 터질 때까지 꾸준히 에너지를 축적 할 수 있습니다.
다행스럽게도 Euler 통합을 거의 단순하지만 이러한 문제가없는 것, 특히 도약 통합 또는 밀접하게 관련된 속도 Verlet 방법 과 같은 2 차 대칭 적분기로 대체하는 것은 어렵지 않습니다 . 특히 기본 오일러 통합은 속도와 위치를 다음과 같이 업데이트합니다.
가속도 = 힘 (시간, 위치) / 질량; 시간 + = 타임 스텝; 위치 + = 타임 스텝 * 속도; 속도 + = 타임 스텝 * 가속;
속도 Verlet 방법은 다음과 같이합니다.
가속도 = 힘 (시간, 위치) / 질량; 시간 + = 타임 스텝; 위치 + = 타임 스텝 * ( 속도 + 타임 스텝 * 가속 / 2) ; newAcceleration = 힘 (시간, 위치) / 질량; 속도 + = 타임 스텝 * ( 가속 + 새로운 가속 ) / 2 ;
상호 작용하는 객체가 여러 개인 경우 힘을 다시 계산하고 속도를 업데이트하기 전에 모든 위치를 업데이트해야합니다. 새 가속을 저장하고 다음 타임 스텝에서 위치를 업데이트하는 데 사용할 수 force()
있으므로 Euler 메서드와 마찬가지로 호출 횟수를 타임 스텝 당 1 개 (개체 당)로 줄입니다.
또한 가속도가 일반적으로 일정하면 (탄도 점프 중 중력과 같이) 위의 내용을 간단히 단순화 할 수 있습니다.
시간 + = 타임 스텝; 위치 + = 타임 스텝 * ( 속도 + 타임 스텝 * 가속 / 2) ; 속도 + = 타임 스텝 * 가속;
여기서 굵은 체로 표시된 추가 용어는 기본 오일러 통합에 비해 유일한 변경입니다.
오일러 통합과 비교할 때 속도 Verlet 및 leapfrog 메소드에는 몇 가지 멋진 특성이 있습니다.
일정한 가속을 위해 정확한 결과를 얻습니다 (어쨌든 부동 소수점 반올림 오차까지). 이는 시간 간격이 변경 되어도 탄도 점프 궤적이 동일하게 유지됨을 의미합니다.
이들은 2 차 적분기이므로, 다양한 가속도를 사용하더라도 평균 적분 오차는 타임 스텝의 제곱에 비례합니다. 이것은 정확성을 떨어 뜨리지 않고 더 큰 타임 스텝을 허용합니다.
그것들은 증상이 있는데 , 그것은 기본 물리학이 (적어도 시간 단계가 일정하다면) 에너지를 보존한다는 것을 의미합니다. 특히 이것은 행성이 자발적으로 궤도에서 날아가거나 스프링으로 서로 붙어있는 물체와 같은 것들이 점점 터져 나올 때까지 점점 더 많이 흔들리지 않을 것임을 의미합니다.
그러나 속도 Verlet / 도약 방법은 확실히 같은 대안보다 훨씬 간단 거의 간단하고 빠른 기본 오일러 통합으로, 그리고 4 차 Runge-Kutta 통합 일반적으로 아주 좋은 통합하면서 사교 속성을 부족하고 필요, ( 사 개 평가 의 force()
시간 스텝 당 작용). 따라서 한 플랫폼에서 다른 플랫폼으로 점프하는 것만 큼 간단하더라도 모든 종류의 게임 물리 코드를 작성하는 사람에게는 강력히 권장합니다.
편집 : 속도 Verlet 방법의 공식 파생은 힘이 속도와 무관 한 경우에만 유효하지만 실제로 유체 드래그 와 같은 속도 종속 힘으로도 잘 사용할 수 있습니다 . 최상의 결과를 얻으려면 다음 force()
과 같이 두 번째 호출에 대한 새 속도를 추정하기 위해 초기 가속 값을 사용해야합니다 .
가속도 = 힘 (시간, 위치, 속도) / 질량; 시간 + = 타임 스텝; 위치 + = 타임 스텝 * ( 속도 + 타임 스텝 * 가속 / 2) ; 속도 + = 타임 스텝 * 가속; newAcceleration = 힘 (시간, 위치, 속도) / 질량; 속도 + = 타임 스텝 * (newAcceleration-가속) / 2 ;
속도 Verlet 방법의 특정 변형이 특정 이름을 가지고 있는지 확실하지 않지만 테스트 한 결과 아주 잘 작동하는 것 같습니다. 그것은 오차 Runge-Kutta만큼 정확하지는 않지만 (2 차 방법에서 기대할 수 있듯이) 중간 속도 추정이없는 오일러 또는 순진한 속도 Verlet보다 훨씬 우수하며 여전히 정상의 대칭 특성을 유지합니다 보수적이며 속도에 의존하지 않는 힘을위한 속도 Verlet.
편집 2 : Groot & Warren ( J. Chem. Phys. 1997) 과 같은 매우 유사한 알고리즘이 설명되어 있지만 라인 사이를 읽으면 newAcceleration
추정 속도를 사용하여 계산 된 값 을 저장하여 추가 속도에 대한 정확도를 희생 한 것처럼 보입니다. acceleration
다음 타임 스텝 으로 다시 사용합니다 . 또한 초기 속도 추정값 과 곱한 0 ≤ λ ≤ 1 파라미터를 도입합니다 acceleration
. 어떤 이유로, 그들은 추천 λ 모든에도 불구하고, = 0.5를 내 테스트가 제안 λ를= 1 (효과적으로 위에서 사용하는 것)은 가속 재사용 유무에 관계없이 잘 작동합니다. 아마도 그들의 힘에는 확률 론적 브라운 운동 성분이 포함되어 있다는 사실과 관련이있을 것입니다.
force(time, position, velocity)
그냥 "에서 물체에 작용하는 힘에 대한 속기 위의 내 대답 position
에 이동 velocity
에 time
". 일반적으로 힘은 물체가 자유 낙하 상태인지 또는 단단한 표면에 앉아 있는지, 근처의 다른 물체가 물체에 힘을가하는지 여부, 물체가 표면 위로 얼마나 빨리 움직이는 지 (마찰) 및 / 또는 액체를 통과하는 것과 같은 것에 의존합니다. 또는 가스 (끌기) 등
게임의 모든 업데이트 루프는 다음과 같이하십시오 :
if (collidingBelow())
gravity = 0;
else gravity = [insert gravity value here];
velocity.y += gravity;
예를 들어, 플랫 포머에서 점프 중력이 활성화되면 (collidingBelow는 바로 아래에 접지가 있는지 여부를 알려줍니다) 접지에 도달하면 비활성화됩니다.
이 외에도 점프를 구현하려면 다음을 수행하십시오.
if (pressingJumpButton() && collidingBelow())
velocity.y = [insert jump speed here]; // the jump speed should be negative
그리고 분명히 분명히 업데이트 루프에서 위치를 업데이트해야합니다.
position += velocity;
적절한 프레임 속도 독립적 * 뉴턴 물리학 통합 :
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;
중력 조정 상수, 이동 상수 및 질량 직관적 인 것이며 기분이 좋아지는 데 시간이 걸릴 수 있습니다.
힘 벡터를 쉽게 확장하여 새로운 게임 플레이를 추가 할 수 있습니다. 예를 들어 근처 폭발이나 블랙홀을 향해 힘을 추가하십시오.
* 편집 :이 결과는 시간이 지남에 따라 잘못되지만 충실도 또는 적성에 "충분히 만족"할 수 있습니다. 자세한 내용은 http://lol.zoy.org/blog/2011/12/14/understanding-motion-in-games 링크를 참조하십시오 .
position += velocity * timestep
위에서 position += (velocity - acceleration * timestep / 2) * timestep
(여기서 velocity - acceleration * timestep / 2
단순히 과거와 속도의 평균). 특히,이 적분기는 가속도가 일정한 경우 일반적으로 중력에 대한 것이므로 정확한 결과를 제공합니다. 다양한 가속에서 정확도를 높이기 위해 속도 업데이트에 유사한 보정을 추가하여 속도 Verlet 통합 을 얻을 수 있습니다.
약간 더 큰 규모로 중력을 구현하려면 각 루프마다 다음과 같은 계산을 사용할 수 있습니다.
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
더 큰 (은하계) 스케일의 경우, 중력만으로는 "실제"모션을 생성하기에 충분하지 않습니다. 스타 시스템의 상호 작용은 유체 역학에 대한 Navier-Stokes 방정식에 의해 결정된 중요하고 매우 눈에 띄는 범위이므로 유한 한 빛의 속도와 중력도 염두에 두어야합니다.
Ilmari Karonen이 제공 한 코드는 거의 정확하지만 약간의 결함이 있습니다. 실제로 틱당 2 회 가속도를 계산하는데, 이는 교과서 방정식을 따르지 않습니다.
acceleration = force(time, position) / mass; // Here
time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
newAcceleration = force(time, position) / mass;
velocity += timestep * (acceleration + newAcceleration) / 2;
다음 모드가 정확합니다.
time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
oldAcceletation = acceleration; // Store it
acceleration = force(time, position) / mass;
velocity += timestep * (acceleration + oldAcceleration) / 2;
건배'
Pecant의 답변은 프레임 시간을 무시하여 물리 행동을 때때로 다르게 만듭니다.
아주 간단한 게임을 만들려면 작은 물리 엔진을 만들어 움직이는 물체마다 질량과 모든 종류의 물리 매개 변수를 할당하고 충돌 감지를 수행 한 다음 프레임마다 위치와 속도를 업데이트하십시오. 이 진행을 가속화하려면 충돌 메시를 단순화하고 충돌 감지 호출을 줄이는 등의 작업이 필요합니다. 대부분의 경우 이는 고통스러운 일입니다.
physix, ODE 및 bullet과 같은 물리 엔진을 사용하는 것이 좋습니다. 그들 중 하나는 당신을 위해 충분히 안정적이고 효율적입니다.