Over the Shoulder View: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (Added chunk of code o)
Line 14: Line 14:
The first step is to make the game default to third person view. This is an easy step. Find and open the file <code>in_camera.cpp</code>, it will be in the client .dll files.
The first step is to make the game default to third person view. This is an easy step. Find and open the file <code>in_camera.cpp</code>, it will be in the client .dll files.


Now, find every instance of <code>sv_cheats</code> and either delete it or comment it out. This will get rid of the game checking to see if the server has sv_cheats enabled, and
Now, find every instance of <code>sv_cheats</code> and either delete it or comment it out (even the <code>if</code> conditions with it). This will get rid of the game checking to see if the server has sv_cheats enabled, and
if it is not, kicking them out of third person view.
if it is not, kicking them out of third person view.



Revision as of 11:57, 25 June 2009

Broom icon.png
This article or section needs to be cleaned up to conform to a higher standard of quality.
For help, see the VDC Editing Help and Wikipedia cleanup process. Also, remember to check for any notes left by the tagger at this article's talk page.

Overview

This tutorial will teach you how to create an over-the-shoulder camera view with collision detection.

Code is also included for variable cross hair screen position that will adjust so the player can have true aim with his/her cross hairs since going third person breaks it.

Steps

Step 1 - Default the game to third-person view

The first step is to make the game default to third person view. This is an easy step. Find and open the file in_camera.cpp, it will be in the client .dll files.

Now, find every instance of sv_cheats and either delete it or comment it out (even the if conditions with it). This will get rid of the game checking to see if the server has sv_cheats enabled, and if it is not, kicking them out of third person view.

Go to the last function of the file at the very end, which is: void CInput::Init_Camera( void ) Inside this function add: m_fCameraInThirdPerson = true; :

void CInput::Init_Camera( void )
{
	m_CameraIsOrthographic = false;
	m_fCameraInThirdPerson = true;
}

This will have the game defaulting to third person view at the very get go with no need for the player to do anything. This file receives no further changes and may be closed.

Step 2 - Creating the over-the-shoulder view

This is the step that will create the necessary changes to make the camera be over-the-shoulder. Find and open the file clientmode_shared.cpp. It is also in the client dll files.

Now, find the function void ClientModeShared::OverrideView( CViewSetup *pSetup ). This is where all the changes will be made.

Replace everything inside the function with:

// Let the player override the view.
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
if(!pPlayer)
     return;

pPlayer->OverrideView( pSetup );

if( ::input->CAM_IsThirdPerson() )
{
	Vector camForward, camRight, camUp;
	
        AngleVectors( pPlayer->EyeAngles(), &camForward, &camRight, &camUp );
 
	trace_t tr, tr2;
	Vector vecStart, vecStop, vecDirection, vecSetDirection; 
	static float camCurrentY; 
	static float camCurrentX=16.0f; //used for fluid camera transfers
	float camDelta=0.5f;
	vecStart=pSetup->origin;

	AngleVectors(pPlayer->EyeAngles(), &vecDirection); 
		
	vecSetDirection.Init(0,0,1.0f);
	vecDirection=vecDirection.Cross(vecSetDirection);
	vecStop = vecStart + vecDirection*52.0f;

	UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr );
		
	if (tr.fraction == 1) //are we far enough away to not be hugging a wall with the camera?
	{
		if(camCurrentX < 16.0f)
			camCurrentX +=camDelta;
		if(camCurrentX >16.0f)
			camCurrentX=16.0f;
		VectorMA( pSetup->origin, camCurrentX, camRight, pSetup->origin); //set the right offset
		VectorMA( pSetup->origin, 16.0f, camUp, pSetup->origin);
	 	vecStart=tr.endpos;
	}
	else 
	{	
		//we weren't clear on the right, lets check the left
			
		vecStop = vecStart + vecDirection * -52.0f;
			
		UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr );
					
		if (tr.fraction == 1) //are we clear on the left?
		{	
			if(camCurrentX > -16.0f)
				camCurrentX -=camDelta;
			if(camCurrentX < -16.0f)
				camCurrentX=-16.0f;
			VectorMA( pSetup->origin, camCurrentX, camRight, pSetup->origin);
			VectorMA( pSetup->origin, 16.0f, camUp, pSetup->origin);
			vecStart=tr.endpos;
		}
		else //not clear, so set the camera behind the player and raise it more than normal to maintain clear view
		{	
			//set camera behind player because left and right are not clear
			VectorMA( pSetup->origin, 0.0f, camRight, pSetup->origin); 
				//check to see if there is enough room above
			AngleVectors(pPlayer->EyeAngles(), &vecDirection); 
			vecSetDirection.Init(1.0f,0,0);
			vecDirection=vecDirection.Cross(vecSetDirection);
			vecStop = vecStart +vecDirection*32.0f;
			
			UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr);

			if(tr.fraction == 1)
			{
				VectorMA( pSetup->origin, 32.0f, camUp, pSetup->origin);
				vecStart=tr.endpos;
			}
			else //not enough room on left, right, or above, so move the camera eye level
				 //TODO: Add code to make player translucent as well so the player can see better
			{
				VectorMA( pSetup->origin, 0.0f, camUp, pSetup->origin);
			}
		}

	}
		 
	AngleVectors(pPlayer->EyeAngles(), &vecDirection); 
	vecStop = vecStart + vecDirection * -96;
	UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr );
	
	vecStart=pSetup->origin;
	
	vecStop = vecStart+vecDirection*-96;
	UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr );
		
	//multiply the default distance by the percentage the traceline traveled, should put the camera infront of the object
	   
	if(tr.fraction != 1)
	{
		camCurrentY = -96 * tr.fraction + 10.0f;
	}
	else
        {
		camCurrentY=-96.0f;
		VectorMA( pSetup->origin, camCurrentY, camForward, pSetup->origin);
	}
}


