I have a basic physics body system (incomplete/very basic; as collision resolution and detection are not implemented, just mass, position, velocity, acceleration, force and impulse).
I implemented position, velocity, acceleration, force and impulse as described in Game Coding Complete 3rd. Ed. by Mike "Mr. Mike" McShaffrty et al.
specifically Chapter 15: Collision and Simple Physics by Jeff Lake
which is: (paraphrasing from text and code, US copyright law and all that.)
Gravity is a Force.
Application of a Force vector adds it to a std::list of forces.
Application of an Impulse vector adds it to a std::list of impulses.
Upon calling Update on the physics body:
- Add all forces and all impulses together (F).
- Clear the impulses list.
- Set acceleration to (F / mass)
- Set velocity to (velocity + (acceleration * deltaTime))
- Set position to (position + (velocity * deltaTime))
(See code below)
The problem I'm having is when gravity is applied as a force the list grows linearly...forever. Eventually it overcomes any attempt to directly set the position of an object to the point that A) The object is always affected by gravity no matter if or how fast the position is changed (clamping to the screen is a good example) B) The list grows so large that the system implodes and chugs. C) If, before the system implodes, the object is stopped at a specific position, the acceleration due to gravity continually increases making it ever harder to change the acceleration/velocity/position of the object (specifically applying an impulse or force upwards).
If I change the noted line in GravityHandler to elem->ApplyImpulse(deltaG);
it works satisfactorily but is this correct or am I missing something?
My implementation according to his example:
RigidBody and its state:
void State::ApplyImpulse(const Vector2D& impulse) {
_impulse.push_back(impulse);
}
void State::ApplyForce(const Vector2D& force) {
_forces.push_back(force);
}
//RigidBody is a `friend` of State.
void RigidBody::ApplyForce(const Vector2D& force) {
_curState.ApplyForce(force);
}
void RigidBody::ApplyImpulse(const Vector2D& impulse) {
_curState.ApplyImpulse(impulse);
}
GravityHandler:
void GravityHandler::Update(double deltaTime) {
class UpdateObject {
public:
UpdateObject(double deltaTime) : _deltaTime(deltaTime) { };
void operator() (RigidBody* elem) {
double mass = elem->GetMass();
if(Math::IsEqual(mass, 0.0)) return;
double gxm = elem->GetXGravityModifier();
double gym = elem->GetYGravityModifier();
Vector2D deltaG(_GRAVITY_VALUE.GetX() * gxm * mass * _deltaTime, _GRAVITY_VALUE.GetY() * gym * mass * _deltaTime);
elem->ApplyForce(deltaG); //The problem seems to be here.
};
private:
double _deltaTime;
};
_accumulator += deltaTime;
if(_accumulator < (1.0 / 60.0)) return;
_accumulator -= deltaTime;
if(!_isActive) return;
std::for_each(_subscribers->begin(), _subscribers->end(), UpdateObject(deltaTime));
}
RigidBody:
void RigidBody::Update(double deltaTime) {
double mass = GetMass();
//If static body, do nothing.
if(Math::IsEqual(mass, 0.0)) return;
Vector2D F;
F += std::accumulate(_curState._forces.begin(), _curState._forces.end(), Vector2D());
F += std::accumulate(_curState._impulses.begin(), _curState._impulses.end(), Vector2D());
_curState._impulses.clear();
SetAcceleration(F / mass);
SetVelocity(GetVelocity() + GetAcceleration() * deltaTime);
SetPosition(GetPosition() + GetVelocity() * deltaTime);
}