Crawl, Walk and Sprint movement speeds

This should be straightforward, or so I thought. Getting quite late so probably just tiredness dumbing me down. I could use a fresh set of eyes on this.

In our game the player character needs to be able to move at different speeds depending on the keys held down. By default, the PC will move at the walkSpeed, and with "c" held down will slow to a crawl, while with "left shift" held down will accelerate to his top sprinting speed. I'd also like to include stamina, so that after a few seconds at top speed, the PC slows back to walking speed and must wait for a cool down period before being able to sprint again.

Here's my script so far. It's a modified version of the FPSWalker script For some reason, holding shift only increases the speed by 1, and not continuously until the limit as i'd like, despite using Input.GetButton to do it as long as the key is held down. Changing speeds worked with hardcoded values, but we'd like to have speeds in multiples like this so it's easier to scale. Also the sprint stamina timer doesn't seem to be having an effect.

var currentSpeed : float;
var walkSpeed : float = 6.0;
var maxSpeed : float = 15.0;

var sprintStamina : float = 8.0;
var sprintTimer : float = 0.0;
var isCoolingDown : boolean = false;

var rotateSpeed : float = 100.0;

var jumpSpeed : float = 8.0;

// artificial gravity since we are not using a rigidbody
var gravity = 20.0;

// variable to store the player's current forward direction
private var moveDirection =;

// boolean variable to track whether the player is on the ground
private var grounded : boolean = false;
static var allowMove : boolean = true;
static var playerSneaking : boolean = false;

function Update(){

    if(!Input.GetButton("Crouch") && !Input.GetButton("Run")){
        currentSpeed = walkSpeed;

    if (Input.GetButton("Crouch")) {
        // load the game
        currentSpeed = walkSpeed * 0.5;
        playerSneaking = true;
    //  print("SNEAKING at "+walkSpeed);

    if (Input.GetButton("Run")){
            if (currentSpeed <= maxSpeed){
                //  print("running");
            } else {

// fixed update function is called every fixed framerate frame
// so is more accurate for physics stuff
function FixedUpdate() {

    if (grounded) {
        // We are grounded, so store a new heading vector based on up/down key inputs
        moveDirection = new Vector3(0, 0, Input.GetAxis("Vertical"));

        // convert local xyz co-ordinates above to world co-ordinates
        moveDirection = transform.TransformDirection(moveDirection);

        // multiply by speed value (editable in the editor)
        moveDirection *= currentSpeed;

        // rotate around the y axis at the rotation speed per second
        transform.Rotate(0,Input.GetAxis("Horizontal") * rotateSpeed * Time.deltaTime, 0);

        // If the default jump button is pressed (spacebar)
        // set the y property of the players heading to the jumpSpeed value
        if (Input.GetButton ("Jump")) {
            moveDirection.y = jumpSpeed;

    // Subtract the value of gravity once a second to bring the player back down
    moveDirection.y -= gravity * Time.deltaTime;

    // Move the controller
    // store a reference to the CharacterController component of this object in a variable called 'controller'
    var controller : CharacterController = GetComponent(CharacterController);

    // move the controller along the heading specified by moveDirection and in metres per second (store this movement in a variable called 'flags')
    var flags = controller.Move(moveDirection * Time.deltaTime);

    // set the grounded variable to the result returned by the comparison between
    // the collisions in the variable flags and the internal CollidedBelow collision flag 
    // of the character controller. If there is a collision, the value returned will be
    // 1 or 'not zero' as is set here when we are colliding.
    grounded = (flags & CollisionFlags.CollidedBelow) != 0;


function sprintControl(){
        sprintTimer += Time.deltaTime;
        if(sprintTimer > sprintStamina){
            currentSpeed = walkSpeed;
            isCoolingDown = true;
            yield WaitForSeconds(3.0);
            isCoolingDown = false;
            sprintTimer = 0.0;

// this line makes sure that a character controller is always assigned to the object this script is attached to.
@script RequireComponent(CharacterController)

The problem with the above code is that you're setting currentSpeed at the top of Update, so each time you increase it, next time it's walk speed again

I would set it to that only if crouch and run is not held - also, you should probably set the crouch speed to walkSpeed *0.5 instead of currentSpeed *0.5, otherwise you may get odd speeds when runcrouching