First Person Shooter Controller

First Person Shooter Controller

I'm using Havok and Ogre.
I'm trying to acomplish a first person shooter controller, but for some reason my character slides like if it is on ice and walks in diagonal.
Here is my code:

#include "OgreHavokCharacterController.h"

static const hkVector4 UP (0,1,0);


OgreHavokCharacterController::OgreHavokCharacterController(OgreHavokWorld* world)
{
	m_world = world;
	m_timestep = 1.0f/60.0f;
	m_posX = 0.0f;
	m_posZ = 0.0f;
	m_jumpHeight = 0.0f;
	m_rotation = 0.0f;

	m_crouch = false;
	m_jump = false;

	m_time = 0.0f;

	m_world->getWorld()->lock();
	//	Create a character rigid body object
	{
		// Construct a shape
		hkVector4 vertexA(0.0f,0.4f, 0.0f);
		hkVector4 vertexB(0.0f,-0.4f,0.0f);

		// Create a capsule to represent the character standing
		m_standShape = new hkpCapsuleShape(vertexA, vertexB, 0.6f);

		// Create a capsule to represent the character crouching
		// Note that we create the smaller capsule with the base at the same position as the larger capsule.		
		vertexA.setZero4();
		m_crouchShape = new hkpCapsuleShape(vertexA, vertexB, 0.6f);


		// Construct a character rigid body
		hkpCharacterRigidBodyCinfo info;
		info.m_mass = 100.0f;
		info.m_shape = m_standShape;
		info.m_maxForce = 1000.0f;
		info.m_up = UP;
		info.m_position.set(0.0f, 0.0f, 0.0f);
		info.m_maxSlope = HK_REAL_PI / 3.0f;
		info.m_friction = 0.0f;
		
		m_characterRigidBody = new hkpCharacterRigidBody( info );
		{
			hkpCharacterRigidBodyListener* listener = new hkpCharacterRigidBodyListener();
			m_characterRigidBody->setListener( listener );
			listener->removeReference();
		}
		m_world->getWorld()->addEntity( m_characterRigidBody->getRigidBody() );

	}

	// Create the Character state machine and context
	{
		hkpCharacterState* state;
		hkpCharacterStateManager* manager = new hkpCharacterStateManager();

		state = new hkpCharacterStateOnGround();
		manager->registerState( state,	HK_CHARACTER_ON_GROUND);
		state->removeReference();

		state = new hkpCharacterStateInAir();
		manager->registerState( state,	HK_CHARACTER_IN_AIR);
		state->removeReference();

		state = new hkpCharacterStateJumping();
		manager->registerState( state,	HK_CHARACTER_JUMPING);
		state->removeReference();

		state = new hkpCharacterStateClimbing();
		manager->registerState( state,	HK_CHARACTER_CLIMBING);
		state->removeReference();

		m_characterContext = new hkpCharacterContext( manager, HK_CHARACTER_ON_GROUND );
		manager->removeReference();

		// Set character type
		m_characterContext->setCharacterType(hkpCharacterContext::HK_CHARACTER_RIGIDBODY);
	}

	// Initialize hkpSurfaceInfo for old ground info holding
	m_previousGround = new hkpSurfaceInfo();
	m_framesInAir = 0;

	// Current camera angle about up
	m_currentAngle = HK_REAL_PI * 0.5f;	

	m_world->getWorld()->unlock();
}

OgreHavokCharacterController::~OgreHavokCharacterController()
{
	m_world->getWorld()->markForWrite();

	m_characterRigidBody->removeReference();

	m_standShape->removeReference();
	m_crouchShape->removeReference();

	delete m_characterContext;

	delete m_previousGround;
}

void OgreHavokCharacterController::move(CharacterMovement direction, Ogre::Real value)
{
	/*if(direction == FORWARD)
	{m_posZ -= value;}
	if(direction == BACKWARD)
	{m_posZ = value;}
	if(direction == STRAFE_LEFT)
	{m_posX = value;}
	if(direction == STRAFE_RIGHT)
	{m_posX -= value;}*/
	if(direction == FORWARD || direction == BACKWARD)
	{
		m_posZ = value;
	}

	if(direction == STRAFE_LEFT || direction == STRAFE_RIGHT)
	{
		m_posX = value;
	}
	
}
void OgreHavokCharacterController::changeState(CharacterMovement direction, bool value)
{
	if(direction == JUMP)
	{m_jump = value;}
	if(direction == CROUCH)
	{m_crouch = value;}
}
void OgreHavokCharacterController::setJumpHeight(Ogre::Real m_jumpHeight)
{
	this->m_jumpHeight = m_jumpHeight;
}

