How to apply a Force
From XNAWiki
This sample shows how to apply a single force on a object.
First we have to create a little force class to hold te relevant informations:
enum ForceKind
{
Complet, //A force that translates and rotates a object
Thrust, //A force that only tranlates a object
Rotation, //A force that only rotates a object
}
class Force
{
public ForceKind Kind; //Defines how the force affects the object
public Vector3 Position; //The starting position of the force in meter(importent for the rotation)
public Vector3 Vector; //The actuall vector of the force in NewtonNow we will create the vlaues for the object, such as position or velocity:
class Object
{
public Vector3 position;
public Vector3 velocity;
public Vector3 angulaVelocitys;
public float mass;
public Model model;
public Quaternion localQuat;
}And now the true physics:
//First we will see how the Force translates the object:
Vector3 Acceleration = Vector3.Zero;
if (Force.Kind != ForceKind.Rotation)
{
Acceleration = Force.Vector / Object.mass;
}
//The values we calculate are SI values, which meanse that they are in meter and seconds.
//And because we calculate them around 60 times in the second we have to divide to values through 60.
//To make it more accurate you also can use a GameTime value to multiply it with the actuall time of
//the current frame.
Object.velocity += Acceleration / 60;
Object.position += Object.velocity;
//After we got the translation of the object we want to get the rotation of it:
//Lets start this in checking if we got a 'Thrust' force, if yes, we can skip the whole code.
if (Force.Kind != ForceKind.Thrust)
{
Vector3 AngulaAcc;
float MomentInertia;
//To keep this sample simple we will take to object as a sphere.
//For a list of Moment of Inertias, please visit http://en.wikipedia.org/wiki/List_of_moments_of_inertia
MomentInertia = 0.4f * Object.mass * (float)Math.Pow(Object.model.Meshes[0].BoundingSphere.Radius, 2f);
//Here we will create a second force which we can rotate into the relative coords of the object.
Force RotationForce = new Force();
Matrix rotationMatrix = Matrix.CreateFromQuaternion(Object.localQuat);
RotationForce.Vector = Vector3.Transform(Force.Vector, Matrix.Invert(rotationMatrix));
RotationForce.Position = Vector3.Transform(Force.Position, Matrix.Invert(rotationMatrix));
//As each axis is more or less affected independetly by the force, we just can calculate them that way.
float RadiusAroundX = 0;
float ForceAroundX;
ForceAroundX = (float)Math.Sqrt(Math.Pow(RotationForce.Vector.Y, 2) + Math.Pow(RotationForce.Vector.Z, 2));
if (ForceAroundX != 0.0f)
{
RadiusAroundX = (float)Math.Sqrt(Math.Pow(RotationForce.Position.Y, 2) + Math.Pow(RotationForce.Position.Z, 2));
float AlphaX; //Absolut angle from 0° axis to Radius vector in the noX plain.
AlphaX = (float)Math.Atan2(RotationForce.Position.Y, RotationForce.Position.Z);
float BetaX; //Absolut angle from 0° axis to Radius vector in the noX plain.
BetaX = (float)Math.Atan2(RotationForce.Vector.Y, RotationForce.Vector.Z);
float GammaX; //The relative angle between Radius and Force.
GammaX = ((float)Math.PI - BetaX) + AlphaX;
RadiusAroundX = (float)Math.Sin(GammaX) * RadiusAroundX;
//In the next line pleace keep in minde that you may have to chagne the algebraic sign. Just try it out.
AngulaAcc.X = - (float)(ForceAroundX * RadiusAroundX) / MomentInertia;
}
float RadiusAroundY = 0;
float ForceAroundY;
ForceAroundY = (float)Math.Sqrt(Math.Pow(RotationForce.Vector.X, 2) + Math.Pow(RotationForce.Vector.Z, 2));
if (ForceAroundY != 0.0f)
{
RadiusAroundY = (float)Math.Sqrt(Math.Pow(RotationForce.Position.X, 2) + Math.Pow(RotationForce.Position.Z, 2));
float AlphaY; //Absolut angle from 0° axis to Radius vector in the noX plain.
AlphaY = (float)Math.Atan2(RotationForce.Position.X, RotationForce.Position.Z);
float BetaY; //Absolut angle from 0° axis to Radius vector in the noX plain.
BetaY = (float)Math.Atan2(RotationForce.Vector.X, RotationForce.Vector.Z);
float GammaY; //The relative angle between Radius and Force.
GammaY = ((float)Math.PI - BetaY) + AlphaY;
RadiusAroundY = (float)Math.Sin(GammaY) * RadiusAroundY;
//In the next line pleace keep in minde that you may have to chagne the algebraic sign. Just try it out.
AngulaAcc.Y = (float)(ForceAroundY * RadiusAroundY) / MomentInertia;
}
float RadiusAroundZ = 0;
float ForceAroundZ;
ForceAroundZ = (float)Math.Sqrt(Math.Pow(RotationForce.Vector.X, 2) + Math.Pow(RotationForce.Vector.Y, 2));
if (ForceAroundZ != 0.0f)
{
RadiusAroundZ = (float)Math.Sqrt(Math.Pow(RotationForce.Position.X, 2) + Math.Pow(RotationForce.Position.Y, 2));
float AlphaZ; //Absolut angle from 0° axis to Radius vector in the noX plain.
AlphaZ = (float)Math.Atan2(RotationForce.Position.Y, RotationForce.Position.X);
float BetaZ; //Absolut angle from 0° axis to Radius vector in the noX plain.
BetaZ = (float)Math.Atan2(RotationForce.Vector.Y, RotationForce.Vector.X);
float GammaZ; //The relative angle between Radius and Force.
GammaZ = ((float)Math.PI - BetaZ) + AlphaZ;
RadiusAroundZ = (float)Math.Sin(GammaZ) * RadiusAroundZ;
//In the next line pleace keep in minde that you may have to chagne the algebraic sign. Just try it out.
AngulaAcc.Z = (float)(ForceAroundZ * RadiusAroundZ) / MomentInertia;
}
}
// /60 frames per seconds
Object.angulaVelocity += (AngulaAcc / 60);
Matrix m = Matrix.CreateFromQuaternion(Object.localQuat);
m = m
* Matrix.CreateFromAxisAngle(m.Right, Object.AngulaVelocity.X)
* Matrix.CreateFromAxisAngle(m.Forward, Object.AngulaVelocity.Z)
* Matrix.CreateFromAxisAngle(m.Up, Object.AngulaVelocity.Y);
Object.localQuat = Quaternion.Normalize(Quaternion.CreateFromRotationMatrix(m));