TorqueGameEngine/RigidShape
From TDN
This is an incomplete work in progress.
Contents |
Overview
Many people new to TGE are surprised to learn that it includes a physics system.
The purpose of this article is to show how to create and use a RigidShape object in TGE. The RigidShape class implements rigid-body physics for DTS objects in the world. "Rigid body physics" refers to a system whereby objects are assumed to have a finite size, equally distributed masses, and where deformations of the objects themselves are not accounted for.
Here is a list of the fields used in a RigidShapeData DataBlock.
What does that mean?
It would probably work well for billiards balls, but not so great for superballs. In TGE, the RigidShape class is the base class for all vehicles. In versions of TGE prior to 1.4.2, the RigidShape class was not exposed to scripting, unless you exposed it yourself in code (as described in this resource [1]). The RidigShape class does not implement joints, ragdolls, springs, or chains.
Other Physics Systems
Rigid-body physics might be sufficient for your needs; if not, some members of the community have integrated other physics systems into TGE. ODE is a popular open-source library used by a number of games. Newton is another free library that has been discussed in the GarageGames forums. PhysiX is a commercial library that can be used for free under certain conditions.
ODE Newton PhysiX
Note
This article is based entirely on information in Thomas Lund's resource. The RigidShape class was exposed to script in TGE 1.5, but its use was left undocumented, aside from that resource. If you know that resource already, there's nothing new here for you. I'm just cleaning it up for newbies like myself.
Using RigidShape in TGE 1.5
Here's some simple steps to get you started using RigidShape objects in TGE.
Step one: create a RigidShape datablock. In /tutorial.base/server/ create a new file called "boulder.cs":
datablock RigidShapeData( BouncingBoulder )
{
category = "RigidShape";
shapeFile = "~/data/shapes/boulder/boulder.dts";
emap = true;
// Rigid Body
mass = 500;
massCenter = "0 0 0"; // Center of mass for rigid body
massBox = "0 0 0"; // Size of box used for moment of inertia,
// if zero it defaults to object bounding box
drag = 0.2; // Drag coefficient
bodyFriction = 0.2;
bodyRestitution = 0.1;
minImpactSpeed = 5; // Impacts over this invoke the script callback
softImpactSpeed = 5; // Play SoftImpact Sound
hardImpactSpeed = 15; // Play HardImpact Sound
integration = 4; // Physics integration: TickSec/Rate
collisionTol = 0.1; // Collision distance tolerance
contactTol = 0.1; // Contact velocity tolerance
minRollSpeed = 10;
maxDrag = 0.5;
minDrag = 0.01;
triggerDustHeight = 1;
dustHeight = 10;
dragForce = 0.05;
vertFactor = 0.05;
normalForce = 0.05;
restorativeForce = 0.05;
rollForce = 0.05;
pitchForce = 0.05;
};
function RigidShapeData::create(%data)
{
// The mission editor invokes this method when it wants to create
// an object of the given datablock type.
%obj = new RigidShape() {
dataBlock = %data;
};
return %obj;
}
function RigidShapeData::onCollision(%data, %obj, %col, %vec, %speed)
{
//if it is colliding with it's self or the terrain, ignore it
if(%obj == %col || %col.getType() & $terrainObjectType)
return;
error("RigidShapeData::onCollision %col:" SPC %col.getDataBlock().getName());
error("RigidShapeData::onCollision %obj:" SPC %obj.getDataBlock().getName());
%normal = vectorDot(%speed, vectorNormalize(%speed));
if(%normal > 58)
%scale = %normal / 2;
%objMass = %obj.getDataBlock().mass;
%colVel = vectorLen(%col.getVelocity());
//add some random boost to the Z
%objVec = getWord(%vec, 0) SPC getWord(%vec, 1) SPC mFloor(getRandom(2, 5));
%objImpulse = %objMass * ((%speed / 100) + (%colVel / 15));
//the case is, we don't want to apply an impulse greater then X times the objects mass, so
//clamp it down, otherwise it might visit the moon
%objImpulse = %objImpulse / 8 > %objMass ? %objMass * 8 : %objImpulse;
error("objImpulse:" SPC %objImpulse);
%obj.applyImpulse(%obj.getWorldBoxCenter(), VectorScale(%objVec, %objImpulse));
}
This creates a datablock based on the RigidShapeData class. In the datablock you set the physical properties used to calculate the forces applied to the object, like the object's mass, center of mass, friction, etc. You also define a DTS shape to be used by the object; here I'm using Thomas Lund's boulder shape. It also implements a function "::create" which is called by the Mission Editor when one of these objects is created. And importantly, it implements "::onCollision" to handle collisions between the itself and other objects. (In Thomas Lund's resource, collisions are handled in the player's ::onArmor method, which meant that objects could collide with the player, but not with other objects. i.e. the player could run into a stack of crates and knock them down; but an AI player couldn't. I've changed this around so that objects themselves handle their collisions, so a wider variety of things in the world can interact.)
Step two: add the following line after line 24 of /tutorial.base/server/game.cs so that the new file is executed:
exec("./boulder.cs");
It should be in the "onServerCreated()" function.
Step three: download Thomas Lund's boulder and place the DTS shape and texture files into /tutorial.base/data/shapes/boulder/. (You can use a different path, but if so you'll need to adjust the shapeFile property in boulder.cs.)
Clear your DSOs and run TGE. Click "World Editor" to start the F World mission.
= Adding a boulder to the mission
Move the camera to the player (control-c). Hit F11 (control-F11 for Mac users) to turn on the mission editor. Turn on the "World Editor Creator" tool. In the list at the bottom right, you will see a tree list. Expand the items in the tree until you reach the "boulder" item:
+Static Shapes
+tutorial.base
+data
+shapes
+boulder
-boulder
Click on the final "boulder" item and a boulder will be instantiated into the world.
Grab its z-axis gizmo handle place it on the ground.
Hit F11 again and control-c so that you're controlling the player. Run into the boulder.