void OgreHavokCharacterController::setRotation(Ogre::Real rotation)
{
	m_rotation = rotation;
}

void OgreHavokCharacterController::setPosition(Ogre::Vector3 position)
{
	hkVector4 hkposition = hkVector4((hkReal)position.x,(hkReal)position.y,(hkReal)position.z);
	m_characterRigidBody->getRigidBody()->setPosition(hkposition);	
}

void OgreHavokCharacterController::setOrientation(Ogre::Quaternion orientation)
{
	m_currentOrient = hkQuaternion ( orientation.w , orientation.x, orientation.y, orientation.z);
}

Ogre::Vector3 OgreHavokCharacterController::getPosition()
{
	Ogre::Vector3 position = Ogre::Vector3::ZERO;

	hkVector4 hkposition = m_characterRigidBody->getRigidBody()->getPosition();
	position.x = hkposition(0);
	position.y = hkposition(1);
	position.z = hkposition(2);

	return position;
}

Ogre::Quaternion OgreHavokCharacterController::getOrientation()
{
	return Ogre::Quaternion(m_currentOrient.m_vec(3),m_currentOrient.m_vec(0),m_currentOrient.m_vec(1),m_currentOrient.m_vec(2));
}
/////////////////////////////////////////////////////////////////////

void OgreHavokCharacterController::update()
{

	m_world->getWorld()->lock();

	// update total run time
	m_time += m_timestep;

	//	Get user input data

	{
		m_currentAngle += m_rotation;
		m_currentOrient.setAxisAngle(UP, m_currentAngle);
	}


	HK_TIMER_BEGIN( "set character state", HK_NULL );

	hkpCharacterInput input;
	hkpCharacterOutput output;
	{
		input.m_inputUD = m_posZ;
		input.m_inputLR = m_posX;
		
		input.m_wantJump =  false;
		input.m_atLadder = false;

		input.m_up = UP;

		
		{
			// We project the camera direction onto the xz-plane to obtain the forward direction.
			Ogre::Vector3 dir = cGraphicalInterface::getSingleton().getCamera()->getDirection();
			input.m_forward.set( dir.x,0.0f,dir.z);
		}

		input.m_forward.setRotatedDir( m_currentOrient, input.m_forward );

		hkStepInfo stepInfo;
		stepInfo.m_deltaTime = m_timestep;
		stepInfo.m_invDeltaTime = 1.0f/m_timestep;
		stepInfo.m_endTime = m_time;

		input.m_stepInfo = stepInfo;

		input.m_characterGravity.set(0,-14,0);
		input.m_velocity = m_characterRigidBody->getRigidBody()->getLinearVelocity();
		input.m_position = m_characterRigidBody->getRigidBody()->getPosition();

		//m_characterRigidBody->checkSupport(stepInfo, input.m_surfaceInfo);

		hkArray supportInfo;
		input.m_surfaceInfo.m_supportedState = m_characterRigidBody->getSupportInfo( stepInfo, supportInfo );
		if ( input.m_surfaceInfo.m_supportedState != hkpSurfaceInfo::UNSUPPORTED )
		{
			getGround( supportInfo, input.m_surfaceInfo );
		}
		
	}

	HK_TIMER_END();

	// Apply the character state machine
	{
		HK_TIMER_BEGIN( "update character state", HK_NULL );

		m_characterContext->update( input, output );

		HK_TIMER_END();
	}

	//Apply the player character controller
	{
		HK_TIMER_BEGIN( "simulate character", HK_NULL );

		// Set output velocity from state machine into character rigid body
		m_characterRigidBody->setLinearVelocity(output.m_velocity, m_timestep);

		HK_TIMER_END();

		m_world->getWorld()->unlock();
	}

	// Display state
	{
		hkpCharacterStateType state = m_characterContext->getState();
		const char * stateStr;

		switch (state)
		{
		case HK_CHARACTER_ON_GROUND:
			stateStr = "On Ground";	break;
		case HK_CHARACTER_JUMPING:
			stateStr = "Jumping"; break;
		case HK_CHARACTER_IN_AIR:
			stateStr = "In Air"; break;
		case HK_CHARACTER_CLIMBING:
			stateStr = "Climbing"; break;
		default:
			stateStr = "Other";	break;
		}
		
	}
}

