How to apply a Force

From XNAWiki
Jump to: navigation, search


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 Newton

Now 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));