So it's always the problem that they show the integration methods for function f(y, t). They always take in time. I don't need it. I just need to update velocity based on force, than update position based on velocity. That's all. Why it's nowhere told how to do this correctly to not to loose stability? Uh...
How does Runge-Kutta 4 work in games
Have you tried Verlet integration? You need to keep the last two positions, instead of a position and a velocity. But it's trivial to implement and generally much more stable than Euler's method.
Verlet is a bit more implicit than euler. For higher order implicit method one always needs a solver to invert the simulation and -- so to say -- correct the energy. But RK4 is more correct overall, not just with respect to energy. So in case that collision is less important ( realistic vehicle ), RK4 wins.
This seems relevant. The example code given is even integrating a spring.
I read that article last year. I experimented with the code and found that the algorithm therein differs from the ones explained in Wikipedia and MathWorld – in a way that makes it less accurate.
In an effort to actually understand what was going on, I reworked the code. I have pasted below the original version, and my reworked one. I hope it can help shed some light on the subject, and I'd be glad if any mistakes are found and pointed out.
Original code:
// Simple RK4 integration framework
// Copyright (c) 2004, Glenn Fiedler
// http://www.gaffer.org/articles
#include <stdio.h>
#include <math.h>
struct State
{
float x;
float v;
};
struct Derivative
{
float dx;
float dv;
};
float acceleration(const State &state, float t)
{
const float k = 10;
const float b = 1;
return - k*state.x - b*state.v;
}
Derivative evaluate(const State &initial, float t)
{
Derivative output;
output.dx = initial.v;
output.dv = acceleration(initial, t);
return output;
}
Derivative evaluate(const State &initial, float t, float dt, const Derivative &d)
{
State state;
state.x = initial.x + d.dx*dt;
state.v = initial.v + d.dv*dt;
Derivative output;
output.dx = state.v;
output.dv = acceleration(state, t+dt);
return output;
}
void integrate(State &state, float t, float dt)
{
Derivative a = evaluate(state, t);
Derivative b = evaluate(state, t, dt*0.5f, a);
Derivative c = evaluate(state, t, dt*0.5f, b);
Derivative d = evaluate(state, t, dt, c);
const float dxdt = 1.0f/6.0f * (a.dx + 2.0f*(b.dx + c.dx) + d.dx);
const float dvdt = 1.0f/6.0f * (a.dv + 2.0f*(b.dv + c.dv) + d.dv);
state.x = state.x + dxdt*dt;
state.v = state.v + dvdt*dt;
}
int main()
{
State state;
state.x = 100;
state.v = 0;
float t = 0;
float dt = 0.1f;
while (fabs(state.x)>0.001f || fabs(state.v)>0.001f)
{
printf("%.2f, %.2f\n", state.x, state.v);
integrate(state, t, dt);
t += dt;
}
getc(stdin);
return 0;
}
My reworked version:
// Simple RK4 integration framework
// Copyright (c) 2004, Glenn Fiedler
// http://gafferongames.com/game-physics/integration-basics/
// Alternative code: http://buttersblog.com/runge-kutta/
#include <cstdio>
#include <cmath>
// NUMERICAL INTEGRATION
// pp.635-36 of GEA1e
// The central problem in rigid body dynamics is to solve for the motion of the body,
// given a set of known forces acting on it.
// For linear dynamics, this means finding v(t) and r(t) given knowledge of the net force F_net(t)
// and possibly other information, such as the position and velocity at some previous time.
// As we'll see below, this amounts to solving a pair of ordinary differential equations:
// i) given a(t), integrate it numerically and find v(t)
// ii) found v(t), integrate it numerically and find r(t)
// ANALYTICAL SOLUTION (closed-form expression)
// p.122 of NBG2M&P5e
// a(t) -- acceleration at time t
// v(t) = ?a(t)dt -- The velocity function v(t) is the integral of the acceleration function a(t)
// r(t) = ?v(t)dt -- The position function r(t) is the integral of the velocity function v(t)
//
// ? ?
// -----> -----> p.113 (this assumes constant acceleration)
// a(t) _/ \_ v(t) _/ \_ r(t) a(t) = a ? ?
// \ / \ / v(t) = at + v_0 ? d/dt
// <----- <----- r(t) = ( ½ * at² ) + ( v_0 * t ) + x_0 ? ?
// d/dt d/dt
//
// a(t) = d(v(t))/dt -- The acceleration function a(t) is the derivative of the velocity function v(t)
// v(t) = d(r(t))/dt -- The velocity function v(t) is the derivative of the position function r(t)
// r(t) -- position at time t
struct State
{
State( float ro_velocity = 0.0f, float ro_position = 0.0f )
: m_velocity( ro_velocity )
, m_position( ro_position )
{}
float m_velocity; // current velocity
float m_position; // current position
State & operator =( const State & right )
{
if ( &right != this )
{
this->m_velocity = right.m_velocity;
this->m_position = right.m_position;
}
return *this;
}
State & operator +=( const State & right ) { this->m_velocity += right.m_velocity; this->m_position += right.m_position; return *this; }
State & operator -=( const State & right ) { this->m_velocity -= right.m_velocity; this->m_position -= right.m_position; return *this; }
State & operator *=( float right ) { this->m_velocity *= right; this->m_position *= right; return *this; }
State & operator /=( float right ) { this->m_velocity /= right; this->m_position /= right; return *this; }
};
struct Derivatives
{
Derivatives( float ro_at = 0.0f, float ro_vt = 0.0f )
: m_at( ro_at )
, m_vt( ro_vt )
{}
float m_at; // acceleration: derivative of velocity
float m_vt; // velocity: derivative of position
Derivatives & operator =( const Derivatives & right )
{
if ( &right != this )
{
this->m_at = right.m_at;
this->m_vt = right.m_vt;
}
return *this;
}
Derivatives & operator +=( const Derivatives & right ) { this->m_at += right.m_at; this->m_vt += right.m_vt; return *this; }
Derivatives & operator -=( const Derivatives & right ) { this->m_at -= right.m_at; this->m_vt -= right.m_vt; return *this; }
Derivatives & operator *=( float right ) { this->m_at *= right; this->m_vt *= right; return *this; }
Derivatives & operator /=( float right ) { this->m_at /= right; this->m_vt /= right; return *this; }
};
Derivatives operator +( const Derivatives & left, const Derivatives & right ) { return Derivatives( left.m_at + right.m_at, left.m_vt + right.m_vt ); }
Derivatives operator -( const Derivatives & left, const Derivatives & right ) { return Derivatives( left.m_at + right.m_at, left.m_vt + right.m_vt ); }
Derivatives operator *( const Derivatives & left, float right ) { return Derivatives( left.m_at * right , left.m_vt * right ); }
Derivatives operator /( const Derivatives & left, float right ) { return Derivatives( left.m_at / right , left.m_vt / right ); }
float acceleration( const State & ro_state );
Derivatives sampleDerivatives( const State & ro_currentState );
Derivatives sampleDerivatives( const State & ro_currentState, float ro_dt, const Derivatives & ro_inDerivs );
void integrate( State & rw_state, float ro_dt );
int main()
{
State state;
state.m_velocity = 0.f;
state.m_position = 100.f;
float t = 0.0f;
float dt = 0.1f;
while ( ( fabs( state.m_position ) > 0.001f ) || ( fabs( state.m_velocity ) > 0.001f ) )
{
printf( "vel = % 8.3f, pos = %8.3f\n", state.m_velocity, state.m_position );
::integrate( state, dt );
t += dt;
}
return 0;
}
// This function is what drives the entire simulation. It uses ro_states's position and velocity to calculate acceleration.
// It's a dummy function you replace with your own model, returning the force at a given object state and (optional) time.
float acceleration( const State & ro_state )
{
const float k = 10;
const float b = 1;
// This function calculates a spring and damper force.
// We assume unit mass, i.e. m = 1 (kg); it follows that a = F/m = F
// Damping works because m_velocity < 0 when m_position > 0 and vice versa
return ( -k * ro_state.m_position ) - ( b * ro_state.m_velocity );
}
// This function uses ro_currentState to return derivatives
Derivatives sampleDerivatives( const State & ro_currentState )
{
Derivatives outDerivs;
outDerivs.m_at = ::acceleration( ro_currentState ); // acceleration
outDerivs.m_vt = ro_currentState.m_velocity; // velocity
return outDerivs;
}
// This function uses ro_currentState, ro_dt, and ro_inDerivs to return derivatives
Derivatives sampleDerivatives( const State & ro_currentState, float ro_dt, const Derivatives & ro_inDerivs )
{
// What we'll do:
// i) advance the physics state from t to (t + dt) using one set of derivatives; and then
// ii) use the advanced physics state to sample new derivatives.
// First we take the current state of the object (its position and velocity),
// and advance it ahead ro_dt seconds using an Euler Integration step
// with the derivatives that were passed in (acceleration and velocity).
// This variable exists solely to serve as input to outDerivs below.
State newState;
newState.m_velocity = ro_currentState.m_velocity + ( ro_inDerivs.m_at * ro_dt );
newState.m_position = ro_currentState.m_position + ( ro_inDerivs.m_vt * ro_dt );
// We could refactor the above to a helper function, e.g. EulerStep()
// Once this new velocity and position are calculated,
// we sample new derivatives at this point in time using the advanced (Euler integrated) state.
Derivatives outDerivs;
outDerivs.m_at = ::acceleration( newState ); // (*)
outDerivs.m_vt = newState.m_velocity;
// (*) To get the benefits of RK4 it is essential that you recalculate all forces at each step of RK4,
// otherwise your force is constant over the frame time – and you are really just doing Euler integration.
return outDerivs;
}
// [1] Weisstein, Eric W. "Runge-Kutta Method." From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/Runge-KuttaMethod.html
void integrate( State & rw_state, float ro_dt )
{
Derivatives a; // derivative at time t (t + 0)
Derivatives b; // sample at time t + dt/2 using previously sampled Derivatives a
Derivatives c; // sample at time t + dt/2 using previously sampled Derivatives b
Derivatives d; // sample at time t + dt using previously sampled Derivatives c
Derivatives o; // best overall derivatives calculated using a, b, c, and d
// RK4 samples derivatives four times to detect curvature instead of just once in Euler integration.
a = ::sampleDerivatives( rw_state );
b = ::sampleDerivatives( rw_state, ( ro_dt * 0.5f ), a / 2.0f ); // Glenn Fiedler does *NOT* divide a by 2 in l49 of Integration.cpp! See [1]
c = ::sampleDerivatives( rw_state, ( ro_dt * 0.5f ), b / 2.0f ); // Glenn Fiedler does *NOT* divide b by 2 in l50 of Integration.cpp! See [1]
d = ::sampleDerivatives( rw_state, ( ro_dt ) , c );
// Once the four derivatives samples have been sampled, the best overall derivatives are calculated
// using a weighted sum of the derivatives which comes from the Taylor Series expansion.
o = ( a + ( b + c ) * 2.0f + d ) * ( ro_dt / 6.0f ); // Glenn Fiedler does *NOT* multiply by ro_dt in l53 and l54 of Integration.cpp! See [1]
// In the case of constant acceleration, there is not much point to using RK4
// because in the 4 steps of the integration, you are just doing extra work
// to come up with the same constant dvdt.
// These single values can now be used to advance the velocity and position over dt.
rw_state.m_velocity += ( o.m_at * ro_dt );
rw_state.m_position += ( o.m_vt * ro_dt );
// We are integrating twice.
// - First from acceleration to velocity, which *IS* 100% accurate because the acceleration is constant.
// - Then from velocity to position, which is *NOT* 100% accurate because the velocity (the result
// of the first integration) is changing over time -- and in fact, in "reality" is changing
// in between our sample points dt apart...
}
OK, thinking about it a bit more, the equations you need to solve are not necessarily linear, so it's a bit trickier than I thought. It's certainly not impossible.
Have you tried Verlet integration? You need to keep the last two positions, instead of a position and a velocity. But it's trivial to implement and generally much more stable than Euler's method.
Another monster that won't let me sleep during nights :lol:
Verlet is a bit more implicit than euler. For higher order implicit method one always needs a solver to invert the simulation and -- so to say -- correct the energy. But RK4 is more correct overall, not just with respect to energy. So in case that collision is less important ( realistic vehicle ), RK4 wins.
Hm... OK!
This seems relevant. The example code given is even integrating a spring.
I read that article last year. I experimented with the code and found that the algorithm therein differs from the ones explained in Wikipedia and MathWorld – in a way that makes it less accurate.
In an effort to actually understand what was going on, I reworked the code. I have pasted below the original version, and my reworked one. I hope it can help shed some light on the subject, and I'd be glad if any mistakes are found and pointed out.
Original code:
// Simple RK4 integration framework // Copyright (c) 2004, Glenn Fiedler // http://www.gaffer.org/articles #include <stdio.h> #include <math.h> struct State { float x; float v; }; struct Derivative { float dx; float dv; }; float acceleration(const State &state, float t) { const float k = 10; const float b = 1; return - k*state.x - b*state.v; } Derivative evaluate(const State &initial, float t) { Derivative output; output.dx = initial.v; output.dv = acceleration(initial, t); return output; } Derivative evaluate(const State &initial, float t, float dt, const Derivative &d) { State state; state.x = initial.x + d.dx*dt; state.v = initial.v + d.dv*dt; Derivative output; output.dx = state.v; output.dv = acceleration(state, t+dt); return output; } void integrate(State &state, float t, float dt) { Derivative a = evaluate(state, t); Derivative b = evaluate(state, t, dt*0.5f, a); Derivative c = evaluate(state, t, dt*0.5f, b); Derivative d = evaluate(state, t, dt, c); const float dxdt = 1.0f/6.0f * (a.dx + 2.0f*(b.dx + c.dx) + d.dx); const float dvdt = 1.0f/6.0f * (a.dv + 2.0f*(b.dv + c.dv) + d.dv); state.x = state.x + dxdt*dt; state.v = state.v + dvdt*dt; } int main() { State state; state.x = 100; state.v = 0; float t = 0; float dt = 0.1f; while (fabs(state.x)>0.001f || fabs(state.v)>0.001f) { printf("%.2f, %.2f\n", state.x, state.v); integrate(state, t, dt); t += dt; } getc(stdin); return 0; }
My reworked version:
// Simple RK4 integration framework // Copyright (c) 2004, Glenn Fiedler // http://gafferongames.com/game-physics/integration-basics/ // Alternative code: http://buttersblog.com/runge-kutta/ #include <cstdio> #include <cmath> // NUMERICAL INTEGRATION // pp.635-36 of GEA1e // The central problem in rigid body dynamics is to solve for the motion of the body, // given a set of known forces acting on it. // For linear dynamics, this means finding v(t) and r(t) given knowledge of the net force F_net(t) // and possibly other information, such as the position and velocity at some previous time. // As we'll see below, this amounts to solving a pair of ordinary differential equations: // i) given a(t), integrate it numerically and find v(t) // ii) found v(t), integrate it numerically and find r(t) // ANALYTICAL SOLUTION (closed-form expression) // p.122 of NBG2M&P5e // a(t) -- acceleration at time t // v(t) = ?a(t)dt -- The velocity function v(t) is the integral of the acceleration function a(t) // r(t) = ?v(t)dt -- The position function r(t) is the integral of the velocity function v(t) // // ? ? // -----> -----> p.113 (this assumes constant acceleration) // a(t) _/ \_ v(t) _/ \_ r(t) a(t) = a ? ? // \ / \ / v(t) = at + v_0 ? d/dt // <----- <----- r(t) = ( ½ * at² ) + ( v_0 * t ) + x_0 ? ? // d/dt d/dt // // a(t) = d(v(t))/dt -- The acceleration function a(t) is the derivative of the velocity function v(t) // v(t) = d(r(t))/dt -- The velocity function v(t) is the derivative of the position function r(t) // r(t) -- position at time t struct State { State( float ro_velocity = 0.0f, float ro_position = 0.0f ) : m_velocity( ro_velocity ) , m_position( ro_position ) {} float m_velocity; // current velocity float m_position; // current position State & operator =( const State & right ) { if ( &right != this ) { this->m_velocity = right.m_velocity; this->m_position = right.m_position; } return *this; } State & operator +=( const State & right ) { this->m_velocity += right.m_velocity; this->m_position += right.m_position; return *this; } State & operator -=( const State & right ) { this->m_velocity -= right.m_velocity; this->m_position -= right.m_position; return *this; } State & operator *=( float right ) { this->m_velocity *= right; this->m_position *= right; return *this; } State & operator /=( float right ) { this->m_velocity /= right; this->m_position /= right; return *this; } }; struct Derivatives { Derivatives( float ro_at = 0.0f, float ro_vt = 0.0f ) : m_at( ro_at ) , m_vt( ro_vt ) {} float m_at; // acceleration: derivative of velocity float m_vt; // velocity: derivative of position Derivatives & operator =( const Derivatives & right ) { if ( &right != this ) { this->m_at = right.m_at; this->m_vt = right.m_vt; } return *this; } Derivatives & operator +=( const Derivatives & right ) { this->m_at += right.m_at; this->m_vt += right.m_vt; return *this; } Derivatives & operator -=( const Derivatives & right ) { this->m_at -= right.m_at; this->m_vt -= right.m_vt; return *this; } Derivatives & operator *=( float right ) { this->m_at *= right; this->m_vt *= right; return *this; } Derivatives & operator /=( float right ) { this->m_at /= right; this->m_vt /= right; return *this; } }; Derivatives operator +( const Derivatives & left, const Derivatives & right ) { return Derivatives( left.m_at + right.m_at, left.m_vt + right.m_vt ); } Derivatives operator -( const Derivatives & left, const Derivatives & right ) { return Derivatives( left.m_at + right.m_at, left.m_vt + right.m_vt ); } Derivatives operator *( const Derivatives & left, float right ) { return Derivatives( left.m_at * right , left.m_vt * right ); } Derivatives operator /( const Derivatives & left, float right ) { return Derivatives( left.m_at / right , left.m_vt / right ); } float acceleration( const State & ro_state ); Derivatives sampleDerivatives( const State & ro_currentState ); Derivatives sampleDerivatives( const State & ro_currentState, float ro_dt, const Derivatives & ro_inDerivs ); void integrate( State & rw_state, float ro_dt ); int main() { State state; state.m_velocity = 0.f; state.m_position = 100.f; float t = 0.0f; float dt = 0.1f; while ( ( fabs( state.m_position ) > 0.001f ) || ( fabs( state.m_velocity ) > 0.001f ) ) { printf( "vel = % 8.3f, pos = %8.3f\n", state.m_velocity, state.m_position ); ::integrate( state, dt ); t += dt; } return 0; } // This function is what drives the entire simulation. It uses ro_states's position and velocity to calculate acceleration. // It's a dummy function you replace with your own model, returning the force at a given object state and (optional) time. float acceleration( const State & ro_state ) { const float k = 10; const float b = 1; // This function calculates a spring and damper force. // We assume unit mass, i.e. m = 1 (kg); it follows that a = F/m = F // Damping works because m_velocity < 0 when m_position > 0 and vice versa return ( -k * ro_state.m_position ) - ( b * ro_state.m_velocity ); } // This function uses ro_currentState to return derivatives Derivatives sampleDerivatives( const State & ro_currentState ) { Derivatives outDerivs; outDerivs.m_at = ::acceleration( ro_currentState ); // acceleration outDerivs.m_vt = ro_currentState.m_velocity; // velocity return outDerivs; } // This function uses ro_currentState, ro_dt, and ro_inDerivs to return derivatives Derivatives sampleDerivatives( const State & ro_currentState, float ro_dt, const Derivatives & ro_inDerivs ) { // What we'll do: // i) advance the physics state from t to (t + dt) using one set of derivatives; and then // ii) use the advanced physics state to sample new derivatives. // First we take the current state of the object (its position and velocity), // and advance it ahead ro_dt seconds using an Euler Integration step // with the derivatives that were passed in (acceleration and velocity). // This variable exists solely to serve as input to outDerivs below. State newState; newState.m_velocity = ro_currentState.m_velocity + ( ro_inDerivs.m_at * ro_dt ); newState.m_position = ro_currentState.m_position + ( ro_inDerivs.m_vt * ro_dt ); // We could refactor the above to a helper function, e.g. EulerStep() // Once this new velocity and position are calculated, // we sample new derivatives at this point in time using the advanced (Euler integrated) state. Derivatives outDerivs; outDerivs.m_at = ::acceleration( newState ); // (*) outDerivs.m_vt = newState.m_velocity; // (*) To get the benefits of RK4 it is essential that you recalculate all forces at each step of RK4, // otherwise your force is constant over the frame time – and you are really just doing Euler integration. return outDerivs; } // [1] Weisstein, Eric W. "Runge-Kutta Method." From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/Runge-KuttaMethod.html void integrate( State & rw_state, float ro_dt ) { Derivatives a; // derivative at time t (t + 0) Derivatives b; // sample at time t + dt/2 using previously sampled Derivatives a Derivatives c; // sample at time t + dt/2 using previously sampled Derivatives b Derivatives d; // sample at time t + dt using previously sampled Derivatives c Derivatives o; // best overall derivatives calculated using a, b, c, and d // RK4 samples derivatives four times to detect curvature instead of just once in Euler integration. a = ::sampleDerivatives( rw_state ); b = ::sampleDerivatives( rw_state, ( ro_dt * 0.5f ), a / 2.0f ); // Glenn Fiedler does *NOT* divide a by 2 in l49 of Integration.cpp! See [1] c = ::sampleDerivatives( rw_state, ( ro_dt * 0.5f ), b / 2.0f ); // Glenn Fiedler does *NOT* divide b by 2 in l50 of Integration.cpp! See [1] d = ::sampleDerivatives( rw_state, ( ro_dt ) , c ); // Once the four derivatives samples have been sampled, the best overall derivatives are calculated // using a weighted sum of the derivatives which comes from the Taylor Series expansion. o = ( a + ( b + c ) * 2.0f + d ) * ( ro_dt / 6.0f ); // Glenn Fiedler does *NOT* multiply by ro_dt in l53 and l54 of Integration.cpp! See [1] // In the case of constant acceleration, there is not much point to using RK4 // because in the 4 steps of the integration, you are just doing extra work // to come up with the same constant dvdt. // These single values can now be used to advance the velocity and position over dt. rw_state.m_velocity += ( o.m_at * ro_dt ); rw_state.m_position += ( o.m_vt * ro_dt ); // We are integrating twice. // - First from acceleration to velocity, which *IS* 100% accurate because the acceleration is constant. // - Then from velocity to position, which is *NOT* 100% accurate because the velocity (the result // of the first integration) is changing over time -- and in fact, in "reality" is changing // in between our sample points dt apart... }
Thank's! I'll see if I can implement it.
One problem is that I don't understand C++. I don't know how to read it. It's way too different from Python. It's construction is complex. I don't know what's struct, void. Is struct something similar to class in Pyhon and void something similar to def in Python? Also, can you shortly explain what type of changes you made to code? What they actually do? Thank you!
One problem is that I don't understand C++. I don't know how to read it. It's way too different from Python. It's construction is complex. I don't know what's struct, void. Is struct something similar to class in Pyhon and void something similar to def in Python? Also, can you shortly explain what type of changes you made to code? What they actually do? Thank you!
void is basically just a placeholder, when you dont want a function to return anything, for example ("the type of nothing").
struct/class (which are basically the same thing apart from minor syntax differences) describe what variables/properties and methods each object/instance of that class/type shall have.
Ignore the :: in front of names. Its just referring to a global function or such (ctrl+f it).
In parameter list of a function, "&" means the parameter is by reference (if you modify the parameter, the original object that was passed by the caller, is also modified). "const" in the same context means you arent allowed to modify it (in that case, just ignore the & - we only read from the passed in data, not write)
"operator blah" is basically the function name, except its defining an operator ("a + b", where "+" is the function).
The stuff before the function name is the type it returns.
If a function has no body ({ *stuff* }) after the name/params, its a declaration and the body will be defined later in the code.
"this->" means we are accessing member variable of the class instance the method is acting on. Likewise, "return *this" is returning the class instance.
The comments should help understand it. Just start reading through it, you will begin to understand from context once the big picture starts to form in your head. Google the rest.
One problem is that I don't understand C++. I don't know how to read it. It's way too different from Python. It's construction is complex. I don't know what's struct, void. Is struct something similar to class in Pyhon and void something similar to def in Python? Also, can you shortly explain what type of changes you made to code? What they actually do? Thank you!
void is basically just a placeholder, when you dont want a function to return anything, for example ("the type of nothing").
struct/class (which are basically the same thing apart from minor syntax differences) describe what variables/properties and methods each object/instance of that class/type shall have.
Ignore the :: in front of names. Its just referring to a global function or such (ctrl+f it).
In parameter list of a function, "&" means the parameter is by reference (if you modify the parameter, the original object that was passed by the caller, is also modified). "const" in the same context means you arent allowed to modify it (in that case, just ignore the & - we only read from the passed in data, not write)
"operator blah" is basically the function name, except its defining an operator ("a + b", where "+" is the function).
The stuff before the function name is the type it returns.
If a function has no body ({ *stuff* }) after the name/params, its a declaration and the body will be defined later in the code.
"this->" means we are accessing member variable of the class instance the method is acting on. Likewise, "return *this" is returning the class instance.
The comments should help understand it. Just start reading through it, you will begin to understand from context once the big picture starts to form in your head. Google the rest.
OK! In C++ Those *, ->, & and many other symbols confuse me always. It'd have clear syntax without them:D
I think that I will understand it now;)
By the way:
what does ro_ refer to in the code? If m_ is current position/velocity than ro_ is the one from previous frame?
Another monster that won't let me sleep during nights :lol:OK, thinking about it a bit more, the equations you need to solve are not necessarily linear, so it's a bit trickier than I thought. It's certainly not impossible.
Have you tried Verlet integration? You need to keep the last two positions, instead of a position and a velocity. But it's trivial to implement and generally much more stable than Euler's method.
Verlet isn't all that complicated. Ian Parberry's book Intro to Game Physics with Box2D has a nice introduction to it: I recommend downloading the 320 MB zip file (it's freely available at the site I linked to) containing the Lecture Notes and reading the one for Chapter 2, Part 3 (he talks about Euler and Verlet there). If you want to see it in action, he uses the technique in the downloadable source code as well -- it's C++, though.
By the way:
what does ro_ refer to in the code? If m_ is current position/velocity than ro_ is the one from previous frame?
Those are just prefixes I use in my coding convention.
m_ is for the member variables of a class or struct. Also, in C++ the only difference betwen classes and structs is that of member visibility; for classes, members are private by default, and for structs, members are public by default. Minus that, they're equivalent.
ro_ is for readonly parameters of functions, which I use only for input.
rw_ is for writable parameter of functions, which I use for output and/or returning.