   Animation problem [opengl/c]
  In a school project I've made I got a ball bouncing inside a box.. but some times the animation is abit weird, its like seeing double.. especialy when the ball is moving fast.. I guess thats natural, but its not moving that fast either.. the animation is done by calling a animation function with glutTimerFunc() every 25 seconds.. the animation function add forces to the ball and calculates the new coordinates and then calles glutPostRedisplay() so the scene is drawn with the ball at the new coordinates.. Is this the right way to animate it?I dont know if you need to see the code, but pasting it here anyway. I've handed it in a month ago so its too late making any changes to it now though..#include <time.h>#include <math.h>#include <gl\glut.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define PI 3.1415926535897932384626433832795#define true 1#define false 0#define X 0#define Y 1#define Z 2//image variables#define MAXROW200#define MAXCOL320typedef unsigned char pixel;typedef char name[15];name image_file_name;pixel image_buf[MAXROW*MAXCOL];pixel image[MAXROW][MAXCOL];pixel p;name inf_name = "data\\me.raw";FILE *inf_handle;int charin;int r,c;//div variablesfloat bumpPower = 2.0;int showHelp = false;char valueStr[30] = "";int showSplash = false;//camera variablesint rotation = 0;const float zDistance = 15;//physics variablesfloat gravity = 0.2;float fluidVisc = 0.01;float boxFriction = 0.5;float ballMass = 1.0;//light variablesint lightOn = true;float light_pos[] = {0.0, 4.0, 0.0, 0.0};float LightAmbient[]= {0.5, 0.5, 0.5, 1.0};float LightDiffuse[]= {1.0, 0.1, 0.1, 1.0};//ball variablesfloat ballRadius = 0.5;float ballPos[] = {0.0, 1.5, 0.0};float ballDirection[] = {0.0, 0.0, 0.0};//box variablesconst float BOXSIZE = 10;float boxPos[] = {0.0, 0.0, 0.0};int boxOutOfPos = false;//function declerations//adds drag force to ball using addForce()void addDrag();//adds friction force to ball using addForce()void addFriction();//applying force to the ball. Projecting the direction vector with the//force * the direction of the forcevoid addForce(float force,float direction[]);//adds gravity force to ball using addForce()void addGravity();//running all functions conserning the animation of the scenevoid animate(int value);//bums the left near corner of the boxvoid bumpLeftNear();//bumps the left far corner of the boxvoid bumpLeftFar();//bumps the right near corner of the boxvoid bumpRightNear();//bumps the right far corner of the boxvoid bumpRightFar();//moves the camera around the boxvoid changeView(int angle);//checking for collisions between the ball and the boxvoid checkCollision();//glut display functionvoid display(void);//draws the boxvoid drawBox();//draws the ballvoid drawBall();//draws the menuvoid drawMenu();//draws the splash screenvoid drawSplash();//get the magnitude of the direction vectorfloat getMagnitude();//glut init functionvoid init(void);//loads the raw input imagevoid InputImage();//glut keyboard functionvoid keyboard (unsigned char key, int x, int y);//turns on and of lightening in the scenevoid light(int on);//glut mouse functionvoid mouse(int button, int state, int x, int y);//moves the box back to the original position after it has been bumpedvoid moveBoxBack();//Prints text to screenvoid printText(float x, float y, float z,char str[],void *font);//glut reshape functionvoid reshape (int w, int h);//glut special keyboard functionvoid (int key, int x, int y);//normalises a 3d vectorvoid normalise3dVector(const float v[], float *normal);//writes the menu text to screenvoid writeMenuText();//write name caption on splash screenvoid writeNameCaption();//writes the new value string to the screenvoid writeValueStr();//main functionint main(int argc, char** argv);//Function defenitionsvoid addDrag(){//F=-b*v,(b = 6*pi*n*r),n = fluid viscosity, r=ball radiusfloat force = (-(6*PI*fluidVisc*ballRadius) * getMagnitude());float direction[] = {0.0, 0.0, 0.0};normalise3dVector(ballDirection, direction);addForce(force, direction);}void addFriction(){//R=u*N  (N = g)float force = -(boxFriction * (ballMass * gravity));float direction[] = {0.0, 0.0, 0.0};normalise3dVector(ballDirection, direction);addForce(force, direction);}void addForce(float force,float direction[]){int i;for(i = X; i <= Z; i++)ballDirection = ballDirection + (direction * force);}void addGravity(){float force = ballMass * gravity;float direction[] = {0.0, 1.0, 0.0};addForce(force, direction);}void animate(int value){int i;addGravity();addDrag();for(i = X; i <= Z; i++)ballPos -= ballDirection;checkCollision();if(boxOutOfPos == true)moveBoxBack();glutPostRedisplay();glutTimerFunc(25,animate,0);}void bumpLeftFar(){float boxBoundary[] = {BOXSIZE/2 - ballRadius - 0.5,BOXSIZE/2 - ballRadius - 0.5,BOXSIZE/2 - ballRadius - 0.5};float direction[] = {0.0, 0.0, 0.0};//set y axis directionif(ballPos[Y] <= -boxBoundary[Y])direction[Y] = 1.0;//move boxboxPos[X] += 0.5;boxPos[Y] += 0.5;boxPos[Z] += 0.5;//Check how much it should move on x-axisif(ballPos[X] <= -boxBoundary[X])direction[X] = 1.0;else if(ballPos[X] >= boxBoundary[X])direction[X] = -1.0;else if(ballPos[Y] <= -boxBoundary[Y])direction[X] = -0.4;//Check how much it should move on z-axisif(ballPos[Z] <= -boxBoundary[Z])direction[Z] = 1.0;else if(ballPos[Z] >= boxBoundary[Z])direction[Z] = -1.0;else if(ballPos[Y] <= -boxBoundary[Y])direction[Z] = -0.4;addForce(bumpPower / ballMass, direction);}void bumpLeftNear(){float boxBoundary[] = {BOXSIZE/2 - ballRadius - 0.5,BOXSIZE/2 - ballRadius - 0.5,BOXSIZE/2 - ballRadius - 0.5};float direction[] = {0.0, 0.0, 0.0};//set y axis directionif(ballPos[Y] <= -boxBoundary[Y])direction[Y] = 1.0;//move boxboxPos[X] += 0.5;boxPos[Y] += 0.5;boxPos[Z] -= 0.5;//Check how much it should move on x-axisif(ballPos[X] <= -boxBoundary[X])direction[X] = 1.0;else if(ballPos[X] >= boxBoundary[X])direction[X] = -1.0;else if(ballPos[Y] <= -boxBoundary[Y])direction[X] = -0.4;//Check how much it should move on z-axisif(ballPos[Z] <= -boxBoundary[Z])direction[Z] = -1.0;else if(ballPos[Z] >= boxBoundary[Z])direction[Z] = 1.0;else if(ballPos[Y] <= -boxBoundary[Y])direction[Z] = 0.4;addForce(bumpPower / ballMass, direction);}void bumpRightFar(){float boxBoundary[] = {BOXSIZE/2 - ballRadius - 0.5,BOXSIZE/2 - ballRadius - 0.5,BOXSIZE/2 - ballRadius - 0.5};float direction[] = {0.0, 0.0, 0.0};//set y axis directionif(ballPos[Y] <= -boxBoundary[Y])direction[Y] = 1.0;//move boxboxPos[X] -= 0.5;boxPos[Y] += 0.5;boxPos[Z] += 0.5;//Check how much it should move on x-axisif(ballPos[X] <= -boxBoundary[X])direction[X] = -1.0;else if(ballPos[X] >= boxBoundary[X])direction[X] = 1.0;else if(ballPos[Y] <= -boxBoundary[Y])direction[X] = 0.4;//Check how much it should move on z-axisif(ballPos[Z] <= -boxBoundary[Z])direction[Z] = 1.0;else if(ballPos[Z] >= boxBoundary[Z])direction[Z] = -1.0;else if(ballPos[Y] <= -boxBoundary[Y])direction[Z] = -0.4;addForce(bumpPower / ballMass, direction);}void bumpRightNear(){float boxBoundary[] = {BOXSIZE/2 - ballRadius - 0.5,BOXSIZE/2 - ballRadius - 0.5,BOXSIZE/2 - ballRadius - 0.5};float direction[] = {0.0, 0.0, 0.0};//set y axis directionif(ballPos[Y] <= -boxBoundary[Y])direction[Y] = 1.0;//move boxboxPos[X] -= 0.5;boxPos[Y] += 0.5;boxPos[Z] -= 0.5;//Check how much it should move on x-axisif(ballPos[X] <= -boxBoundary[X])direction[X] = -1.0;else if(ballPos[X] >= boxBoundary[X])direction[X] = 1.0;else if(ballPos[Y] <= -boxBoundary[Y])direction[X] = 0.4;//Check how much it should move on z-axisif(ballPos[Z] <= -boxBoundary[Z])direction[Z] = -1.0;else if(ballPos[Z] >= boxBoundary[Z])direction[Z] = 1.0;else if(ballPos[Y] <= -boxBoundary[Y])direction[Z] = 0.4;addForce(bumpPower / ballMass, direction);}void changeView(int angle){    glMatrixMode(GL_MODELVIEW);    glLoadIdentity();glTranslatef (0.0, 0.0,(GLfloat) -zDistance);glRotatef ((GLfloat)angle, 0.0, 1.0, 0.0);}void checkCollision(){float boxBoundary[] = {BOXSIZE/2 - ballRadius, BOXSIZE/2 - ballRadius ,BOXSIZE/2 - ballRadius};int i;for(i = X; i <= Z; i++){//if ball is at the boundary of the box change the ball direction and move the ball//back inside the box if its past the border,then add friction from the box to the ballif(ballPos <= -boxBoundary){ballDirection = -ballDirection;ballPos = -boxBoundary;addFriction();}else if(ballPos >= boxBoundary){ballDirection = -ballDirection;ballPos = boxBoundary;addFriction();}}}void display(void){glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//if esc button is pressed show the splash screen otherwise draw the sceneif(showSplash)drawSplash();else{writeValueStr();drawBall();drawBox();//show help menu if f1 is pressedif(showHelp)drawMenu();}glutSwapBuffers();glFlush();}void drawBox(){GLfloat box_mat_amb_diff[] = {0.2,0.2, 0.4};GLfloat box_mat_specular[] = {0.5, 0.5, 0.5};GLfloat box_mat_shininess[] = {10.0};glPushMatrix();glColor3f(0.2,0.2, 0.4);glMaterialfv(GL_FRONT, GL_SPECULAR, box_mat_specular);glMaterialfv(GL_FRONT, GL_SHININESS, box_mat_shininess);glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, box_mat_amb_diff);glTranslatef(boxPos[X],boxPos[Y],boxPos[Z]);glScalef(BOXSIZE, BOXSIZE, BOXSIZE);glBegin(GL_QUADS);//Top FaceglNormal3f( 0.0, 1.0, 0.0);glVertex3f(-0.5,  0.5, -0.5);glVertex3f(-0.5,  0.5,  0.5);glVertex3f( 0.5,  0.5,  0.5);glVertex3f( 0.5,  0.5, -0.5);//Bottom FaceglNormal3f( 0.0, 1.0, 0.0f);glVertex3f(-0.5, -0.5, -0.5);glVertex3f( 0.5, -0.5, -0.5);glVertex3f( 0.5, -0.5,  0.5);glVertex3f(-0.5, -0.5,  0.5);glEnd();glutWireCube(1);glPopMatrix();}void drawBall(){GLfloat ball_mat_amb_diff[] = {0.2,0.3, 0.0};GLfloat ball_mat_specular[] = {1.0, 1.0, 1.0};GLfloat ball_mat_shininess[] = {70.0};glPushMatrix();glColor3f(0.2, 0.3, 0.0);glMaterialfv(GL_FRONT, GL_SPECULAR, ball_mat_specular);glMaterialfv(GL_FRONT, GL_SHININESS, ball_mat_shininess);glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, ball_mat_amb_diff);glTranslatef(ballPos[0],ballPos[1],ballPos[2]);glutSolidSphere(ballRadius, 20*((int)ballRadius+1), 20*((int)ballRadius+1));glPopMatrix();}void drawMenu(){GLfloat helpMenu_mat_amb_diff[] = {0.2, 0.3, 0.3, 0.1};glPushMatrix();glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, helpMenu_mat_amb_diff);glScalef(5,5,5);glRotatef ((GLfloat)-rotation, 0.0, 1.0, 0.0);glTranslatef(0.0,0.0,1.5);glBlendFunc(GL_ONE, GL_SRC_ALPHA);glEnable (GL_BLEND);glBegin(GL_POLYGON);glVertex3f(-1.0f, 1.0f, 0.0f);glVertex3f( 1.0f, 1.0f, 0.0f);glVertex3f( 1.0f,-1.0f, 0.0f);glVertex3f(-1.0f,-1.0f, 0.0f);glEnd();glDisable(GL_BLEND);glPopMatrix();writeMenuText();}void drawSplash(){int splashOffset = 0;changeView(0);glPushMatrix();for (r = MAXROW-1; r >= 0; r--){for (c = 0; c < MAXCOL; c++)image_buf[MAXCOL*splashOffset + c] = image[r][c];splashOffset++;}writeNameCaption();glRasterPos3f(-3.5, -2.5, 5.0);glDrawPixels(MAXCOL, MAXROW, GL_LUMINANCE, GL_UNSIGNED_BYTE, image_buf);glPopMatrix();}float getMagnitude(){//calculate the value of the vector magnitude: abs(sqrt(x^2 + y^2 + z^2))return fabs(sqrt(pow(ballDirection[0],2) + pow(ballDirection[1],2) + pow(ballDirection[2],2)));}void init(void){    glShadeModel(GL_SMOOTH);    glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);    glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);    glLightfv(GL_LIGHT0, GL_POSITION, light_pos);    glEnable(GL_LIGHT0);light(true);glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    glClearColor (0.0, 0.0, 0.0, 0.0);    glEnable(GL_DEPTH_TEST);}void InputImage(){    //opens the image file and store it in the image arrayif ((inf_handle = fopen(inf_name, "rb")) == NULL)    {   puts("*** Can't open image file !\n ");   charin = getchar();   exit(1);    }    for ( r = 0; r < MAXROW; r++ )    {   for ( c = 0; c < MAXCOL; c++)   {  if((charin=fgetc(inf_handle))==EOF)  { printf("***File reading failed! \n"); charin = getchar(); exit(1);  }  image[r][c] = charin;   }    }   fclose(inf_handle);}void keyboard (unsigned char key, int x, int y){//handles keyboard input. Change the value string if anything values are changedswitch (key){case 'q':case 'Q':if(boxOutOfPos == false){bumpLeftFar();boxOutOfPos = true;}break;case 'w':case 'W':if(boxOutOfPos == false){bumpRightFar();boxOutOfPos = true;}break;case 'a':case 'A':if(boxOutOfPos == false){bumpLeftNear();boxOutOfPos = true;}break;case 's':case 'S':if(boxOutOfPos == false){bumpRightNear();boxOutOfPos = true;}break;case 'l':case 'L':if(lightOn == true)light(false);elselight(true);break;case 'e':case 'E':gravity += 0.005;sprintf(valueStr,"Gravity: %f", gravity);break;case 'd':case 'D':gravity -= 0.005;sprintf(valueStr,"Gravity: %f", gravity);break;case 'r':case 'R':ballMass += 0.1;sprintf(valueStr,"Ball mass: %f", ballMass);break;case 'f':case 'F':ballMass -= 0.1;sprintf(valueStr,"Ball mass: %f", ballMass);break;case 't':case 'T':fluidVisc += 0.0005;sprintf(valueStr,"Fluid viscosity: %f", fluidVisc);break;case 'g':case 'G':fluidVisc -= 0.0005;sprintf(valueStr,"Fluid viscosity: %f", fluidVisc);break;case 'y':case 'Y':boxFriction += 0.05;sprintf(valueStr,"Box Friction: %f", boxFriction);break;case 'h':case 'H':boxFriction -= 0.05;sprintf(valueStr,"Box Friction: %f", boxFriction);break;case 'u':case 'U':bumpPower += 0.5;sprintf(valueStr,"bump Power: %f", bumpPower);break;case 'j':case 'J':bumpPower -= 0.5;sprintf(valueStr,"bump Power: %f", bumpPower);break;case 'o':case 'O':gravity = 0.2;fluidVisc = 0.01;boxFriction = 0.5;ballMass = 1.0;bumpPower = 2.0;ballRadius = 0.5;strcpy(valueStr,"Values reset");break;case 'i':case 'I':ballRadius += 0.05;sprintf(valueStr,"ball radius: %f", ballRadius);break;case 'k':case 'K':ballRadius -= 0.05;sprintf(valueStr,"ball radius: %f", ballRadius);break;case 27:  showSplash = true;  break;   default:  break;    }}void light(int turnOn){if(turnOn){glEnable(GL_LIGHTING);lightOn = true;}else{glDisable(GL_LIGHTING);lightOn = false;}}void mouse(int button, int state, int x, int y){if (button == GLUT_LEFT_BUTTON && showSplash == true)exit(0);}void moveBoxBack(){int i,tmp;for(i = X; i <= Z; i++){tmp = boxPos * 10;if(tmp < 0)boxPos += 0.1;elseboxPos -= 0.1;if(tmp == 0){boxPos = 0.0;boxOutOfPos = false;}}}void printText(float x,float y,float z,char str[],void *font){char *c;glRasterPos3f(x,y,z);for (c=str; *c != '\0'; c++)glutBitmapCharacter(font , *c);}void reshape (int w, int h){    glViewport (0, 0, (GLsizei) w, (GLsizei) h);    glMatrixMode (GL_PROJECTION);    glLoadIdentity ();    gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 30.0);    changeView(0);}void specialKeyboard(int key, int x, int y){//handles special keyboard input that keyboard cannot handleswitch (key){case GLUT_KEY_LEFT:rotation = (rotation + 4) % 360;changeView(rotation);break;case GLUT_KEY_RIGHT:rotation = (rotation - 4) % 360;changeView(rotation);break;case GLUT_KEY_F1:if(showHelp == false){showHelp = true;glDisable(GL_DEPTH_TEST);}else{showHelp = false;glEnable(GL_DEPTH_TEST);}}}void normalise3dVector(const float v[], float *normal){int i;float magnitude = getMagnitude(v);for(i = X; i <= Z; i++){normal = v/magnitude;}}void writeMenuText(){char helpStr1[] = "Left/Right - move camera";char helpStr2[] = "Q/W/A/S - bump bottom corners";char helpStr3[] = "L - turn lightening on/off";char helpStr4[] = "E/D - adjust gravity up/down";char helpStr5[] = "R/F - adjust ball mass up/down";char helpStr6[] = "T/G - adjust fluid viscosity up/down";char helpStr7[] = "Y/H - adjust box friction up/down";char helpStr8[] = "U/J - adjust bump power up/down";char helpStr9[] = "I/K - adjust ball radius up/down";char helpStr10[] = "O - reset";char helpStr11[] = "F1 - show this menu";char helpStr12[] = "ESC - Quit";GLfloat helpStr_mat_amb_diff[] = {1.0, 0.0, 0.0};glPushMatrix();glColor3f(1.0, 0.0, 0.0);glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, helpStr_mat_amb_diff);glRotatef ((GLfloat)-rotation, 0.0, 1.0, 0.0);printText(-1.5, 1.5 ,12.0, helpStr1,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, 1.3 ,12.0, helpStr2,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, 1.1 ,12.0, helpStr3,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, 0.9 ,12.0, helpStr4,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, 0.7 ,12.0, helpStr5,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, 0.5 ,12.0, helpStr6,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, 0.3 ,12.0, helpStr7,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, 0.1 ,12.0, helpStr8,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, -0.1 ,12.0, helpStr9,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, -0.3 ,12.0, helpStr10,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, -0.5 ,12.0, helpStr11,GLUT_BITMAP_TIMES_ROMAN_24);printText(-1.5, -0.7 ,12.0, helpStr12,GLUT_BITMAP_TIMES_ROMAN_24);glPopMatrix();}void writeNameCaption(){    char caption[] = "Terje Loe";    glColor3f(0.0, 0.0, 0.0);printText(-0.7, -3.5 ,5.0, caption,GLUT_BITMAP_9_BY_15);}void writeValueStr(){GLfloat valueStr_mat_amb_diff[] = {1.0,1.0, 1.0};glPushMatrix();glColor3f(1.0, 1.0, 1.0);glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, valueStr_mat_amb_diff);glRotatef ((GLfloat)-rotation, 0.0, 1.0, 0.0);printText(-1.7, 1.6 ,12.0, valueStr,GLUT_BITMAP_HELVETICA_10);glPopMatrix();}int main(int argc, char** argv){//InputImage();glutInit(&argc, argv);glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);glutInitWindowSize (500, 500);glutInitWindowPosition (0, 0);glutCreateWindow ("Ball in a box");init();glutDisplayFunc(display);glutReshapeFunc(reshape);glutMouseFunc(mouse);glutKeyboardFunc(keyboard);glutSpecialFunc(specialKeyboard);glutTimerFunc(25,animate,0);glutMainLoop();return 0;}Edit: commented out the InputImage() function so its possible to compile and run it without the image..   
  Bump :) Anyone?   ;  It doesn't look like your animation is wrong, just choppy because of very quick acceleration.  Decrease the force by a factor fo 2 or 4 and you'll see that the animation is a lot smoother.  Usually to fix that you need to increase your framerate, but your physics code doesn't appear to be dependent on the timestep.  Change your code so that it takes into account the time between physics updates, and then increase the framerate a little, see if that helps.  If it still doesn't look good, you could try using the accumulation buffer to generate a motion blur effect.   ;Quote:Original post by syntheticIt doesn't look like your animation is wrong, just choppy because of very quick acceleration.  Decrease the force by a factor fo 2 or 4 and you'll see that the animation is a lot smoother.  Usually to fix that you need to increase your framerate, but your physics code doesn't appear to be dependent on the timestep.  Change your code so that it takes into account the time between physics updates, and then increase the framerate a little, see if that helps.  If it still doesn't look good, you could try using the accumulation buffer to generate a motion blur effect.So you mean having the physics update and drawing of frames on two different timers right? I tried setting it to draw the frame every 5 ms and then do the physics update every 25 ms as it does in the code I posted, but its still not looking good..I'll have a look at the accumulation buffer..;  No, the problems is that your physics code is independent of time.  You  need to update your physics faster, but with a smaller  timestep, but you aren't using a timestep at all, so you can't get finer ball positioning.Your code appears to work like this:calculate force.add force to velocity.add velocity to position.but, if you remember your kinematics.a=dv/dtv=dx/dt,so your new velocity v+dv = v + a*dt, not just v + a.  likewise, your new position is x+vdt, not x+v.  You have to multiply your acceleration and velocity by the length of your timestep, and then, when you decrease the size of your timestep, the position will update more frequently, and you will get smoother animation.  This is the reason your gravity constant has to be so light, 0.2 instead of 9.8, because you needed to multiply your forces by a small number to keep it from freaking out and flying all over the place incredibly quickly.  Remember, you're updating the position 40 times a second, so each change should be small.If you're really interested in physics simulations, you should look into physics for game developers by bourg, its pretty thorough.;  aha. I havent thought of it like that before.. I just worked out a way of doing it my self.. I found an article on it.. I'll read through that.. After my exams... :phttp://gafferongames.com/game-physics/   ;  That looks like a good article, although don't believe his rather biased nonsense about euler's method being stupid.  runge-kutta IS euler's method, they both approximate a continuous integral as a sum of discrete samples across the region of integration.  The only difference is that euler approximates the curve as a series of line segments, whereas runge-kutta approximates the curve as a series of higher order polynomials.  You get less integration error per step with runge-kutta, but at the cost of extra calculation time.  If I were you,  I would go with a modified euler's method, as his RK4 algorithm seems like a bit of overkill for your purposes.  An actual game probably doesn't need very low-error physics as much as it needs fast physics.   