How to Make the FPS Character Controller RUN and CROUCH

Hi everybody, how can i tweak the FPS Character Controller to make my character can RUN and CROUCH?, Thank you.

The basic idea is simple: use different speeds when walking, running or crouching, and to crouch reduce the character vertical scale (remember to fix the character position while standing up to avoid falling through the floor). But if you are using the First Person Controller that comes with Unity, however, this may be a really hard job: it uses CharacterMotor.js, a very complicated script that usually drives crazy anyone who try to modify it. I know this very well: I was one of them… but I survived, and learned enough to do some tricks - including this!

To avoid editing the terrible CharacterMotor.js, I put all the code in this script; attach it to the First Person Controller, and they will communicate and do the magic:

var walkSpeed: float = 7; // regular speed
var crchSpeed: float = 3; // crouching speed
var runSpeed: float = 20; // run speed

private var chMotor: CharacterMotor;
private var tr: Transform;
private var dist: float; // distance to ground

function Start(){
    chMotor = GetComponent(CharacterMotor);
    tr = transform;
    var ch:CharacterController = GetComponent(CharacterController);
    dist = ch.height/2; // calculate distance to ground
}

function Update(){

    var vScale = 1.0;
    var speed = walkSpeed;
    
    if (chMotor.grounded && Input.GetKey("left shift") || Input.GetKey("right shift")){
        speed = runSpeed;
    }
    if (Input.GetKey("c")){ // press C to crouch
        vScale = 0.5;
        speed = crchSpeed; // slow down when crouching
    }
    chMotor.movement.maxForwardSpeed = speed; // set max speed
    var ultScale = tr.localScale.y; // crouch/stand up smoothly 
    tr.localScale.y = Mathf.Lerp(tr.localScale.y, vScale, 5*Time.deltaTime);
    tr.position.y += dist * (tr.localScale.y-ultScale); // fix vertical position
}

EDITED: @bacca87 posted a C# version below, and also included a fix to avoid run speed while in the air - feature that I’ve added to this answer.

EDITED 2: The script above works, but has the bad collateral effect of modifying the vertical scale of any character child - like the weapon, as @Fishman92 noticed! To avoid this, the version below modify the character height instead of its vertical scale:

var walkSpeed: float = 7; // regular speed
var crchSpeed: float = 3; // crouching speed
var runSpeed: float = 20; // run speed

private var chMotor: CharacterMotor;
private var ch: CharacterController;
private var tr: Transform;
private var height: float; // initial height

function Start(){
    chMotor = GetComponent(CharacterMotor);
    tr = transform;
    ch = GetComponent(CharacterController);
	height = ch.height;
}

function Update(){

    var h = height;
    var speed = walkSpeed;
    
    if (ch.isGrounded && Input.GetKey("left shift") || Input.GetKey("right shift")){
        speed = runSpeed;
    }
    if (Input.GetKey("c")){ // press C to crouch
        h = 0.5 * height;
        speed = crchSpeed; // slow down when crouching
    }
    chMotor.movement.maxForwardSpeed = speed; // set max speed
    var lastHeight = ch.height; // crouch/stand up smoothly 
    ch.height = Mathf.Lerp(ch.height, h, 5*Time.deltaTime);
    tr.position.y += (ch.height-lastHeight)/2; // fix vertical position
}

NOTE: The white capsule childed to the FPC (“Graphics”) isn’t modified by this script, thus it will sink a little in the terrain when crouching. A similar problem will happen when the player crouches under a low ceiling: the top of the capsule will penetrate the ceiling. This makes no difference, since “Graphics” doesn’t have a collider.

This is the C# version of the script with a little fix that prevent the character to run while jumping.


using UnityEngine;
using System.Collections;

public class RunAndCrouch : MonoBehaviour 
{
	public float walkSpeed = 7; // regular speed
	public float crchSpeed = 3; // crouching speed
	public float runSpeed = 20; // run speed
	
	private CharacterMotor chMotor;
	private Transform tr;
	private float dist; // distance to ground
	
	// Use this for initialization
	void Start () 
	{
		chMotor =  GetComponent<CharacterMotor>();
	    tr = transform;
	    CharacterController ch = GetComponent<CharacterController>();
	    dist = ch.height/2; // calculate distance to ground
	}
	
	// Update is called once per frame
	void FixedUpdate ()
	{
		float vScale = 1.0f;
	    float speed = walkSpeed;
	    
	    if ((Input.GetKey("left shift") || Input.GetKey("right shift")) && chMotor.grounded)
	    {
	        speed = runSpeed;			
	    }
		
	    if (Input.GetKey("c"))
	    { // press C to crouch
	        vScale = 0.5f;
	        speed = crchSpeed; // slow down when crouching
	    }
		
	    chMotor.movement.maxForwardSpeed = speed; // set max speed
	    float ultScale = tr.localScale.y; // crouch/stand up smoothly 
		
		Vector3 tmpScale = tr.localScale;
		Vector3 tmpPosition = tr.position;
		
		tmpScale.y = Mathf.Lerp(tr.localScale.y, vScale, 5 * Time.deltaTime);
		tr.localScale = tmpScale;
	    
		tmpPosition.y += dist * (tr.localScale.y - ultScale); // fix vertical position		
		tr.position = tmpPosition;
	}
}