void OgreHavokCharacterController::getGround( const hkArray& supportInfo, hkpSurfaceInfo& ground ) const
{
	// Produce an average version of the ground

	ground.m_surfaceVelocity.setZero4();
	ground.m_surfaceNormal.setZero4();
	ground.m_surfaceDistanceExcess = 0.0f;
	ground.m_surfaceIsDynamic = false;

	const int numSupportInfo = supportInfo.getSize();
	for ( int i = 0; i < numSupportInfo; ++i )
	{
		const hkpCharacterRigidBody::SupportInfo& support = supportInfo[i];
		ground.m_surfaceNormal.add4( support.m_point.getNormal() );
		ground.m_surfaceDistanceExcess += support.m_point.getDistance();
		const hkpMotion::MotionType motionType = support.m_rigidBody->getMotionType();
		if ( motionType == hkpMotion::MOTION_KEYFRAMED )
		{
			hkVector4 pointVelocity;
			support.m_rigidBody->getPointVelocity( support.m_point.getPosition(), pointVelocity );
			ground.m_surfaceVelocity.add4( pointVelocity );
		}
		else if ( motionType != hkpMotion::MOTION_FIXED )
		{
			ground.m_surfaceIsDynamic = true;
			if ( bool(m_bodyType.getWithDefault ( support.m_rigidBody, false )) )
			{
				hkVector4 pointVelocity;
				support.m_rigidBody->getPointVelocity( support.m_point.getPosition(), pointVelocity );
				ground.m_surfaceVelocity.add4( pointVelocity );
			}
		}
	}

	ground.m_surfaceNormal.normalize3();
	const hkReal portion = 1.f / numSupportInfo;

	ground.m_surfaceVelocity.mul4( portion );
	ground.m_surfaceDistanceExcess = ( ground.m_surfaceDistanceExcess * portion );

	if ( ground.m_surfaceIsDynamic )
	{
		// We need to apply the character's weight onto dynamic bodies. We do this by
		// setting a positive surfaceDistanceExcess which the controller should try
		// to reduce by applying gravity.
		ground.m_surfaceDistanceExcess = 0.01f;
	}
	else
	{
		// For fixed and keyframed bodies, we subtract m_hardSupportDistance from the excess
		// to ensure an extra gap below the character. This improves the smoothness of the
		// character's motion over the ground.
		ground.m_surfaceDistanceExcess -= m_characterRigidBody->m_hardSupportDistance;
	}

	// For dynamic supporting bodies which do keep velocity, we apply gravity representing the 
	// force required to keep the character in position.
	hkVector4 impulse;
	{
		impulse.setMul4( m_characterRigidBody->m_character->getMass() * m_timestep, m_world->getWorld()->getGravity() );
		// We want the component of the impulse tangential to the surface.
		const hkSimdReal normal = impulse.dot3( ground.m_surfaceNormal );
		impulse.setAddMul4( impulse, ground.m_surfaceNormal, -normal );
		// Divide the amount of impulse by the number of supporting surfaces
		impulse.mul4( 1.0f / numSupportInfo );
	}

	for ( int i = 0; i < numSupportInfo; ++i )
	{
		const hkpCharacterRigidBody::SupportInfo& support = supportInfo[i];

		if ( bool(m_bodyType.getWithDefault( support.m_rigidBody, false )) )
		{
			// Apply the impulse at the contact point.
			support.m_rigidBody->applyPointImpulse( impulse, support.m_point.getPosition() );
		}
	}
}

and this is my mopp level:

