CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 8 of 8
  1. #1
    Join Date
    Feb 2012
    Posts
    23

    Unhappy Still Some Trouble Creating Gravity

    Hi, I've been having trouble working on this physics engine of mine. Right now I'm having trouble adding gravity.

    The game is a spaceship--which you control--flying around in space with asteroids and eventually the ability to shoot bullets. There should be a wrap on the edges of the screen and gravity for each object depending on their mass.

    I'm creating a forceX and forceY for each force put onto each object, and then computing that force into a velX and velY which will determine the direction of each object and at which speed.

    Where my problem arises:
    Code:
    	//add gravity pulls to forces
    	for(int i = 0; i <= pushCount; i++) //add gravity pulls for each object
    	{
    		for(int u = 0 ; u <= pushCount; u++) //each object should add a force for every other object
    		{
    			if(i != u)
    			{
    				switch(id[i])
    				{
    				case ASTEROID_ID:
    					switch(id[u])
    					{
    					case ASTEROID_ID: //m1 = ASTEROID_MASS, m2 = ASTEROID_MASS
    						tempForce = ( (GRAV_CONST * ASTEROID_MASS * ASTEROID_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					case SPACESHIP_ID: //m1 = ASTEROID_MASS, m2 = SPACESHIP_MASS
    						tempForce = ( (GRAV_CONST * ASTEROID_MASS * SPACESHIP_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					case BULLET_ID: //m1 = ASTEROID_MASS, m2 = BULLET_MASS
    						tempForce = ( (GRAV_CONST * ASTEROID_MASS * BULLET_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					}
    					break;
    
    				case SPACESHIP_ID:
    					switch(id[u])
    					{
    					case ASTEROID_ID: //m1 = SPACESHIP_MASS, m2 = ASTEROID_MASS
    						tempForce = ( (GRAV_CONST * SPACESHIP_MASS * ASTEROID_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					case BULLET_ID: //m1 = SPACESHIP_MASS, m2 = BULLET_MASS
    						tempForce = ( (GRAV_CONST * SPACESHIP_MASS * BULLET_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					}
    					break;
    
    				case BULLET_ID:
    					switch(id[u])
    					{
    					case ASTEROID_ID: //m1 = BULLET_MASS, m2 = ASTEROID_MASS
    						tempForce = ( (GRAV_CONST * BULLET_MASS * ASTEROID_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					case SPACESHIP_ID: //m1 = BULLET_MASS, m2 = SPACESHIP_MASS
    						tempForce = ( (GRAV_CONST * BULLET_MASS * SPACESHIP_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					case BULLET_ID: //m1 = BULLET_MASS, m2 = BULLET_MASS
    						tempForce = ( (GRAV_CONST * BULLET_MASS * BULLET_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					}
    					break;
    				}
    			
    				tempAngle = atan2( ( (x[i]-x[u]) / tempForce ), ( (y[i]-y[u]) / tempForce ) );
    
    				if( (tempAngle * 180 / PI) > 0 && 90 > (tempAngle * 180 / PI) ) //sin +, cos +
    				{
    					forceX[i] += cos(tempAngle);
    					forceY[i] += sin(tempAngle);
    				}
    				else
    					if( (tempAngle * 180 / PI) > 90 && 180 > (tempAngle * 180 / PI) ) //sin +, cos -
    					{
    						forceX[i] += -cos(tempAngle);
    						forceY[i] += sin(tempAngle);
    					}
    					else
    						if( (tempAngle * 180 / PI) > 180 && 270 > (tempAngle * 180 / PI) ) //sin -, cos -
    						{
    							forceX[i] += -cos(tempAngle);
    							forceY[i] += -sin(tempAngle);
    						}
    						else
    							if( (tempAngle * 180 / PI) > 270 && 360 > (tempAngle * 180 / PI) ) //sin -, cos +
    							{
    								forceX[i] += cos(tempAngle);
    								forceY[i] += -sin(tempAngle);
    							}
    			}
    		}
    	}
    And then my entire engine.cpp code, if necessary:
    Code:
    #include <vector>
    #include <string>
    #include <math.h>
    #include <valarray>
    #include "constants.h"
    using namespace std;
    
    void crunchCommand(string command, int pushCount, vector<int> id, vector<float> & x, vector<float> & y, vector<int> & angle, vector<float> & velX, vector<float> & velY, vector<float> & forceX, vector<float> & forceY)
    {
    	float 
    		tempForce,
    		tempAngle,
    		ratio;
    
    	if(command == "right")
    	{
    		if(angle.front() + 1 <= 71) //if a turn right doesn't overflow the 72 limit, turn right one.
    			angle.front() = angle.front() + 1;
    		else //else it does overflow, so wrap back to the first angle.
    			angle.front() = 0;
    	}
    	else
    		if(command == "left")
    		{
    			if(angle.front() - 1 >= 0) //if a turn left doesn't go below 0 (the first angle), turn left one.
    				angle.front() = angle.front() - 1;
    			else //else it does go below 1, so wrap back to the last angle (72)
    				angle.front() = 71; 
    		}
    		else
    			if(command == "space")
    			{
    				//shoot a bullet.. pause for now
    				velX[0] = 0;
    				velY[0] = 0;
    				forceX[0] = 0;
    				forceY[0] = 0;
    			}
    			else
    				if(command == "up")
    				{
    					if(angle[0] == 0)
    						forceY[0] = -1;
    					else
    						if(angle[0] == 36)
    							forceY[0] = 1;
    						else
    							if(angle[0] == 18)
    								forceX[0] = 1;
    							else
    								if(angle[0] == 54)
    									forceX[0] = -1;
    								else
    								{
    									forceX[0] = sin(angle[0] * 5 * (PI / 180)); //force of 1 per tick
    									forceY[0] = -cos(angle[0] * 5 * (PI / 180)); //force of 1 per tick.. negative cosine to account for the reversed y-axis
    								}
    				}
    				
    	//add gravity pulls to forces
    	for(int i = 0; i <= pushCount; i++) //add gravity pulls for each object
    	{
    		for(int u = 0 ; u <= pushCount; u++) //each object should add a force for every other object
    		{
    			if(i != u)
    			{
    				switch(id[i])
    				{
    				case ASTEROID_ID:
    					switch(id[u])
    					{
    					case ASTEROID_ID: //m1 = ASTEROID_MASS, m2 = ASTEROID_MASS
    						tempForce = ( (GRAV_CONST * ASTEROID_MASS * ASTEROID_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					case SPACESHIP_ID: //m1 = ASTEROID_MASS, m2 = SPACESHIP_MASS
    						tempForce = ( (GRAV_CONST * ASTEROID_MASS * SPACESHIP_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					case BULLET_ID: //m1 = ASTEROID_MASS, m2 = BULLET_MASS
    						tempForce = ( (GRAV_CONST * ASTEROID_MASS * BULLET_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					}
    					break;
    
    				case SPACESHIP_ID:
    					switch(id[u])
    					{
    					case ASTEROID_ID: //m1 = SPACESHIP_MASS, m2 = ASTEROID_MASS
    						tempForce = ( (GRAV_CONST * SPACESHIP_MASS * ASTEROID_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					case BULLET_ID: //m1 = SPACESHIP_MASS, m2 = BULLET_MASS
    						tempForce = ( (GRAV_CONST * SPACESHIP_MASS * BULLET_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					}
    					break;
    
    				case BULLET_ID:
    					switch(id[u])
    					{
    					case ASTEROID_ID: //m1 = BULLET_MASS, m2 = ASTEROID_MASS
    						tempForce = ( (GRAV_CONST * BULLET_MASS * ASTEROID_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					case SPACESHIP_ID: //m1 = BULLET_MASS, m2 = SPACESHIP_MASS
    						tempForce = ( (GRAV_CONST * BULLET_MASS * SPACESHIP_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					case BULLET_ID: //m1 = BULLET_MASS, m2 = BULLET_MASS
    						tempForce = ( (GRAV_CONST * BULLET_MASS * BULLET_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    						break;
    					}
    					break;
    				}
    			
    				tempAngle = atan2( ( (x[i]-x[u]) / tempForce ), ( (y[i]-y[u]) / tempForce ) );
    
    				if( (tempAngle * 180 / PI) > 0 && 90 > (tempAngle * 180 / PI) ) //sin +, cos +
    				{
    					forceX[i] += cos(tempAngle);
    					forceY[i] += sin(tempAngle);
    				}
    				else
    					if( (tempAngle * 180 / PI) > 90 && 180 > (tempAngle * 180 / PI) ) //sin +, cos -
    					{
    						forceX[i] += -cos(tempAngle);
    						forceY[i] += sin(tempAngle);
    					}
    					else
    						if( (tempAngle * 180 / PI) > 180 && 270 > (tempAngle * 180 / PI) ) //sin -, cos -
    						{
    							forceX[i] += -cos(tempAngle);
    							forceY[i] += -sin(tempAngle);
    						}
    						else
    							if( (tempAngle * 180 / PI) > 270 && 360 > (tempAngle * 180 / PI) ) //sin -, cos +
    							{
    								forceX[i] += cos(tempAngle);
    								forceY[i] += -sin(tempAngle);
    							}
    			}
    		}
    	}
    	//compute force to velocity
    	velX[0] += (forceX[0] / SPACESHIP_MASS) * ACCEL; //scaler of ACCEL .. makes objects obtain force slower
    	velY[0] += (forceY[0] / SPACESHIP_MASS) * ACCEL; //scaler of ACCEL
    
    	for(int i = 1; i <= pushCount; i++)
    	{
    		velX[i] += (forceX[i] / ASTEROID_MASS);
    		velY[i] += (forceY[i] / ASTEROID_MASS);
    	}
    
    	//limit the speed of every object pushed into the game. (will need to make special case for bullets)
    	for(int i = 0; i <= pushCount; i++)
    	{
    		if(sqrt(velX[i]*velX[i] + velY[i]*velY[i]) > MAX_VELOCITY)
    		{
    			ratio = MAX_VELOCITY / sqrt(velX[i]*velX[i] + velY[i]*velY[i]);
    			velX[i] = velX[i] * ratio;
    			velY[i] = velY[i] * ratio;
    		}
    	}
    
    	//change position of x and y
    	for(int i = 0; i <= pushCount; i++)
    	{
    		if((x[i] + velX[i]) > SCREEN_WIDTH) //if x gets bigger than maxX, wrap
    			x[i] = velX[i] - SCREEN_WIDTH - x[i];
    		else
    			if(x[i] + velX[i] < 0)
    				x[i] = SCREEN_WIDTH + velX[i] + x[i]; //if x gets smaller than minX, wrap
    			else
    				x[i] += velX[i];
    
    		if((y[i] + velY[i]) > SCREEN_HEIGHT) //if y gets bigger than maxY, wrap
    			y[i] = velY[i] - SCREEN_HEIGHT - y[i]; 
    		else
    			if(y[i] + velY[i] < 0)
    				y[i] = SCREEN_HEIGHT + velY[i] + y[i]; //if y gets smaller than minY, wrap
    			else
    				y[i] += velY[i];
    	}
    }
    Any help would be very much appreciated. This has cost me countless hours of headache, and is due Wednesday (extended from Monday).

  2. #2
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,234

    Re: Still Some Trouble Creating Gravity

    [ Moved thread ]
    Ovidiu
    "When in Rome, do as Romans do."
    My latest articles: https://codexpertro.wordpress.com/

  3. #3
    Join Date
    Jan 2009
    Posts
    596

    Re: Still Some Trouble Creating Gravity

    Hello again!

    Firstly, a tip on making your code neater (I do like neat code ). You have a lot of code duplication in this switch statement:
    Code:
    switch(id[i])
    {
    case ASTEROID_ID:
    	switch(id[u])
    	{
    	case ASTEROID_ID: //m1 = ASTEROID_MASS, m2 = ASTEROID_MASS
    		tempForce = ( (GRAV_CONST * ASTEROID_MASS * ASTEROID_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    		break;
    	case SPACESHIP_ID: //m1 = ASTEROID_MASS, m2 = SPACESHIP_MASS
    		tempForce = ( (GRAV_CONST * ASTEROID_MASS * SPACESHIP_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    		break;
    	case BULLET_ID: //m1 = ASTEROID_MASS, m2 = BULLET_MASS
    		tempForce = ( (GRAV_CONST * ASTEROID_MASS * BULLET_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    		break;
    	}
    	break;
    
    case SPACESHIP_ID:
    	switch(id[u])
    	{
    	case ASTEROID_ID: //m1 = SPACESHIP_MASS, m2 = ASTEROID_MASS
    		tempForce = ( (GRAV_CONST * SPACESHIP_MASS * ASTEROID_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    		break;
    	case BULLET_ID: //m1 = SPACESHIP_MASS, m2 = BULLET_MASS
    		tempForce = ( (GRAV_CONST * SPACESHIP_MASS * BULLET_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    		break;
    	}
    	break;
    
    case BULLET_ID:
    	switch(id[u])
    	{
    	case ASTEROID_ID: //m1 = BULLET_MASS, m2 = ASTEROID_MASS
    		tempForce = ( (GRAV_CONST * BULLET_MASS * ASTEROID_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    		break;
    	case SPACESHIP_ID: //m1 = BULLET_MASS, m2 = SPACESHIP_MASS
    		tempForce = ( (GRAV_CONST * BULLET_MASS * SPACESHIP_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    		break;
    	case BULLET_ID: //m1 = BULLET_MASS, m2 = BULLET_MASS
    		tempForce = ( (GRAV_CONST * BULLET_MASS * BULLET_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    		break;
    	}
    	break;
    }
    The end cases in this double switch all have the same form, the only difference between them being the two masses used in the calculation.

    Now, if you liked that bit of refactoring I suggested on your other thread (http://forums.codeguru.com/showthrea...40#post2061740) then you'll love this bit!

    Write a simple helper function, called e.g. "mass", which returns the appropriate mass for a given object id (i.e. ASTEROID_ID/SPACESHIP_ID/BULLET_ID) by switching on the object id. Then you can replace all the above switch statement with one line of code. Pretty neat eh?

    Now, for the bit which is causing problems:
    Code:
    tempAngle = atan2( ( (x[i]-x[u]) / tempForce ), ( (y[i]-y[u]) / tempForce ) );
    
    if( (tempAngle * 180 / PI) > 0 && 90 > (tempAngle * 180 / PI) ) //sin +, cos +
    {
    	forceX[i] += cos(tempAngle);
    	forceY[i] += sin(tempAngle);
    }
    else
    	if( (tempAngle * 180 / PI) > 90 && 180 > (tempAngle * 180 / PI) ) //sin +, cos -
    	{
    		forceX[i] += -cos(tempAngle);
    		forceY[i] += sin(tempAngle);
    	}
    	else
    		if( (tempAngle * 180 / PI) > 180 && 270 > (tempAngle * 180 / PI) ) //sin -, cos -
    		{
    			forceX[i] += -cos(tempAngle);
    			forceY[i] += -sin(tempAngle);
    		}
    		else
    			if( (tempAngle * 180 / PI) > 270 && 360 > (tempAngle * 180 / PI) ) //sin -, cos +
    			{
    				forceX[i] += cos(tempAngle);
    				forceY[i] += -sin(tempAngle);
    			}
    You are overcomplicating things here. The equation for the gravitational attraction between two masses (in vector form) is:

    F_12 = -G*M1*M2/R^2 * V

    where F_12 is the force applied on object 2 due to object 1,
    G is the gravitational constant
    M1/M2 are the masses
    R^2 is the square of the distance between them
    and V is a unit vector from object 1 to object 2.

    You have already calculated the first part (G*M1*M2/R^2). So all you need is to calculate the unit vector. You get this by normalising the vector between the two objects, i.e. normalise the vector ( (x[i]-x[u]), (y[i]-y[u]) ). No need to mess around with angles at all

  4. #4
    Join Date
    Feb 2012
    Posts
    23

    Re: Still Some Trouble Creating Gravity

    I'm glad it's Peter_B here to save the day yet again.

    As for the complicated switch.. I know it's horrible x.x
    I was really trying to get things working correctly before going to clean up (for some reason--making it hard on myself).

    I planned on creating classes and doing simply class.mass()
    But I might do a quick implementation of a function call to simplify things for me for now!

    As for the next part, I'm not sure how to multiply the G*M1*M2/R^2 by vector V.
    I did a quick Google to find that normalizing is simply dividing each of the components by the length--so dividing each one by r would do it I believe, or would it be dividing by tempForce?

    I think it would be r, as you said the distance from object 1 to object 2.

    But how would I go about multiplying by the vector? Sorry for the utter ignorance on the subject of vectors, but thanks so much for your help again!

    Also, did you mean to put the - in front of the equation G*M1*M2/R^2 * V ? It's meant to be negative?
    Last edited by Filthy_Utter; March 27th, 2012 at 06:51 PM. Reason: Forgot one thing

  5. #5
    Join Date
    Feb 2012
    Posts
    23

    Re: Still Some Trouble Creating Gravity

    Wait, so would I simply be able to say:

    forceX = (-G*M1*M2/R^2) / (y2-y1)
    and
    forceY = (-G*M1*M2/R^2) / (x2-x1)
    ?

  6. #6
    Join Date
    Jan 2009
    Posts
    596

    Re: Still Some Trouble Creating Gravity

    Quote Originally Posted by Filthy_Utter View Post
    I'm glad it's Peter_B here to save the day yet again.

    As for the complicated switch.. I know it's horrible x.x
    I was really trying to get things working correctly before going to clean up (for some reason--making it hard on myself).

    I planned on creating classes and doing simply class.mass()
    But I might do a quick implementation of a function call to simplify things for me for now!
    Adding classes would definitely be a good idea. I didn't suggest that as I wasn't sure whether you had been taught them yet. It is best to add them sooner rather than later, as they help keep the code simple.

    Quote Originally Posted by Filthy_Utter View Post
    As for the next part, I'm not sure how to multiply the G*M1*M2/R^2 by vector V.
    I did a quick Google to find that normalizing is simply dividing each of the components by the length--so dividing each one by r would do it I believe, or would it be dividing by tempForce?

    I think it would be r, as you said the distance from object 1 to object 2.

    But how would I go about multiplying by the vector? Sorry for the utter ignorance on the subject of vectors, but thanks so much for your help again!
    You are correct about normalizing a vector - you divide each component by the length, which in this case is R.
    To multiply a scalar (i.e. a plain number) by a vector you just multiply each component of the vector by the scalar in turn. So e.g.
    Code:
    5*(1,2) = (5,10)
    Quote Originally Posted by Filthy_Utter View Post
    Also, did you mean to put the - in front of the equation G*M1*M2/R^2 * V ? It's meant to be negative?
    Don't worry about the minus sign - that way is just the conventional way of writing it. If you reverse the direction of the vector V the minus sign disappears.

    I've just noticed an error in the gravity calculation, by the way. In this line:
    Code:
    tempForce = ( (GRAV_CONST * ASTEROID_MASS * ASTEROID_MASS)  /  ( (float)sqrt(  (x[i]-x[u])*(x[i]-x[u]) + (y[i]-y[u])*(y[i]-y[u])  ) )  );
    you are dividing by the distance rather than the square of the distance

    Quote Originally Posted by Filthy_Utter View Post
    Wait, so would I simply be able to say:

    forceX = (-G*M1*M2/R^2) / (y2-y1)
    and
    forceY = (-G*M1*M2/R^2) / (x2-x1)
    ?
    Not quite. If you had y1=y2 or x1=x2 these would give infinite forces
    The X component of the force will be (G*M1*M2/R^2) * X component of normalised V.
    Then the X component of normalised V will be (x2-x1)/R. Or maybe (x1-x2)/R, depending upon which objects force you are calculating - if things start repelling each other instead of attracting, just change the sign.

  7. #7
    Join Date
    Feb 2012
    Posts
    23

    Re: Still Some Trouble Creating Gravity

    I don't know how to thank you enough. Thank you so much

    My program is working correctly now. I have to make a video of it, so I'll go ahead and post it later so you can see!

    You have saved me!

  8. #8
    Join Date
    Jan 2009
    Posts
    596

    Re: Still Some Trouble Creating Gravity

    Quote Originally Posted by Filthy_Utter View Post
    My program is working correctly now. I have to make a video of it, so I'll go ahead and post it later so you can see!
    Excellent - I'd like to see it working. Asteroids in the arcades used to be one of my favourites

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured