Game Development Community

dev|Pro Game Development Curriculum

T2D TScript-controlled rigid body impulse forces

by Kyle Kronyak · 03/17/2006 (6:58 pm) · 0 comments

Download Code File

Introduction

I have created this demo and engine modification in a (successful) attempt to gain greater control over the physics system of the T2D engine, specifically the rigid-body physics, allowing forces to affect both linear and angular motion of objects by applying force to a specific location on the object.

The Change

The code consists of modifying 2 files, located in the [root]/engine/source/T2D directory:
t2dSceneObject.h and t2dSceneObject.cc.

The .h (header) file is modified to provide function declarations for 2 new functions:
setImpulseTorqueForce(. . .) and setImpulseAngularForce(. . .)
These functions act as a proxy between 3 new Torque Script console commands:
setImpulseTorqueForce(. . .), setImpulseTorqueForcePolar(. . .) and setImpulseAngularForce(. . .)

Since this article is intended for those who use Torque Script (C++ coders can simply use the functions these commands interface to) I will not bother to explain how the C++ works. If you understand C++, they should be pretty easy to figure out. Below is an explanation of each of the Script console commands and what they do:

setImpulseTorqueForce(vector force, vector forcePosition, [bool gravitic], [bool relative])
vector force is x/y specifying the magnitude of the force on each axis
vector forcePosition is x/y specifying the location the force applies to, relative to the center of the object
bool relative is a boolean value that specifies if the force position is relative to the object's origin or the scene's origin (false if not specified)

setImpulseTorqueForcePolar(float angle, float force, vector forcePosition, [bool gravitic], [bool relative])
float angle is a decimal # specifying the direction of the force
float force is a decimal # specifying the magnitude of the force
vector forcePosition is x/y specifying the location the force applies to, relative to the center of the object
bool relative is a boolean value that specifies if the force position is relative to the object's origin or the scene's origin (false if not specified)

setImpulseAngularForce(float force, [bool gravitic])
float force is a decimal # specifying the magnitude of the force (positive == counter clockwise)

Here are several examples of how these functions can be used in real-world situations:
(note: running the same command multiple times will increase force / speed)

%object.setImpulseAngularForce( 50 );
Causes the object to spin counter-clockwise.

%object.setImpulseAngularForce( -50 );
Causes the object to spin clockwise.

%object.setImpulseTorqueForce("0 10", "0 0");
Causes the object to move straight down.

%object.setImpulseTorqueForce("0 10", "1 0");
Causes the object to move downward while rotating clockwise

%object.setImpulseTorqueForce("0 10", "1 0", false);
This is a bit tricky. Since the relative variable is false, the force will be applied to coordinates 1,0 from the center of the scene. Either way it will move down, but which way it rotates depends on whether it is left or right of X coordinate 1.

%object.setImpulseTorqueForce("0 10", $player.getLinkPoint($player.lp1), false);
This will push down on the object at the point where link point $player.lp1 points to. Since the getLinkPoint function provides the location of the link point relative to the scene and not the object, we use the 'false' relative value.

%object.setImpulseTorqueForcePolar(45, 10, $player.getLinkPoint($player.lp1), false);
This will push on the object, with 10 units of force, on a 45-degree angle, at the point specified by link point $player.lp1

%object.setImpulseTorqueForcePolar($player.getRotation, 10, "0 0", false);
This will push on the object, with 10 units of force, on the center of the object, facing the same direction as the object as defined by the getRotation function. This has the effect of moving the object 'forward'. It is also possible, using link points or other references, to 'steer' the object.

The Demo

The demo provides the ability to play around with the physics. Simply extract the contents of the ZIP file into a clean copy of the engine, overwriting the files where applicable, and rebuild the engine. I had originally intended to provide a binary executable, but it is too big to upload (1.1MB zipped while the upload limits to 200K) so you will have to build the engine yourself.
Please note this was built on version 1.1B1, I do not know (and doubt) that this will work on the 1.02 version.

The demo uses setImpulseTorqueForcePolar to drive the ship around. All directions described below are relative to the ship, i.e. "up" means "the top of the ship". You can use the following controls to control the ship:

W - apply an upward force to both sides of ship
S - apply a downward force to the center of the ship
A - apply an upward force to the right side of the ship (making it turn left)
D - apply an upward force to the left side of the ship (making it turn right)
[space bar] - set the position of the ship to the center of the screen
0 (number zero) set camera zoom to default
- (minus sign) zoom out
= (or + sign) zoom in

The Code

Below is the code if you wish to modify your engine. The demo also includes the 2 modified files.

t2dSceneObject.h
I put this code directly under the "setImpulseTorqueForce" declaration, under like 577 (in my copy). It can probably be placed elsewhere, though this location will keep it within context.
void			setImpulseTorqueForce( const t2dVector& force, const t2dVector& forcePosition, const bool gravitic, const bool relative);
void			setImpulseAngularForce( const F32& force, const bool gravitic );

t2dSceneObject.cc
I put this code at the bottom of the file, mainly so I could play around with it without constantly having to search. You could potentially put it anywhere in the file, as long as, of course, it is not inside of another function (but that's basic programming knowledge!)
//-----------------------------------------------------------------------------
// Set Impulse Torque Force
//-----------------------------------------------------------------------------
ConsoleMethod(t2dSceneObject, setImpulseTorqueForce, void, 3, 8, "(forceX / forceY, forcePosX / forcePosY, [gravitic?], [relative?] ) - Apply an instantaneous force to a specific location.")
{
   // The force.
   t2dVector force;
   // The force position.
   t2dVector forcePosition;
   // Absolute or Relative force position
   bool relative = true;
   // Gravitic force
   bool gravitic = false;
   // Grab the element count.
   U32 elementCount1 = t2dSceneObject::getStringElementCount(argv[2]);
   U32 elementCount2 = t2dSceneObject::getStringElementCount(argv[3]);
   U32 elementCount3 = t2dSceneObject::getStringElementCount(argv[4]);

   // ("forceX forceY", ...)
   if ((elementCount1 == 2) && (argc < 9))
   {
      force = t2dSceneObject::getStringElementVector(argv[2]);  
	  // ( ..., "forcePosX forcePosY", [gravitic], [relative])
	  if ((elementCount2 == 2) && (argc < 9))
	  {
		forcePosition = t2dSceneObject::getStringElementVector(argv[3]);  
		if(argc > 4) gravitic = dAtob(argv[4]);
		if(argc > 5) relative = dAtob(argv[5]);		
	  }
	  // ( ..., forcePosX, forcePosY, [gravitic], [relative])
	  else if ((elementCount2 == 1) && (argc > 3))
	  {
		forcePosition = t2dVector(dAtof(argv[3]), dAtof(argv[4]));
		if(argc > 5) gravitic = dAtob(argv[5]);
		if(argc > 6) relative = dAtob(argv[6]);
	  }
   }

   // (forceX, forceY, ...)
   else if ((elementCount1 == 1) && (argc > 3))
   {
      force = t2dVector(dAtof(argv[2]), dAtof(argv[3]));
	  // ( ..., "forcePosX forcePosY", [gravitic], [relative])
	  if ((elementCount3 == 2) && (argc < 9))
	  {
		forcePosition = t2dSceneObject::getStringElementVector(argv[4]);  
		if(argc > 5) gravitic = dAtob(argv[5]);
		if(argc > 6) relative = dAtob(argv[6]);
	  }
	  // ( ..., forcePosX, forcePosY, [gravitic], [relative])
	  else if ((elementCount3 == 1) && (argc > 3))
	  {
		forcePosition = t2dVector(dAtof(argv[4]), dAtof(argv[5]));
		if(argc > 6) gravitic = dAtob(argv[6]);
		if(argc > 7) relative = dAtob(argv[7]);
	  }
   }

   // Invalid
   else
   {
      Con::warnf("t2dSceneObject::setImpulseTorqueForce() - Invalid number of parameters!");
      return;
   }

   // Set Torque Force.
   object->setImpulseTorqueForce(force, forcePosition, gravitic, relative);
}

//-----------------------------------------------------------------------------
// Set Impulse Torque Force Polar
//-----------------------------------------------------------------------------
ConsoleMethod(t2dSceneObject, setImpulseTorqueForcePolar, void, 3, 8, "(angle, force, forcePosX / forcePosY, [gravitic?], [relative?]) - Apply an instantaneous polar force to a specific location.")
{
   // Renormalise Angle.
   F32 angle = mFmod(dAtof(argv[2]), 360.0f);
   // Fetch Force.
   F32 force = dAtof(argv[3]);
   // The force position.
   t2dVector forcePosition;
   // Absolute or Relative force position
   bool relative = true;
   // Gravitic force;
   bool gravitic = false;
   // Grab the element count.
   U32 elementCount = t2dSceneObject::getStringElementCount(argv[4]);
   
   // (angle, force "forcePosX forcePosY", [relative], [gravitic])
   if ((elementCount == 2 && argc < 9))
   {
      forcePosition = t2dSceneObject::getStringElementVector(argv[4]);
	  if(argc > 5) gravitic = dAtob(argv[5]);
	  if(argc > 6) relative = dAtob(argv[6]);
   }

   // (angle, force forcePosX, forcePosY, [relative], [gravitic])
   else if ((elementCount == 1) && (argc > 3))
   {
      forcePosition = t2dVector(dAtof(argv[4]), dAtof(argv[5]));
	  if(argc > 6) gravitic = dAtob(argv[6]);
	  if(argc > 7) relative = dAtob(argv[7]);
   }
   // Invalid
   else
   {
      Con::warnf("t2dSceneObject::setImpulseTorqueForce() - Invalid number of parameters!");
	  return;
   }
   
   // Set Torque Force.
   object->setImpulseTorqueForce(t2dVector( mSin(mDegToRad(angle))*force, -mCos(mDegToRad(angle))*force ), forcePosition, relative, gravitic);
}

// Set Torque Force.
void t2dSceneObject::setImpulseTorqueForce( const t2dVector& force, const t2dVector& forcePosition, const bool gravitic, const bool relative)
{
	t2dVector objectPosition;
	//Determine where to apply force
	if(relative){
		// Get absolute location of force, relative to object
		objectPosition = getWorldPoint(forcePosition);
	}else{
		// Get absolute location of force, relative to center of scene
		objectPosition = forcePosition;
	}
    // Add Impulse Torque Force to Physics.
    getParentPhysics().addGrossTorqueForce( gravitic ? force * getParentPhysics().getMass() : force, objectPosition );
}

//-----------------------------------------------------------------------------
// Set Angular Force
//-----------------------------------------------------------------------------
ConsoleMethod(t2dSceneObject, setImpulseAngularForce, void, 2, 4, "(force, gravitic) - Apply an instantaneous rotational force.")
{
   F32 force = dAtof(argv[2]);
   bool gravitic = false;
   if(argc > 3) gravitic = dAtob(argv[3]);
   // Set Angular Force.
   object->setImpulseAngularForce( force, gravitic );
}

// Set Angular Force.
void t2dSceneObject::setImpulseAngularForce( const F32& force, const bool gravitic )
{
    // Add Impulse Torque Force to Physics.
    getParentPhysics().addGrossAngularForce( gravitic ? force * getParentPhysics().getMass() : force );
}

Once the code is in, rebuild and you're ready to start using these new commands!

Conclusion

I hope this article has been a helpful addition to the T2D quorum of knowledge. I know I will definitely find good use for this code, as many of the ideas I've had for games involve physics which are either extremely difficult or extremely dirty if forces can only be applied to an object's center of gravity. Hopefully the exposure of these functions will help Torque Script users make more powerful and more interesting games!

About the author

Recent Blogs