Tutorial - Character Controllers

From Newton Wiki
Jump to: navigation, search

We will use several features in cooperation to create a character that will be controlled by forces and torque, and at the same time it will stay in an upright position.

The elements we are going to use are: compound collision primitive with identification, especial joint called up vector, and special force and torque. This is a technical demo and therefore it will focus in showing the minimum amount of code needed to get the character running, features like: jumping, moving up and down ramps and stairs, crouching etc. are left to the application to implement.

The framework for this tutorial is taken from tutorial Introduction to Materials.

Open project Tutorial7_CharaterController in the function InitScene() you will see that the ball character is replaced by the call to

// Create a ball controllable by the player
AddCharacter (nWorld);

Open the file and find the function AddCharacter this function creates a rigid body with a compound collision geometry. The collision geometry is made out tree spheres and one skinny rod used to sense the floor under the character. Each part of the collision geometry has a generic ID that will be used later on by the contact callback in order to determine what part of the body produced the collision.

// Place a sphere at the center
collisionParts[count] = NewtonCreateSphere (nWorld, size.m_x * 0.75f, NULL); 
// set a identifier for this collsion
NewtonConvexCollisionSetUserID (collisionParts[count], SPHERE_COLLISION);
count ++;

// place a sphere on the upper half
offsetMatrix.m_posit.m_y = size.m_y * 0.25f;
collisionParts[count] = NewtonCreateSphere (nWorld, size.m_x * 0.75f, &offsetMatrix[0][0]); 
// set a identifier for this collsion
NewtonConvexCollisionSetUserID (collisionParts[count], SPHERE_COLLISION);
count ++;

// place a sphere on the lower half
offsetMatrix.m_posit.m_y = -size.m_y * 0.25f;
collisionParts[count] = NewtonCreateSphere (nWorld, size.m_x * 0.75f, &offsetMatrix[0][0]); 
// set a identifier for this collision
NewtonConvexCollisionSetUserID (collisionParts[count], SPHERE_COLLISION);
count ++;

// use a long box rod for support on the ground
offsetMatrix.m_posit.m_y = -size.m_y * 0.25f;
collisionParts[count] = NewtonCreateBox (nWorld, 0.05f, size.m_y, 0.05f, &offsetMatrix[0][0]); 
// set a identifier for this collsion
NewtonConvexCollisionSetUserID (collisionParts[count], BOX_COLLISION);
count ++;

// create the compound collision
collision = NewtonCreateCompoundCollision (nWorld, count, collisionParts);

The rest of this function follows the same model of all other rigid body creation, with the exception that this rigid body is constrained to the world by an up vector joint. This joint will allow the rigid body to translate in all three directions but it only allows rotation about the joint pin axis.

// Add an up vector constraint to help in keeping the body upright
NewtonJoint* upVector;
dVector upDirection (0.0f, 1.0f, 0.0f);
upVector = NewtonConstraintCreateUpVector (nWorld, &upDirection.m_x, chrBody);

The function CharacterApplyForceAndTorque applies a torque and a force to the body. This function uses information set up by the camera and keyboard in order to push and steer the body to the direction and orientation desired by the player.

The last interesting part of this project is the function callback CharacterContactProcess in the file materials.cpp

Here after the contact is processed by the base process contact call back, the function finds out which one of the bodies belongs to the character.

// Apply the default behavio
GenericContactProcess (material, contact);

// Get the spherical body, it is the body with non zero mass
// This is a quit and dirty way to determine the player body, but it no safe, 
//it only work in this case because one of the two bodies is the terrain which we now have //infinite mass.

// A better way to do this is by getting the user data and finding some object identifier // stored with the user data.

body = g_currectEffect->m_body0;
NewtonBodyGetMassMatrix (g_currectEffect->m_body0, &mass, &Ixx, &Iyy, &Izz);
if (mass == 0.0f) {
	body = g_currectEffect->m_body1;
	NewtonBodyGetMassMatrix (g_currectEffect->m_body1, &mass, &Ixx, &Iyy, &Izz);

Then once we have the character rigid body we ask Newton what part of the body is generating this contact

// Get the collision ID
collisionID = NewtonMaterialGetBodyCollisionID(material, body);

If the contact was produced by one of the spheres then we tell Newton not to use friction with it, this will allow the body to slide without rolling when it hit a wall or vertical obstacles.

if (collisionID == SPHERE_COLLISION) {
	// disable fiction calculation for sphere collision
	NewtonMaterialSetContactFrictionState (material, 0, 0);
	NewtonMaterialSetContactFrictionState (material, 0, 1);

If the contact was produced by the skinny rod then we align the tangent surface direction with the linear velocity vector of the body.