Game Development Community

Gravity

by Daniel Waterworth · in Torque Game Builder · 11/26/2006 (4:11 am) · 10 replies

In this game I'm making, there are planets and spaceships. I want to add gravity to the planets without using triggers or blank scene objects on either the planets of the spaceships, because I want every planet to have an affect on every spaceship. I want the each planet to hav less of an affect on each spaceship as it gets further away (which I know is easy, its jus pythag.). Is there a way of doing this without slowing the performance of the game down 2 much. ie I can imagine using 2 while loops, one inside the other for every cycle would be processor intensive.

Any help would be much appreciated.

Dan

#1
11/26/2006 (11:56 am)
You could put everything that you want affected by gravity into a SimSet when it's loaded/added and then run though that, say, every half second and calculated the netforce from the objects on each of them and set the object's constant force. (Note: setConstantForce can only hold one force at a time, that's why you need to calculate the net force - or you could use impulses instead, but that might look weird if it's only done occasionally.

Then again, it would probably be a good idea to do only, say, a couple of the objects in the set at a time - spread out the calculations over a few frames so that you don't have a noticable hitch when all the gravity checks are done. Might not be need - it would be a nice feature anyhow.

Hope that made some sense ;)
#2
11/27/2006 (6:39 am)
Thanks, I probably never would hav thought of that. I'll try to put that into my game asap and tell u if it works.

Dan
#3
11/27/2006 (8:52 am)
If you don't mind getting your hands a little dirty, Melv implemented something similar but engine side. If you're interested I'll have a dig through my emails and see if I can find it.

Also in the same thread Thomas posted a way to do it script side using a trigger.

www.garagegames.com/mg/forums/result.thread.php?qt=28261
#4
11/28/2006 (1:23 am)
Unfortunately I dont hav the source code for TGB and so the only way I could do it is through scripting, and as I said above, I dont want to use triggers because I want the force to affect all the objects and not jus ones inside a zoned area. but thanks anyway.

Dan
#5
11/28/2006 (8:16 am)
The bottom line is you'll have to start out with doing N-squared calculations at first. Once you get that working you can start to think about optimizations if you have problems, but if your goal is to have every planet "realistically" affect every object, you need an N-squared algorithm. Whatever your tick rate is going to be, every frame, every 30ms, etc. you'll have to just add up the forces on every object. Build a nested for loop with spaceships in the outer loop and planets in the inner loop. The inner loop will get the vector from planet to spaceship, perform some calculation to invert its scale (because longer vectors should result in less force) and add it together with the next iteration of the loop. When you're done, you'll be left with a vector that represents the aggregate force on the ship.
#6
12/01/2006 (2:14 pm)
I'm actually a little curious about this as well. I'm a bit rusty on my physics, but has anyone written some script to simulate planetary gravity, such that a ship could use its gravity to swing around the planet and gain speed, ala some space probes do? I think 'Star Control Melee' did this.... any ideas?
#7
12/07/2006 (1:03 pm)
I'v started and finished the code for gravity today. if anyones interested I used the following:-

function ship::setgravity(%this)
{
%num = $planets.getcount();
while(%i<%num)
{
%obj = $planets.getobject(%i);
%force = 5000/t2dVectorDistance(%obj.getposition(), %this.getposition());
%angle = mAtan(%obj.getpositiony()-%this.getpositiony(),%obj.getpositionx()-%this.getpositionx());
%i = %i + 1;
%x = %x + mcos(%angle) * %force;
%y = %y + msin(%angle) * %force;
}
%this.setconstantforce(%x, %y);
}
function gravity()
{
if($ships.getcount()>0)
{
if($i>=$ship.maxcount)
{
$i = 0;
}
else
{
if($i<$ships.getcount())
{
%obj = $ships.getobject($i);
%obj.setgravity();
}
$i = $i + 1;
}
}
schedule(1, 0, gravity);
}

If anyone needs any of it explaining, jus ask and equally, if u can find any way to optimize it, however small, I'd love to hear from u.

Dan
#8
01/31/2007 (8:11 am)
Hi Daniel,
I have tried out this code unsuccesfully... would you be able to comment it? Thanks!
#9
01/31/2007 (6:26 pm)
I haven't been following this thread too closely, but I guess you can get rid of the trigonometry functions to gain an extra boost. The code would look something like this, I haven't actually tested it so it might be wrong, but I'm sure you get the idea.

function ship::setgravity(%this)
{
%shipPos = %this.getposition();
%totalForce = "0 0";

%num = $planets.getcount();
for (%i = 0; %i < %num; %i++)
{
%diff = t2dVectorSub($planets.getobject(%i).getposition(), %shipPos);
%diffMag = t2dVectorLength(%diff);

%force = t2dVectorScale(%diff, 5000 / (%diffMag * %diffMag));
/* the line above is a simplification from
%forceMag = 5000 / %diffMag;
%diff = t2dVectorNormalise(%diff);
%force = t2dVectorScale(%diff, %forceMag);*/

%totalForce = t2dVectorAdd(%totalForce, %force);
}

%this.setconstantforce(getWord(%totalForce, 0), getWord(%totalForce, 1));
}
#10
02/12/2007 (6:01 am)
I'v replaced the old trigonometry code with the your new code and it seems to work, except I replaced...

%diff = t2dVectorSub($planets.getobject(%i).getposition(), %shipPos);

with...

%obj = $planets.getobject(%i);
%diff = t2dVectorSub(%obj.getposition(), %shipPos);

and for some reason the gravity isn't as strong so I replaced...

%force = t2dVectorScale(%diff, 5000 / (%diffMag * %diffMag));

with...

%force = t2dVectorScale(%diff, 10000 / (%diffMag * %diffMag));

and then it worked fine. which leaves me with this, plus comments...

function gravity()                         //function to cause gravity to start (I'v called it in the onlevelloaded callback)
{
	if($ships.getcount()>0)        //$ships is a simset containing all the ships u want gravity to affect
	{
		if($i>=$ships.maxcount)     //$ships.maxcount is a variable containing the max amount of ships
		{
				$i = 0;          //once the max number of ships has been affected it loops
		}
		else
		{
			if($i<$ships.getcount())                      //if $ships.getobject($i) exists 
			{
				%obj = $ships.getobject($i);
				%obj.setgravity();                      //apply gravity to it
			}
			$i = $i + 1;                                          //next ship
		}
	}
	schedule(1, 0, gravity);    //loop. this could be changed to, "$gravity = schedule(1, 0, gravity);"
}                                              //and then "cancel($gravity);" would cause gravity to stop

function ship::setgravity(%this) //this function applies the affect of gravity to a ship once
{
	%shipPos = %this.getposition();   //the variable containing the current coordinate of the ship
	%totalForce = "0 0";                      //the variable that will contain the gravity vector specific to a ship

	%num = $planets.getcount();    // $planets is a simset containing all the planet objects
	for (%i = 0; %i < %num; %i++) // loop through all the planets
	{
		%obj = $planets.getobject(%i);  // asign planet %i to object %obj
		%diff = t2dVectorSub(%obj.getposition(), %shipPos); //create a vector from the ship to the planet
		%diffMag = t2dVectorLength(%diff); //get the magnitude of the vector

		%force = t2dVectorScale(%diff, 10000 / (%diffMag * %diffMag));
                //multiply this vector by 10000 / the magnitude squared

		%totalForce = t2dVectorAdd(%totalForce, %force);
                //add the forces created by all the planets together
	}
	%this.setconstantforce(getWord(%totalForce, 0), getWord(%totalForce, 1)); //apply the force
}