OgreHavokBody::OgreHavokBody(Ogre::Entity *entity, int vertex_count, Ogre::Vector3* vertices, int index_count, int *indices)
{	
	Ogre::Vector3 sPos = Ogre::Vector3::ZERO;

	m_indices.setSize(index_count);
	m_vertices.setSize(vertex_count);

	for (int j = 0; j < vertex_count; j+=1 )
	{
		m_vertices[j] = hkVector4(vertices[j].x,vertices[j].y,vertices[j].z,0);
	}

	for (int j = 0; j < index_count; j++ )
	{
		m_indices[j] = int(indices[j]);
	}

	hkpExtendedMeshShape* pMesh = new hkpExtendedMeshShape();
	pMesh->setRadius( 0.01f );

	hkpExtendedMeshShape::TrianglesSubpart part;

	part.m_vertexBase = (float*)m_vertices.begin();
	part.m_vertexStriding = sizeof(hkVector4);//sizeof(float) * 4;
	part.m_numVertices = m_vertices.getSize();//vertex_count;


	part.m_indexBase = m_indices.begin();
	part.m_indexStriding =  sizeof( hkUint32 ) * 3;//3*sizeof(int);
	hkUint32 ic = index_count/3;
	part.m_numTriangleShapes = (hkUint32)ic;

	part.m_stridingType = hkpExtendedMeshShape::INDICES_INT32;

	pMesh->addTrianglesSubpart(part);


	/////////////////// SHAPE CONSTRUCTION ////////////////

	hkpMoppCompilerInput mci;

	mci.m_cachePrimitiveExtents = true;
	mci.m_enableChunkSubdivision = true;
	mci.m_enableInterleavedBuilding = true;
	mci.m_enablePrimitiveSplitting = false;

	pMesh->m_defaultCollisionFilterInfo = hkpGroupFilter::calcFilterInfo(1,0);
	pMesh->m_weldingType = hkpWeldingUtility::WELDING_TYPE_ANTICLOCKWISE;

	hkpMoppCode* pCode = hkpMoppUtility::buildCode(pMesh, mci);		

	hkpMoppBvTreeShape* pTreeShape = new hkpMoppBvTreeShape(pMesh, pCode);

	// Remove references since the MoppBvTreeShape now "owns" the listShape and code
	pCode->removeReference();
	pMesh->removeReference();

	hkpRigidBodyCinfo rigidBodyInfos;

	rigidBodyInfos.m_shape = pTreeShape;
	rigidBodyInfos.m_position.set(sPos.x, sPos.y, sPos.z);
	// If we set this to true, the body is fixed, and no mass properties need to be computed.
	rigidBodyInfos.m_motionType = hkpMotion::MOTION_FIXED;

	// Create a rigid body (using the template above).
	rigidBody = new hkpRigidBody(rigidBodyInfos);	

	// Remove references since the body now "owns" the Shapes.
	pTreeShape->removeReference();
}

Since it's a fps game i'm getting the forward vector like this:

Ogre::Vector3 dir = cGraphicalInterface::getSingleton().getCamera()->getDirection();
			input.m_forward.set( dir.x,0.0f,dir.z);

And my up vector is in Y not Z:

static const hkVector4 UP (0,1,0);

What I'm doing wrong?

Thanks in advance!!

SOLUTION:

I finally solved my two problems, walk in diagonal and sliding, i added this two lines in the end of update():

m_posZ = 0;
m_posX = 0;

in here:

in here:

// Display state
	{
		hkpCharacterStateType state = m_characterContext->getState();
		const char * stateStr;

		switch (state)
		{
		case HK_CHARACTER_ON_GROUND:
			stateStr = "On Ground";	break;
		case HK_CHARACTER_JUMPING:
			stateStr = "Jumping"; break;
		case HK_CHARACTER_IN_AIR:
			stateStr = "In Air"; break;
		case HK_CHARACTER_CLIMBING:
			stateStr = "Climbing"; break;
		default:
			stateStr = "Other";	break;
		}
		
	}
	
	m_posZ = 0;
	   m_posX = 0;
}

The problem was that x and z pos had the last value in every frame even if i only walk in the z position, x also had the value of the last frame instead of 0.

I hope this help someone with the same problem.

3 帖子 / 0 全新
最新文章
如需更全面地了解编译器优化,请参阅优化注意事项

Thanks for sharing your solution :)

Cormac

No problem mate, I allways do that, that way other guys with the same problem don't need to create new threads.
For some reason i always find difficult to find examples for FPS games, if i share my examples maybe i can change that.

发表评论

登录添加评论。还不是成员?立即加入