Keeping speed while crouching in midair and remove clipping while returning to standing

I’ve been working on making a good crouching script now for a while but no matter how I try I can’t put the last pieces together.

One thing I want is that if you jump while running (standing) and then start to crouch in midair you should not slow down except for when landing on the ground. I’ve been trying some IsGrounded-functions but when I do the script just ignore the slowdown altogether and makes the player move around with normal standingspeed while crouching.

The other (and most important - I even dare to say gamebreaking) missing component is the fact that the player should not be able to stand up if standing up will result in the player clipping through objects. At the moment I can stand up straight from crouching anywhere which isn’t how it normally works in games. I’ve tried to create a collider with the same size as the First Person Controller collider and used some collider scripts to make the game realize that while anything is touching this collider the player should not be able to stand.

The code so far looks like this (it’s a modified version of a crouching script I found here a while ago):

var forWalkSpeed: float = 7; // regular speed forward
var sideWalkSpeed: float = 7; // regular speed sidestep
var backWalkSpeed: float = 7; // regular speed backward
var forCrchSpeed: float = 3; // crouching speed forward
var sideCrchSpeed: float = 3; // crouching speed sidestep
var backCrchSpeed: float = 3; // crouching speed backward

internal var forSpeed: float;            // the current speed state
internal var sideSpeed: float; 
internal var backSpeed: float; 

internal var crouchHeight : float;     // variables for character height while crouching
internal var crouchingHeight : float;  
internal var standingHeight: float;    // height of character while standing
internal var crouching = false;        // boolean is crouching?
//internal var running = false;          // boolean is running?

internal var characterMotor: CharacterMotor;  //direct variable to the characterMotor
internal var mainCamera: GameObject;          // direct variable to the mainCamera

//var distToGround: float;

function Start()
{
    //initialize
    characterMotor = GetComponent(CharacterMotor);
    mainCamera = gameObject.FindWithTag ("MainCamera");
    standingHeight = mainCamera.transform.localPosition.y;
    crouchingHeight = (standingHeight - 0.9);
    crouchHeight = (standingHeight - crouchingHeight);
    distToGround = collider.bounds.extents.y; // get the distance to ground
}

function Update()
{
    //simple machine for setting the current speed of the character
    if(crouching)
    {
        forSpeed = forCrchSpeed; // slow down when crouching
        sideSpeed = sideCrchSpeed;
        backSpeed = backCrchSpeed;
    }
    //else if( running )
    //{
    //   speed = runSpeed;
    //}
    else
    {
        forSpeed = forWalkSpeed;
        backSpeed = backWalkSpeed;
        sideSpeed = sideWalkSpeed;
    }
    /*
    //If the sprint button is clicked, and not crouching .. SPRINT!

    if (Input.GetKey("left ctrl") || Input.GetKey("right ctrl")){
        if(!crouching){
            running = true;
            //cameraZoom.sprint = true;  //sets sprint true so the camera knows to pan out
        }
    } else {
        //cameraZoom.sprint = false;
        running = false;
    } */

    // if crouch button has been pressed, put FPS into crouch
    if (Input.GetButtonDown ("Crouch")){
        if(crouching){
            stopCrouching();
            return;
        }
        if(!crouching)
             crouch();
    }
    // crouch with smoothing
    if(crouching)
    {
        if(mainCamera.transform.localPosition.y > crouchingHeight){
            if(mainCamera.transform.localPosition.y - (crouchHeight*Time.deltaTime*4) < crouchingHeight){
                mainCamera.transform.localPosition.y = crouchingHeight;
            }
            else 
            {
                mainCamera.transform.localPosition.y -= crouchHeight*Time.deltaTime*4;
            }
        }
    }
    else 
    {
        if(mainCamera.transform.localPosition.y < standingHeight){
            if(mainCamera.transform.localPosition.y + (crouchHeight*Time.deltaTime*4) > standingHeight){
                mainCamera.transform.localPosition.y = standingHeight;
            }
            else
            {
                mainCamera.transform.localPosition.y += crouchHeight*Time.deltaTime*4;
            }
        }
    }
    //set the max speed in the char motor;
    characterMotor.movement.maxForwardSpeed = forSpeed; // set max forward speed
    characterMotor.movement.maxSidewaysSpeed = sideSpeed; // max sideway speed
    characterMotor.movement.maxBackwardsSpeed = backSpeed; //max backwards speed
}

// sets the size of the collider to crouch size

function crouch() 
{
    this.GetComponent(CharacterController).height -= crouchHeight;
    this.GetComponent(CharacterController).center -= Vector3(0,crouchHeight/2, 0);
    crouching = true;
}

// resets the size of the collider to standing size  
function stopCrouching()
{
    crouching = false;
    this.GetComponent(CharacterController).height += crouchHeight;
    this.GetComponent(CharacterController).center += Vector3(0,crouchHeight/2, 0);
}  

/* function IsGrounded(): boolean 
{
  return Physics.Raycast(transform.position, -Vector3.up, distToGround + 0.1);
}*/

I would do the following changes:

1- cache the CharacterController at Start to save time:

...
internal var character: CharacterController;

function Start(){
  character = GetComponent(CharacterController);
  ...

You can use it to know when the character is grounded with character.isGrounded

2- Use characterMotor.jumping.jumping to know when the character is jumping, and block the crouch speed:

    ...
    if(crouching && !characterMotor.jumping.jumping)
    {
        forSpeed = forCrchSpeed; // slow down when crouching
        sideSpeed = sideCrchSpeed;
        backSpeed = backCrchSpeed;
    }
    ...

You may also try to use character.isGrounded instead:

    if (crouching && character.isGrounded){

3- Before standing up, do a raycast upwards to check if there’s enough room (set the float distUp variable to a suitable value):

    ...
    if (Input.GetButtonDown ("Crouch")){
        if(crouching && !Physics.RaycastHit(transform.position, Vector3.up, distUp)){
            stopCrouching();
            return;
        }
        if(!crouching)
            ...

I hope this can solve your problems - or at least help you to find the solutions.

Thanks alot! The speed is now the same even while I crouch midair. I also disabled the ability to jump while crouched since it's not appropriate behaviour to do so in the kind of slowpaced game I'm making.

The raycast worked after a light tweak I had to make since Unity gave me an error. Now it looks like this:

function Update(){

var hit : RaycastHit; ...

// if crouch button has been pressed, put FPS into crouch
if (Input.GetButtonDown ("Crouch")){
    if(crouching && !Physics.Raycast(transform.position, Vector3.up, hit, distUp)){
        stopCrouching();
        return;
    }
    if(!crouching)
         crouch();
}

The only issue I have is that if I don't stand directly below the object the rays won't hit. Is there any possibility to shoot out more rays and in different directions so that my whole character can enjoy this feeling of not being able to stand straight?