Move while jumping. Fun vs Realism or is there a "best of both worlds" solution?

Hello!

I understand the question I’ve posted is probably pretty confusing so I’ll try to clear it up.

I have two functions, one of them (MovePlayerNoAirMove()) allows the player to jump, let go of all controls and maintain their trajectory until they land. The draw back is that they can not change direction while in the air. This is very realistic, but not too much fun. It feels like I’m punishing the player for jumping.

The other function (MovePlayerWithAirMove()) allows a player to change direction while in the air, however if you let go of all controls while in the air, your velocity immediately drops to zero and you fall to the ground. It looks like the character is hitting an invisible wall.

The only thing wrong with the second function (MovePlayerWithAirMove()) is the strange screeching halt in mid air effect. So when you’re running straight, hit jump then let go of the analog stick, you would expect to stay on the same trajectory. Instead, the player just stops and falls. It’s really odd looking.

I’m hoping for a “best of both worlds” solution. I’m thinking that I need to use different methods of applying gravity and moving the controller based on whether or not isGrounded is true. Right now I have two states, either you’re on the ground or you’re in the air. Do I need four states? Grounded moving/standing, Air moving/maintaining trajectory.

Any ideas?

Thanks!

(Here’s the code for both functions.)

	// -- Directional movement (Can not move while in the air -- realistic) ---------------
	private void MovePlayerNoAirMove() {
		// Cache a Character Controller reference only if one does not exist
		if (!controller)
			controller = GetComponent<CharacterController>();

		// Movement when character is on the ground
        if (controller.isGrounded) {
            moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
            moveDirection = transform.TransformDirection(moveDirection);
            moveDirection *= speed;
			
			// Jump!
            if (Input.GetKey(KeyCode.Space) || Input.GetButton("Jump"))
                moveDirection.y = jumpSpeed;
        }
		
		// Apply gravity and keep moving the character in the direction he was going
		moveDirection.y -= gravity * Time.deltaTime;
        controller.Move(moveDirection * Time.deltaTime);

	}

	
	// -- Directional movement (Player can move while in the air after jumping -- fun) ------
	private void MovePlayerWithAirMove() {
		// Cache a Character Controller reference only if one does not exist
		if (!controller)
			controller = GetComponent<CharacterController>();
		
		// Setup move directions
		moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
		moveDirection = transform.TransformDirection(moveDirection);
		moveDirection *= speed;
	
		// Jump!
    	if (controller.isGrounded) {
        	jumpSpeedAirMove = 0; 				// a grounded character has zero vertical speed unless...
        	if (Input.GetButton ("Jump")) { 	// ...Jump is pressed!
            	jumpSpeedAirMove = jumpSpeed; 
        	}
    	}

    	// Apply gravity and move the player controller
    	jumpSpeedAirMove -= gravity * Time.deltaTime;
   	moveDirection.y = jumpSpeedAirMove;
    	controller.Move(moveDirection * Time.deltaTime);
	}

In your second function, you should get the speed decay, and not being set as bound only to input.
I would add a var like previousSpeed.

something like (untested)

newSpeed = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
newSpeed = transform.TransformDirection(newSpeed);
newSpeed *= playerSpeedFactor;

if (!controller.isGrounded) {
    previousSpeed.X *= decaySpeed;
    previousSpeed.Z *= decaySpeed;
    previousSpeed.Y -= gravity;
}
else{// Jump!
    // a grounded character has zero vertical speed unless...
    if (Input.GetButton ("Jump")) {    // ...Jump is pressed!
        newSpeed.Y = jumpSpeed;
    }
}

moveDirection = newSpeed+previousSpeed; //Do some magic mixing between both speed
moveDirection = Clamp(moveDirection) //This function doesn't exist, up to you to avoid the movement to get crazy
previousSpeed = moveDirection;
controller.Move(moveDirection * Time.deltaTime);

It’s far far away from being good. Just merely pointing the fact that you need to maintain the player velocity variable.