This will create an over-the-shoulder view that is defaulted to be over the right shoulder and it is offset 16 units right, 16 units up, and 96 units back. At time of creation this function is still undergoing tweaking and bug fixing and will be updated as it is fixed, but for most it will be more than enough to get someone started with their mod and they can make changes as they see fit. The camera also has collision detection built in by way of tracelines. The process has the camera checking if there is enough room on the right, and if there is it will set the x and z location to be +16/+16 of the player's origin. It then goes to the end where the distance behind the player is checked, where another two tracelines are done, one going back at the +16/+16 location, and one going straight back from the player. This is to prevent the camera from going into objects.

If there is no room on the right, it will check the left and if there is room it will set x and z to -16/+16 and then check distance behind. If there is no room on the left or the right, z is made +32 and x 0. This will put the camera above the player's model's head so the player can still see. If there is not enough room above, the camera will be placed at eye level and whatever distance is determined. This is one area of tweaking needed still as it should make the model become around 75% transparent so the player can still see forward. This would only occur in crawl spaces such as air ducts or very small hallways.

Step 3 - Fixing a Small Bug

There is one small bug that must be fixed and that is jittery animations when you move the camera up and down in the game with the mouse while looking around.

Find and open the file c_sdk_player.cpp. This should be in the client/sdk folder.

Find the function void C_SDKPlayer::UpdateClientSideAnimation() and replace its body with:

// Update the animation data. It does the local check here so this works when using
// a third-person camera (and we don't have valid player angles).
if ( this == C_SDKPlayer::GetLocalSDKPlayer() )
	m_PlayerAnimState->Update( EyeAngles()[YAW], EyeAngles()[PITCH] );
else
	m_PlayerAnimState->Update( m_angEyeAngles[YAW], EyeAngles()[PITCH] );

BaseClass::UpdateClientSideAnimation();

Now animations should be perfectly smooth in scratch built mods.

For HL2MP SDK, the steps are a bit different.

Find and open the file hl2mp_player.cpp. This should be in the server/hl2mp folder.

Find the function void CHL2MP_Player::PostThink( void ) and replace its body with:

BaseClass::PostThink();
	
if ( GetFlags() & FL_DUCKING )
{
	SetCollisionBounds( VEC_CROUCH_TRACE_MIN, VEC_CROUCH_TRACE_MAX );
}

m_PlayerAnimState.Update();

QAngle angles = GetLocalAngles();

// We need to see if this is the client that we're dealing with.
CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>(this);
if ( pPlayer )
{
	angles[PITCH] = EyeAngles()[PITCH];
	angles[YAW] = EyeAngles()[YAW];
}
else
{
	angles[PITCH] = EyeAngles()[PITCH];
	angles[YAW] = m_angEyeAngles[YAW];
}

SetLocalAngles( angles );

Now there should be no jitterness at all when moving your mouse in the thirdperson view.

Step 4 - Self-adjusting crosshair

This step will initialize self-adjusting cross hairs that will be drawn on the spot the player will actually shoot since the change of camera breaks the center-screen default of the cross hairs.

Find and open the file hud_crosshair.cpp. This is in the client files.

Now, find the fucntion void CHudCrosshair::Paint( void ).

Replace the inside of this function with:

if ( !m_pCrosshair )
	return;

if ( !IsCurrentViewAccessAllowed() )
	return; 

C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
Vector vecStart, vecStop, vecDirection, vecCrossPos;
trace_t tr;
	
AngleVectors(pPlayer->EyeAngles(), &vecDirection);

vecStart= pPlayer->EyePosition();
vecStop = vecStart + vecDirection * MAX_TRACE_LENGTH;
	
UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer , COLLISION_GROUP_NONE, &tr );
	
	
ScreenTransform(tr.endpos, vecCrossPos);

m_pCrosshair->DrawSelf( 0.5f*ScreenWidth()+0.5f*ScreenWidth()*vecCrossPos[0]-0.5f*m_pCrosshair->Width(), 
			0.5f*ScreenHeight()+(0.5f*ScreenHeight()*-vecCrossPos[1])-0.5f*m_pCrosshair->Height(),
			m_clrCrosshair );


This function will now have the cross hairs be drawn on the exact spot the player is actually going to hit, minus cone of fire spread. What it does is draw a traceline out pretty much exactly as it would if the player were to fire a weapon, get the exact world coordinates of the spot it hits, turn those coordinates into screen coordinates (not exactly though, read below for the full explanation), and then adjust those values to be the REAL screen coordinates of where the crosshair should be while painting it on the screen.

Conclusion

Now you should have an overview-the-shoulder view that will adjust based on the player's surroundings and adjusting cross hairs so the players can aim properly.

See also