Third-person camera
Contents
Do not use this on the Orange Box Engine
This is the old way of doing things. For the OrangeBox, please see Thirdperson-OrangeBox
Overview
This tutorial will teach you how to create a third person camera perspective with the following features:
- Collision detection
- Mouse-wheel zooming
- Transparency when too close
Techniques Used
- Hiding view model (Gordon's hands) in third person view
- Hiding weapon in third person (or first) view
- Hiding crosshair in third person (or first) view
- Hiding player model when in first person view
- Adding your own keyboard command
Thank you to the Third Person Camera articles on The Wavelength. This tutorial is a combination of both with a few user friendly modifications.
Step 1: in_camera.cpp
This tutorial assumes you are creating a Half-Life 2 Single Player modification and using Visual Studio .NET 2003.
Open up Game_Sdk.sln in VS.NET.
The first step will be to change the camera code inside in_camera.cpp (located in the client Project).
Comment the line:
#define CAM_MIN_DIST 36.0
// #define CAM_MIN_DIST 36.0
Under the line #define YAW_MIN -135.0
Add:
in_camera.cpp
#define CAM_MIN_DIST 16.0 // Don't let the camera get any closer than ...
#define CAM_MAX_DIST 96.0 // ... or any farther away than ...
#define CAM_SWITCH_DIST 96.0 // the default camera distance when switching 1st to 3rd person
#define CAM_HULL_OFFSET 6.0 // the size of the bounding hull used for collision checking
#define START_TRANS_DIST 40.0 // how close to player when it starts making model translucent
#define TRANS_DELTA 1.9921875 // Set to 255 / START_TRANS_DIST
static Vector CAM_HULL_MIN(-CAM_HULL_OFFSET,-CAM_HULL_OFFSET,-CAM_HULL_OFFSET);
static Vector CAM_HULL_MAX( CAM_HULL_OFFSET, CAM_HULL_OFFSET, CAM_HULL_OFFSET);
Under the line: static kbutton_t cam_in, cam_out, cam_move;
Add:
float mf_NextSwitch;
Replace:
static ConVar cam_idealyaw( "cam_idealyaw", "90", FCVAR_ARCHIVE ); // thirdperson yaw
With:
static ConVar cam_idealyaw( "cam_idealyaw", "0", FCVAR_ARCHIVE ); /* thirdperson yaw
| fix side view bug */
Here, all we are doing is change the CAM_MIN_DIST variable and adding our own variables to use later in the code. Instead of commenting out CAM_MIN_DIST you can just delete it. We also fixed a bug that makes the camera view the player from the side and not from behind (you will need to delete your config.cfg file for this to update and take effect).
Replace this section (under the function CAM_Think):
if( !m_fCameraInThirdPerson )
return;
With this:
// Toggle first/3rd person, check every 0.5 seconds
if(gpGlobals->curtime >= mf_NextSwitch) {
if(input->KeyState(&in_camchange))
{
mf_NextSwitch = gpGlobals->curtime + 0.5; /* Check if the key has been pressed every
0.5 seconds */
if(!m_fCameraInThirdPerson)
{
CAM_ToThirdPerson();
} else {
CAM_ToFirstPerson();
}
}
}
if( !m_fCameraInThirdPerson )
{
// If in FP mode, transparent
// This is sort of a hack since when the map first loads you ARE transparent...
// This is for when you switch from 3rd to 1st.
if(C_BasePlayer::GetLocalPlayer() ) {
C_BasePlayer::GetLocalPlayer()->SetRenderMode( kRenderTransColor );
C_BasePlayer::GetLocalPlayer()->SetRenderColorA(0);
}
return;
}
Here we tell the code when to check if the user has pressed the in_camchange button (which we will define later on). We have also put in some code that will turn the player transparent when you switch FROM 3rd person TO 1st person. This is to prevent the player model from getting in the way of the camera.
mf_NextSwitch = gpGlobals->curtime + 0.5
to a smaller number if you want to check more frequently if a user has pressed the "in_camchange" key.Within this piece of code:
else if( input->KeyState( &cam_out ) ) {
dist += CAM_DIST_DELTA;
}
Add right after dist += CAM_DIST_DELTA;
:
// Prevent the cam_out from going any further
// NOTE: Without this, it _appears_ it's not moving, but
// the transparency changes so it is... this fixes it.
if( dist > CAM_MAX_DIST )
dist = CAM_MAX_DIST;
Replace this:
if( cam_snapto.GetInt() )
{
camAngles[ YAW ] = cam_idealyaw.GetFloat() + viewangles[ YAW ];
camAngles[ PITCH ] = cam_idealpitch.GetFloat() + viewangles[ PITCH ];
camAngles[ 2 ] = cam_idealdist.GetFloat();
}
else
{
// code already here
}
With:
if( cam_snapto.GetInt() )
{
camAngles[ YAW ] = cam_idealyaw.GetFloat() + viewangles[ YAW ];
camAngles[ PITCH ] = cam_idealpitch.GetFloat() + viewangles[ PITCH ];
camAngles[ 2 ] = cam_idealdist.GetFloat();
}
else
{
if( camAngles[ YAW ] - viewangles[ YAW ] != cam_idealyaw.GetFloat() )
camAngles[ YAW ] = MoveToward( camAngles[ YAW ],
cam_idealyaw.GetFloat() + viewangles[ YAW ],
CAM_ANGLE_SPEED );
if( camAngles[ PITCH ] - viewangles[ PITCH ] != cam_idealpitch.GetFloat() )
camAngles[ PITCH ] = MoveToward( camAngles[ PITCH ],
cam_idealpitch.GetFloat() + viewangles[ PITCH ],
CAM_ANGLE_SPEED );
if( !C_BasePlayer::GetLocalPlayer() ) {
// this code can be hit from the main menu, where it will crash
camAngles[ 2 ] = dist; // if there's no localplayer to calc from
}
else {
trace_t tr;
float adjDist = dist;
C_BasePlayer* localPlayer = C_BasePlayer::GetLocalPlayer();
Vector origin = localPlayer->GetLocalOrigin(); // find our player's origin
origin += localPlayer->GetViewOffset(); // and from there, his eye position
AngleVectors( QAngle(camAngles.x, camAngles.y, camAngles.z),
&camForward, NULL, NULL ); // get the forward vector
UTIL_TraceHull( origin, origin - (camForward * dist),
CAM_HULL_MIN, CAM_HULL_MAX,
MASK_SOLID, NULL, &tr ); /* use our previously #defined hull to
collision trace */
if( tr.fraction < 1.0 ) {
adjDist = dist * tr.fraction; // move the camera closer if it hit something
}
else {
adjDist = dist; // no trace hit, use cam_idealdist without adjusting it
}
if ( adjDist < START_TRANS_DIST ) {
localPlayer->SetRenderMode( kRenderTransColor ); // make him translucent
localPlayer->SetRenderColorA( (byte)(adjDist * TRANS_DELTA) ); // closer=less opacity
}
if( adjDist < CAM_MIN_DIST )
adjDist = CAM_MIN_DIST; // clamp up to minimum
if( adjDist > CAM_MAX_DIST )
adjDist = CAM_MAX_DIST; // clamp down to maximum
camAngles[ 2 ] = adjDist;
}
}
Just under the above code you should see
m_vecCameraOffset[ 0 ] = camAngles[ 0 ];
m_vecCameraOffset[ 1 ] = camAngles[ 1 ];
m_vecCameraOffset[ 2 ] = dist;
Replace
m_vecCameraOffset[ 2 ] = dist;
With:
m_vecCameraOffset[ 2 ] = camAngles[ 2 ];
Here we are checking for collision and handling transparency.
In the function void CInput::CAM_ToThirdPerson(void)
Change:
m_vecCameraOffset[ 2 ] = CAM_MIN_DIST;
To:
m_vecCameraOffset[ 2 ] = CAM_SWITCH_DIST; /* When switching, move camera to our specified switch
distance */
Add underneath that:
// Set model to 'player' because when a map first loads
// it doesn't set the model... sort of a hack.
//C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
//pPlayer->SetModel("player");
NB: This code actually causes the game to crash when using the current HL2DM code, although the third person camera works fine without it. Uncomment these lines if you're experiencing problems and your player model is not being loaded, otherwise leave them commented.
Step 2: c_basecombatweapon.cpp
Open the file c_basecombatweapon.cpp under the client project.
Underneath this, in C_BaseCombatWeapon::DrawModel( int flags ):
// check if local player chases owner of this weapon in first person
C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer();
Add:
// Get rid of view model, crosshair and weapon in 3rd person
// Once we actually get weapons working, we'll let it draw only the weapon
if (localplayer)
{
if (input->CAM_IsThirdPerson())
{
// make sure the caller is the player to prevent hiding EVERYONE's weapon model.
if(localplayer == GetOwner())
{
SetModel( GetWorldModel() );
// Hide crosshair, remove this line if you still want to see it.
localplayer->m_Local.m_iHideHUD |= HIDEHUD_CROSSHAIR;
return false;
/* Comment above and uncomment the line underneath if you want
to be able to do weapon things (it will display the weapon). */
//return BaseClass:: DrawModel( flags );
}
}
}
Here we have told the code to hide the view model (Gordon's hands) and the crosshair when in third person view. The way the code is written above, the weapon model is also hidden in 3rd person view. If you want your mod to be able to show the weapon in third person view, comment return false;
and uncomment return BaseClass:: DrawModel( flags );
.
On how to go about positioning weapons and whatnot for 3rd person view, this tutorial does not cover that and assumes you would like to hide the weapon in 3rd person view.
Step 3: in_main.cpp
Open up in_main.cpp in the client project.
Underneath the line kbutton_t in_graph;
, add:
kbutton_t in_camchange;
Underneath:
void IN_ZoomDown(void) {KeyDown(&in_zoom);}
void IN_ZoomUp(void) {KeyUp(&in_zoom);}
Add:
void IN_CamChangeDown(void) {KeyDown(&in_camchange);}
void IN_CamChangeUp(void) {KeyUp(&in_camchange);}
Underneath CalcButtonBits( bits, IN_ZOOM, s_ClearInputState, &in_zoom, bResetState );
, add:
CalcButtonBits( bits, IN_CAMCHANGE, s_ClearInputState, &in_camchange, bResetState );
Underneath:
static ConCommand startzoom("+zoom", IN_ZoomDown);
static ConCommand endzoom("-zoom", IN_ZoomUp);
Add:
static ConCommand startcamchange("+camchange", IN_CamChangeDown);
static ConCommand endcamchange("-camchange", IN_CamChangeUp);
Step 4: input.h & in_buttons.h
Open the file in_buttons.h under Header Files under the server project.
Underneath #define IN_BULLRUSH (1 << 22)
, add:
#define IN_CAMCHANGE (1 << 23)
#define {command} (1 << {int})
Open the file input.h under Header Files under the client project.
Underneath:
extern kbutton_t in_back;
Add:
extern kbutton_t in_camchange;
Step 5: kb_act.lst
Open up kb_act.lst in your SourceMods/{your_mod}/scripts/ folder. Now, anywhere you want the label to appear, add:
"+camchange" "Toggle 3rd/1st Person View"
You can add a token in your {mod_name}_english.txt also and refer to that (e.g. "#Mod_CameraToggle").
Step 6: Animations
Originally found on VERC Forums:
Posted by: Whisp
- This is a follow up to a post that I did awhile back. I was trying to see if any knew how to add animation in thirdperson view to a single player mod. No one knew, but I am happy to annouce that I have found the way. By looking at the multiple player code I found that the CHL2MP_Player class is derived from CHL2_Player of the single player game. If you look in the CHL2MP_Player class you will find that the SetAnimation function is overridden. I copied this function from the MP source and added it to the CHL2_Player class of my single player mod. I then change the player model to one of the Humans (Alyx works also) and modified the MP function to use SP activities. Below is the function as it sits now. I hope this helps anyone trying to add this sort of animation. I have not found a way to tell if the player is running to not so the animation is currently always run (that is the way it is in MP as well). I tried checking for the IN_RUN key but that did not work. I also tried the speed code at the top of the MP function and that also did not work. If anyone knows a way to tell this please let me know.
// Set the activity based on an event or current state
void CHL2_Player::SetAnimation( PLAYER_ANIM playerAnim )
{
int animDesired;
float speed;
speed = GetAbsVelocity().Length2D();
if ( GetFlags() & ( FL_FROZEN | FL_ATCONTROLS ) )
{
speed = 0;
playerAnim = PLAYER_IDLE;
}
Activity idealActivity = ACT_RUN;
// This could stand to be redone. Why is playerAnim abstracted from activity? (sjb)
if ( playerAnim == PLAYER_JUMP )
{
idealActivity = ACT_JUMP;
}
else if ( playerAnim == PLAYER_DIE )
{
if ( m_lifeState == LIFE_ALIVE )
{
return;
}
}
else if ( playerAnim == PLAYER_ATTACK1 )
{
if ( GetActivity( ) == ACT_HOVER ||
GetActivity( ) == ACT_SWIM ||
GetActivity( ) == ACT_HOP ||
GetActivity( ) == ACT_LEAP ||
GetActivity( ) == ACT_DIESIMPLE )
{
idealActivity = GetActivity( );
}
else
{
idealActivity = ACT_GESTURE_RANGE_ATTACK1;
}
}
else if ( playerAnim == PLAYER_RELOAD )
{
idealActivity = ACT_GESTURE_RELOAD;
}
else if ( playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK )
{
if ( !( GetFlags() & FL_ONGROUND ) && GetActivity( ) == ACT_JUMP ) // Still jumping
{
idealActivity = GetActivity( );
}
/*
else if ( GetWaterLevel() > 1 )
{
if ( speed == 0 )
idealActivity = ACT_HOVER;
else
idealActivity = ACT_SWIM;
}
*/
else
{
if ( GetFlags() & FL_DUCKING )
{
if ( speed > 0 )
{
idealActivity = ACT_WALK_CROUCH;
}
else
{
idealActivity = ACT_COVER_LOW; //ACT_IDLE_CROUCH;
}
}
else
{
if ( speed > 0 )
{
idealActivity = m_fIsSprinting ? ACT_WALK : ACT_RUN;
}
else
{
idealActivity = ACT_IDLE;
}
}
}
//idealActivity = TranslateTeamActivity( idealActivity );
}
if ( idealActivity == ACT_GESTURE_RANGE_ATTACK1 )
{
RestartGesture( Weapon_TranslateActivity( idealActivity ) );
// FIXME: this seems a bit wacked
Weapon_SetActivity( Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 );
return;
}
else if ( idealActivity == ACT_GESTURE_RELOAD )
{
RestartGesture( Weapon_TranslateActivity( idealActivity ) );
return;
}
else
{
SetActivity( idealActivity );
animDesired = SelectWeightedSequence( Weapon_TranslateActivity ( idealActivity ) );
if (animDesired == -1)
{
animDesired = SelectWeightedSequence( idealActivity );
if ( animDesired == -1 )
{
animDesired = 0;
}
}
// Already using the desired animation?
if ( GetSequence() == animDesired )
return;
m_flPlaybackRate = 1.0;
ResetSequence( animDesired );
SetCycle( 0 );
return;
}
// Already using the desired animation?
if ( GetSequence() == animDesired )
return;
//Msg( "Set animation to %d\n", animDesired );
// Reset to first frame of desired animation
ResetSequence( animDesired );
SetCycle( 0 );
}
== the above code needs to go inside hl2_player.cpp. this then needs to be defined in hl2_player.h so at line 174 put:
void SetAnimation ( PLAYER_ANIM playerAnim );
==
Conclusion
You should now have a working third person / first person hybrid. If you want to add a default key for "+camchange", refer to Customizing Options: Keyboard wiki article.
If you didn't hide the player model and the weapon, you will see an ugly model which is supposed to represent Gordon Freeman. Valve didn't attempt to use the 3rd person in HL2 so you will have to found a player model with all weapon animations and install it !
Reference
Reference code
in_camera.cpp
Your finished in_camera.cpp should now look like:
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Handles 3rd and 1st Person Views
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "hud.h"
#include "kbutton.h"
#include "input.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-------------------------------------------------- Constants
#define CAM_DIST_DELTA 1.0
#define CAM_ANGLE_DELTA 2.5
#define CAM_ANGLE_SPEED 2.5
#define CAM_ANGLE_MOVE .5
#define MAX_ANGLE_DIFF 10.0
#define PITCH_MAX 90.0
#define PITCH_MIN 0
#define YAW_MAX 135.0
#define YAW_MIN -135.0
#define CAM_MIN_DIST 16.0 // Don't let the camera get any closer than ...
#define CAM_SWITCH_DIST 96.0 // the default distance when switching from 1st to 3rd
#define CAM_MAX_DIST 96.0 // ... or any farther away than ...
#define CAM_HULL_OFFSET 6.0 // the size of the bounding hull used for collision checking
#define START_TRANS_DIST 40.0 // how close to player when it starts making model translucent
#define TRANS_DELTA 1.9921875 // Set to 255 / START_TRANS_DIST
static Vector CAM_HULL_MIN(-CAM_HULL_OFFSET,-CAM_HULL_OFFSET,-CAM_HULL_OFFSET);
static Vector CAM_HULL_MAX( CAM_HULL_OFFSET, CAM_HULL_OFFSET, CAM_HULL_OFFSET);
//-------------------------------------------------- Global Variables
static ConVar cam_command( "cam_command", "0", FCVAR_CHEAT | FCVAR_ARCHIVE ); /* tells camera to go
to thirdperson */
static ConVar cam_snapto( "cam_snapto", "0", FCVAR_ARCHIVE ); // snap to thirdperson view
static ConVar cam_idealyaw( "cam_idealyaw", "0", FCVAR_ARCHIVE ); // thirdperson yaw
static ConVar cam_idealpitch( "cam_idealpitch", "0", FCVAR_ARCHIVE ); // thirperson pitch
static ConVar cam_idealdist( "cam_idealdist", "64", FCVAR_ARCHIVE ); // thirdperson distance
static ConVar c_maxpitch( "c_maxpitch", "90", FCVAR_ARCHIVE );
static ConVar c_minpitch( "c_minpitch", "0", FCVAR_ARCHIVE );
static ConVar c_maxyaw( "c_maxyaw", "135", FCVAR_ARCHIVE );
static ConVar c_minyaw( "c_minyaw", "-135", FCVAR_ARCHIVE );
static ConVar c_maxdistance( "c_maxdistance", "200", FCVAR_ARCHIVE );
static ConVar c_mindistance( "c_mindistance", "30", FCVAR_ARCHIVE );
static ConVar c_orthowidth( "c_orthowidth", "100", FCVAR_ARCHIVE );
static ConVar c_orthoheight( "c_orthoheight", "100", FCVAR_ARCHIVE );
static kbutton_t cam_pitchup, cam_pitchdown, cam_yawleft, cam_yawright;
static kbutton_t cam_in, cam_out, cam_move;
float mf_NextSwitch;
// API Wrappers
/*
==============================
CAM_ToThirdPerson
==============================
*/
void CAM_ToThirdPerson(void)
{
input->CAM_ToThirdPerson();
}
/*
==============================
CAM_ToFirstPerson
==============================
*/
void CAM_ToFirstPerson(void)
{
input->CAM_ToFirstPerson();
}
/*
==============================
CAM_ToOrthographic
==============================
*/
void CAM_ToOrthographic(void)
{
input->CAM_ToOrthographic();
}
/*
==============================
CAM_StartMouseMove
==============================
*/
void CAM_StartMouseMove( void )
{
input->CAM_StartMouseMove();
}
/*
==============================
CAM_EndMouseMove
==============================
*/
void CAM_EndMouseMove( void )
{
input->CAM_EndMouseMove();
}
/*
==============================
CAM_StartDistance
==============================
*/
void CAM_StartDistance( void )
{
input->CAM_StartDistance();
}
/*
==============================
CAM_EndDistance
==============================
*/
void CAM_EndDistance( void )
{
input->CAM_EndDistance();
}
/*
==============================
CAM_ToggleSnapto
==============================
*/
void CAM_ToggleSnapto( void )
{
cam_snapto.SetValue( !cam_snapto.GetInt() );
}
/*
==============================
MoveToward
==============================
*/
float MoveToward( float cur, float goal, float maxspeed )
{
if( cur != goal )
{
if( abs( cur - goal ) > 180.0 )
{
if( cur < goal )
cur += 360.0;
else
cur -= 360.0;
}
if( cur < goal )
{
if( cur < goal - 1.0 )
cur += ( goal - cur ) / 4.0;
else
cur = goal;
}
else
{
if( cur > goal + 1.0 )
cur -= ( cur - goal ) / 4.0;
else
cur = goal;
}
}
// bring cur back into range
if( cur < 0 )
cur += 360.0;
else if( cur >= 360 )
cur -= 360;
return cur;
}
/*
==============================
CAM_Think
==============================
*/
void CInput::CAM_Think( void )
{
Vector origin;
Vector ext, pnt, camForward, camRight, camUp;
float dist;
Vector camAngles;
float flSensitivity;
QAngle viewangles;
switch( cam_command.GetInt() )
{
case CAM_COMMAND_TOTHIRDPERSON:
CAM_ToThirdPerson();
break;
case CAM_COMMAND_TOFIRSTPERSON:
CAM_ToFirstPerson();
break;
case CAM_COMMAND_NONE:
default:
break;
}
// Toggle first/3rd person
if(gpGlobals->curtime >= mf_NextSwitch) {
if(input->KeyState(&in_camchange))
{
mf_NextSwitch = gpGlobals->curtime + 0.5;
if(!m_fCameraInThirdPerson)
{
CAM_ToThirdPerson();
} else {
CAM_ToFirstPerson();
}
}
}
if( !m_fCameraInThirdPerson )
{
// If in FP mode, transparent
// This is sort of a hack since when the map first loads you ARE transparent...
// This is for when you switch from 3rd to 1st.
if(C_BasePlayer::GetLocalPlayer() ) {
C_BasePlayer::GetLocalPlayer()->SetRenderMode( kRenderTransColor );
C_BasePlayer::GetLocalPlayer()->SetRenderColorA(0);
}
return;
}
camAngles[ PITCH ] = cam_idealpitch.GetFloat();
camAngles[ YAW ] = cam_idealyaw.GetFloat();
dist = cam_idealdist.GetFloat();
//
//movement of the camera with the mouse
//
if (m_fCameraMovingWithMouse)
{
int cpx, cpy;
//get windows cursor position
GetMousePos (cpx, cpy);
m_nCameraX = cpx;
m_nCameraY = cpy;
//check for X delta values and adjust accordingly
//eventually adjust YAW based on amount of movement
//don't do any movement of cam using YAW/PITCH if we are zooming in/out the camera
if (!m_fCameraDistanceMove)
{
int x, y;
GetWindowCenter( x, y );
/* keep the camera within certain limits around the player (ie avoid certain
bad viewing angles) */
if (m_nCameraX>x)
{
//if ((camAngles[YAW]>=225.0)||(camAngles[YAW]<135.0))
if (camAngles[YAW]<c_maxyaw.GetFloat())
{
camAngles[ YAW ] += (CAM_ANGLE_MOVE)*((m_nCameraX-x)/2);
}
if (camAngles[YAW]>c_maxyaw.GetFloat())
{
camAngles[YAW]=c_maxyaw.GetFloat();
}
}
else if (m_nCameraX<x)
{
//if ((camAngles[YAW]<=135.0)||(camAngles[YAW]>225.0))
if (camAngles[YAW]>c_minyaw.GetFloat())
{
camAngles[ YAW ] -= (CAM_ANGLE_MOVE)* ((x-m_nCameraX)/2);
}
if (camAngles[YAW]<c_minyaw.GetFloat())
{
camAngles[YAW]=c_minyaw.GetFloat();
}
}
//check for y delta values and adjust accordingly
//eventually adjust PITCH based on amount of movement
//also make sure camera is within bounds
if (m_nCameraY > y)
{
if(camAngles[PITCH]<c_maxpitch.GetFloat())
{
camAngles[PITCH] +=(CAM_ANGLE_MOVE)* ((m_nCameraY-y)/2);
}
if (camAngles[PITCH]>c_maxpitch.GetFloat())
{
camAngles[PITCH]=c_maxpitch.GetFloat();
}
}
else if (m_nCameraY<y)
{
if (camAngles[PITCH]>c_minpitch.GetFloat())
{
camAngles[PITCH] -= (CAM_ANGLE_MOVE)*((y-m_nCameraY)/2);
}
if (camAngles[PITCH]<c_minpitch.GetFloat())
{
camAngles[PITCH]=c_minpitch.GetFloat();
}
}
//set old mouse coordinates to current mouse coordinates
//since we are done with the mouse
if ( ( flSensitivity = gHUD.GetSensitivity() ) != 0 )
{
m_nCameraOldX=m_nCameraX*flSensitivity;
m_nCameraOldY=m_nCameraY*flSensitivity;
}
else
{
m_nCameraOldX=m_nCameraX;
m_nCameraOldY=m_nCameraY;
}
ResetMouse();
}
}
//Nathan code here
if( input->KeyState( &cam_pitchup ) )
camAngles[ PITCH ] += CAM_ANGLE_DELTA;
else if( input->KeyState( &cam_pitchdown ) )
camAngles[ PITCH ] -= CAM_ANGLE_DELTA;
if( input->KeyState( &cam_yawleft ) )
camAngles[ YAW ] -= CAM_ANGLE_DELTA;
else if( input->KeyState( &cam_yawright ) )
camAngles[ YAW ] += CAM_ANGLE_DELTA;
if( input->KeyState( &cam_in ) )
{
dist -= CAM_DIST_DELTA;
if( dist < CAM_MIN_DIST )
{
// If we go back into first person, reset the angle
camAngles[ PITCH ] = 0;
camAngles[ YAW ] = 0;
dist = CAM_MIN_DIST;
}
}
else if( input->KeyState( &cam_out ) ) {
dist += CAM_DIST_DELTA;
// Prevent the cam_out from going any further
// NOTE: Without this, it _appears_ it's not moving, but
// the transparency changes so it is... this fixes it.
if( dist > CAM_MAX_DIST )
dist = CAM_MAX_DIST;
}
if (m_fCameraDistanceMove)
{
int x, y;
GetWindowCenter( x, y );
if (m_nCameraY>y)
{
if(dist<c_maxdistance.GetFloat())
{
dist +=CAM_DIST_DELTA * ((m_nCameraY-y)/2);
}
if (dist>c_maxdistance.GetFloat())
{
dist=c_maxdistance.GetFloat();
}
}
else if (m_nCameraY<y)
{
if (dist>c_mindistance.GetFloat())
{
dist -= (CAM_DIST_DELTA)*((y-m_nCameraY)/2);
}
if (dist<c_mindistance.GetFloat())
{
dist=c_mindistance.GetFloat();
}
}
//set old mouse coordinates to current mouse coordinates
//since we are done with the mouse
m_nCameraOldX=m_nCameraX*gHUD.GetSensitivity();
m_nCameraOldY=m_nCameraY*gHUD.GetSensitivity();
ResetMouse();
}
// update ideal
cam_idealpitch.SetValue( camAngles[ PITCH ] );
cam_idealyaw.SetValue( camAngles[ YAW ] );
cam_idealdist.SetValue( dist );
// Move towards ideal
VectorCopy( m_vecCameraOffset, camAngles );
engine->GetViewAngles( viewangles );
if( cam_snapto.GetInt() )
{
camAngles[ YAW ] = cam_idealyaw.GetFloat() + viewangles[ YAW ];
camAngles[ PITCH ] = cam_idealpitch.GetFloat() + viewangles[ PITCH ];
camAngles[ 2 ] = cam_idealdist.GetFloat();
}
else
{
if( camAngles[ YAW ] - viewangles[ YAW ] != cam_idealyaw.GetFloat() )
camAngles[ YAW ] = MoveToward( camAngles[ YAW ],
cam_idealyaw.GetFloat() + viewangles[ YAW ],
CAM_ANGLE_SPEED );
if( camAngles[ PITCH ] - viewangles[ PITCH ] != cam_idealpitch.GetFloat() )
camAngles[ PITCH ] = MoveToward( camAngles[ PITCH ],
cam_idealpitch.GetFloat() + viewangles[ PITCH ],
CAM_ANGLE_SPEED );
if( !C_BasePlayer::GetLocalPlayer() ) {
// this code can be hit from the main menu, where it will crash
camAngles[ 2 ] = dist; // if there's no localplayer to calc from
}
else {
trace_t tr;
float adjDist = dist;
C_BasePlayer* localPlayer = C_BasePlayer::GetLocalPlayer();
Vector origin = localPlayer->GetLocalOrigin(); // find our player's origin
origin += localPlayer->GetViewOffset(); // and from there, his eye position
AngleVectors( QAngle(camAngles.x, camAngles.y, camAngles.z),
&camForward, NULL, NULL ); // get the forward vector
UTIL_TraceHull( origin, origin - (camForward * dist),
CAM_HULL_MIN, CAM_HULL_MAX,
MASK_SOLID, NULL, &tr ); /* use our previously #defined hull to
collision trace */
if( tr.fraction < 1.0 ) {
adjDist = dist * tr.fraction; // move the camera closer if it hit something
}
else {
adjDist = dist; // no trace hit, use cam_idealdist without adjusting it
}
if ( adjDist < START_TRANS_DIST ) {
// make him translucent
localPlayer->SetRenderMode( kRenderTransColor );
// closer = less opacity
localPlayer->SetRenderColorA( (byte)(adjDist * TRANS_DELTA) );
}
if( adjDist < CAM_MIN_DIST )
adjDist = CAM_MIN_DIST; // clamp up to minimum
if( adjDist > CAM_MAX_DIST )
adjDist = CAM_MAX_DIST; // clamp down to maximum
camAngles[ 2 ] = adjDist;
}
}
m_vecCameraOffset[ 0 ] = camAngles[ 0 ];
m_vecCameraOffset[ 1 ] = camAngles[ 1 ];
m_vecCameraOffset[ 2 ] = camAngles[ 2 ];
}
void CAM_PitchUpDown(void) { KeyDown( &cam_pitchup ); }
void CAM_PitchUpUp(void) { KeyUp( &cam_pitchup ); }
void CAM_PitchDownDown(void) { KeyDown( &cam_pitchdown ); }
void CAM_PitchDownUp(void) { KeyUp( &cam_pitchdown ); }
void CAM_YawLeftDown(void) { KeyDown( &cam_yawleft ); }
void CAM_YawLeftUp(void) { KeyUp( &cam_yawleft ); }
void CAM_YawRightDown(void) { KeyDown( &cam_yawright ); }
void CAM_YawRightUp(void) { KeyUp( &cam_yawright ); }
void CAM_InDown(void) { KeyDown( &cam_in ); }
void CAM_InUp(void) { KeyUp( &cam_in ); }
void CAM_OutDown(void) { KeyDown( &cam_out ); }
void CAM_OutUp(void) { KeyUp( &cam_out ); }
/*
==============================
CAM_ToThirdPerson
==============================
*/
void CInput::CAM_ToThirdPerson(void)
{
QAngle viewangles;
// Do allow third person in TF2 for now
#if !defined( TF2_CLIENT_DLL ) && !defined( CSTRIKE_DLL )
#if !defined( _DEBUG )
if ( gpGlobals->maxClients > 1 )
{
// no thirdperson in multiplayer.
return;
}
#endif
#endif
engine->GetViewAngles( viewangles );
if( !m_fCameraInThirdPerson )
{
m_fCameraInThirdPerson = true;
m_vecCameraOffset[ YAW ] = viewangles[ YAW ];
m_vecCameraOffset[ PITCH ] = viewangles[ PITCH ];
m_vecCameraOffset[ 2 ] = CAM_SWITCH_DIST;
// Set model to player because when a map first loads
// it doesn't set the model... sort of a hack.
//C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
//pPlayer->SetModel("player");
}
cam_command.SetValue( 0 );
}
/*
==============================
CAM_ToFirstPerson
==============================
*/
void CInput::CAM_ToFirstPerson(void)
{
m_fCameraInThirdPerson = false;
cam_command.SetValue( 0 );
}
/*
==============================
CAM_ToFirstPerson
==============================
*/
bool CInput::CAM_IsOrthographic(void) const
{
return m_CameraIsOrthographic;
}
/*
==============================
CAM_ToFirstPerson
==============================
*/
void CInput::CAM_OrthographicSize(float& w, float& h) const
{
w = c_orthowidth.GetFloat(); h = c_orthoheight.GetFloat();
}
/*
==============================
CAM_ToFirstPerson
==============================
*/
void CInput::CAM_ToOrthographic(void)
{
m_fCameraInThirdPerson = false;
m_CameraIsOrthographic = true;
cam_command.SetValue( 0 );
}
/*
==============================
CAM_StartMouseMove
==============================
*/
void CInput::CAM_StartMouseMove(void)
{
float flSensitivity;
//only move the cam with mouse if we are in third person.
if ( m_fCameraInThirdPerson )
{
//set appropriate flags and initialize the old mouse position
//variables for mouse camera movement
if (!m_fCameraMovingWithMouse)
{
int cpx, cpy;
m_fCameraMovingWithMouse=true;
m_fCameraInterceptingMouse=true;
GetMousePos(cpx, cpy);
m_nCameraX = cpx;
m_nCameraY = cpy;
if ( ( flSensitivity = gHUD.GetSensitivity() ) != 0 )
{
m_nCameraOldX=m_nCameraX*flSensitivity;
m_nCameraOldY=m_nCameraY*flSensitivity;
}
else
{
m_nCameraOldX=m_nCameraX;
m_nCameraOldY=m_nCameraY;
}
}
}
//we are not in 3rd person view..therefore do not allow camera movement
else
{
m_fCameraMovingWithMouse=false;
m_fCameraInterceptingMouse=false;
}
}
/*
==============================
CAM_EndMouseMove
the key has been released for camera movement
tell the engine that mouse camera movement is off
==============================
*/
void CInput::CAM_EndMouseMove(void)
{
m_fCameraMovingWithMouse=false;
m_fCameraInterceptingMouse=false;
}
/*
==============================
CAM_StartDistance
routines to start the process of moving the cam in or out
using the mouse
==============================
*/
void CInput::CAM_StartDistance(void)
{
//only move the cam with mouse if we are in third person.
if ( m_fCameraInThirdPerson )
{
//set appropriate flags and initialize the old mouse position
//variables for mouse camera movement
if (!m_fCameraDistanceMove)
{
int cpx, cpy;
m_fCameraDistanceMove=true;
m_fCameraMovingWithMouse=true;
m_fCameraInterceptingMouse=true;
GetMousePos(cpx, cpy);
m_nCameraX = cpx;
m_nCameraY = cpy;
m_nCameraOldX=m_nCameraX*gHUD.GetSensitivity();
m_nCameraOldY=m_nCameraY*gHUD.GetSensitivity();
}
}
//we are not in 3rd person view..therefore do not allow camera movement
else
{
m_fCameraDistanceMove=false;
m_fCameraMovingWithMouse=false;
m_fCameraInterceptingMouse=false;
}
}
/*
==============================
CAM_EndDistance
the key has been released for camera movement
tell the engine that mouse camera movement is off
==============================
*/
void CInput::CAM_EndDistance(void)
{
m_fCameraDistanceMove=false;
m_fCameraMovingWithMouse=false;
m_fCameraInterceptingMouse=false;
}
/*
==============================
CAM_IsThirdPerson
==============================
*/
int CInput::CAM_IsThirdPerson( void )
{
return m_fCameraInThirdPerson;
}
/*
==============================
CAM_GetCameraOffset
==============================
*/
void CInput::CAM_GetCameraOffset( Vector& ofs )
{
VectorCopy( m_vecCameraOffset, ofs );
}
/*
==============================
CAM_InterceptingMouse
==============================
*/
int CInput::CAM_InterceptingMouse( void )
{
return m_fCameraInterceptingMouse;
}
static ConCommand startpitchup( "+campitchup", CAM_PitchUpDown );
static ConCommand endpitcup( "-campitchup", CAM_PitchUpUp );
static ConCommand startpitchdown( "+campitchdown", CAM_PitchDownDown );
static ConCommand endpitchdown( "-campitchdown", CAM_PitchDownUp );
static ConCommand startcamyawleft( "+camyawleft", CAM_YawLeftDown );
static ConCommand endcamyawleft( "-camyawleft", CAM_YawLeftUp );
static ConCommand startcamyawright( "+camyawright", CAM_YawRightDown );
static ConCommand endcamyawright( "-camyawright", CAM_YawRightUp );
static ConCommand startcamin( "+camin", CAM_InDown );
static ConCommand endcamin( "-camin", CAM_InUp );
static ConCommand startcamout( "+camout", CAM_OutDown );
static ConCommand camout( "-camout", CAM_OutUp );
static ConCommand thirdperson( "thirdperson", ::CAM_ToThirdPerson,
"Switch to thirdperson camera.", FCVAR_CHEAT );
static ConCommand firstperson( "firstperson", ::CAM_ToFirstPerson, "Switch to firstperson camera." );
static ConCommand camortho( "camortho", ::CAM_ToOrthographic,
"Switch to orthographic camera.", FCVAR_CHEAT );
static ConCommand startcammousemove( "+cammousemove",::CAM_StartMouseMove);
static ConCommand endcammousemove( "-cammousemove",::CAM_EndMouseMove);
static ConCommand startcamdistance( "+camdistance", ::CAM_StartDistance );
static ConCommand endcamdistance( "-camdistance", ::CAM_EndDistance );
static ConCommand snapto( "snapto", CAM_ToggleSnapto );
/*
==============================
Init_Camera
==============================
*/
void CInput::Init_Camera( void )
{
m_CameraIsOrthographic = false;
}
c_basecombatweapon.cpp
The DrawModel function should now look like:
//-----------------------------------------------------------------------------
// Purpose: Render the weapon. Draw the Viewmodel if the weapon's being carried
// by this player, otherwise draw the worldmodel.
//-----------------------------------------------------------------------------
int C_BaseCombatWeapon::DrawModel( int flags )
{
VPROF_BUDGET( "C_BaseCombatWeapon::DrawModel", VPROF_BUDGETGROUP_MODEL_RENDERING );
if ( !m_bReadyToDraw )
return 0;
if ( !IsVisible() )
return 0;
// check if local player chases owner of this weapon in first person
C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer();
// Get rid of view model, crosshair and weapon in 3rd person
// Once we actually get weapons working, we'll let it draw only the weapon
if (localplayer)
{
if (input->CAM_IsThirdPerson())
{
if(localplayer == GetOwner())
{
SetModel( GetWorldModel() );
localplayer->m_Local.m_iHideHUD |= HIDEHUD_CROSSHAIR;
return false;
//return BaseClass:: DrawModel( flags );
/* Uncomment this if you want to be
able to do weapon things
(it will display the weapon). */
}
}
}
if ( localplayer && localplayer->IsObserver() && GetOwner() )
{
// don't draw weapon if chasing this guy as spectator
// we don't check that in ShouldDraw() since this may change
// without notification
if ( localplayer->GetObserverMode() == OBS_MODE_IN_EYE &&
localplayer->GetObserverTarget() == GetOwner() )
return false;
}
return BaseClass::DrawModel( flags );
}