En este post aprenderemos un poco más acerca del salto de nuestro personaje.
Empezaremos hablando del control de nuestro personaje cuando está en el aire. Ahora mismo el movimiento de nuestro personaje cuando está en el aire es igual que cuando está en el suelo. Lo que haremos será que el personaje reduzca su capacidad de movimiento cuando no esté en el suelo. Para ello modificaremos la siguiente línea de código:
currentMovement = Vector3.SmoothDamp(currentMovement, targetHorizontalMovement * moveSpeed, ref currentMovementV, moveSpeedSmooth);
Sustituiremos la variable moveSpeedSmooth, que indicará el tiempo que el personaje tarda en alcanzar su velocidad máxima de movimiento, por otra cuyo valor dependerá de si el personaje está o no en el suelo. Esta variable de selección será privada de tipo float y la llamaremos moveSmoothUse ( float moveSmoothUse;
). Le asignaremos el valor de moveSpeedSmooth dentro de Start() para cuando se inicialice del siguiente modo: moveSmoothUse = moveSpeedSmooth;
. Crearemos una variable pública de tipo float a la que llamaremos airControlSmooth y a la que le asignaremos un valor de 0.8 ( public float airControlSmooth = 0.8f;
). Esta variable es la que seleccionaremos en caso de que nuestro personaje esté en el aire. Ahora sustituímos moveSpeedSmooth por moveSmoothUse:
currentMovement = Vector3.SmoothDamp(currentMovement, targetHorizontalMovement * moveSpeed, ref currentMovementV, moveSmoothUse);
A continuación iremos a la parte donde detectamos si el personaje está o no en el suelo. Si está tocando el suelo igualaremos moveSmoothUse con moveSpeedSmooth y si no lo está la igualaremos a airControlSmooth.
if (!controller.isGrounded)
{
moveSmoothUse = airControlSmooth;
verticalSpeed -= gravity * Time.deltaTime;
}
else
{
moveSmoothUse = moveSpeedSmooth;
verticalSpeed = 0;
}
El siguiente punto a tratar será introducir un pequeño margen de tiempo para poder saltar, de modo que el personaje pueda saltar aunque ya no esté tocando el suelo, siempre que se encuentre dentro de ese margen temporal. Con esta finalizad crearemos una variable pública tipo float a la que llamaremos jumpAllowTime que igualaremos a 0.1 ( public float jumpAllowTime = 0.1f;
). Esta variable indicará el margen de tiempo en segundos para saltar si previamente se estaba tocando el suelo, aunque ahora esté en el aire. Ahora crearemos un contador para saber si el personaje se encuentra dentro de ese intervalo. Será una variable privada de tipo float a la que llamaremos jumpAllowTimeTrack ( float jumpAllowTimeTrack;
) y la inicializaremos dentro de Start() igualándola a jumpAllowTime ( jumpAllowTimeTrack = jumpAllowTime;
). Cuando el personaje esté en el suelo jumpAllowTimeTrack tomará el valor de jumpAllowTime, mientras que si está en el aire decrementaremos el valor de jumpAllowTimeTrack con respecto al tiempo:
if (!controller.isGrounded)
{
moveSmoothUse = airControlSmooth;
verticalSpeed -= gravity * Time.deltaTime;
jumpAllowTimeTrack -= Time.deltaTime;
}
else
{
moveSmoothUse = moveSpeedSmooth;
verticalSpeed = 0;
jumpAllowTimeTrack = jumpAllowTime;
}
Ahora modificaremos las condiciones de salto. Cambiaremos la comprobación de si el personaje está en el suelo por una comprobación de que el contador aún no ha llegado a 0.
if (jumpAllowTimeTrack >= 0 && Input.GetButtonDown ("Jump"))
verticalSpeed = jumpSpeed;
El siguiente tema a tratar es que a veces al pulsar la barra espaciadora para saltar el personaje no realizará dicha acción. Esto se debe a que puede darse el caso de que pulsemos justo entre un frame y otro y el juego no recogerá la orden. A simple vista parece que ya está solucionado (en mi caso al menos), pero explicaremos como solventar este problema. Básicamente tendremos que hacer lo mismo que en el apartado anterior pero a la inversa. Crear dos variables waitToLand y waitToLandTrack:
public float waitToLand = 0.1f;
float waitToLandTrack;
Ahora será hacer el mismo proceso pero cambiado de posición respecto a la comprobación de suelo. Además tendremos que igualar verticalSpeed a 0 sólo si waitToLandTrack es menor o igual a 0.
if (!controller.isGrounded)
{
moveSmoothUse = airControlSmooth;
verticalSpeed -= gravity * Time.deltaTime;
jumpAllowTimeTrack -= Time.deltaTime;
waitToLandTrack = waitToLand;
}
else
{
moveSmoothUse = moveSpeedSmooth;
jumpAllowTimeTrack = jumpAllowTime;
waitToLandTrack -= Time.deltaTime
}
if(waitToLandTrack <= 0)
verticalSpeed = 0;
A partir de ahora el personaje saltará siempre que pulsemos la barra espaciadora.
Recordad activar el script de la cámara para que vuelva a seguir a nuestro personaje y ajustad los valores de las variables a vuestro gusto para conseguir la fluidez según vuestras preferencias.
El código del script PlayerScript.cs quedará así:
using UnityEngine;
using System.Collections;
public class PlayerScript : MonoBehaviour {
public float moveSpeed = 5;
public float rotateSpeed = 180;
public float jumpSpeed = 20;
public float jumpAllowTime = 0.1f;
float jumpAllowTimeTrack;
public float waitToLand = 0.1f;
float waitToLandTrack;
public float gravity = 9.8f;
public float moveSpeedSmooth = 0.3f;
public float airControlSmooth = 0.8f;
public float rotateSpeedSmooth = 0.3f;
float moveSmoothUse;
float currentForwardSpeed;
float forwardSpeedV;
float targetRotation;
float currentRotation;
float rotationV;
CharacterController controller;
Vector3 currentMovement;
Vector3 currentMovementV;
Transform cameraTransform;
float verticalSpeed;
void Start () {
jumpAllowTimeTrack = jumpAllowTime;
waitToLandTrack = waitToLand;
moveSmoothUse = moveSpeedSmooth;
controller = GetComponent<CharacterController> ();
cameraTransform = Camera.main.transform;
}
void Update() {
/*
targetRotation += Input.GetAxisRaw ("Horizontal") * rotateSpeed * Time.deltaTime;
if (targetRotation > 360)
targetRotation -= 360;
if (targetRotation < 0)
targetRotation += 360;
currentRotation = Mathf.SmoothDampAngle (currentRotation, targetRotation, ref rotationV, rotateSpeedSmooth);
transform.eulerAngles = new Vector3 (0, currentRotation, 0);
currentForwardSpeed = Mathf.SmoothDamp (currentForwardSpeed, Input.GetAxisRaw ("Vertical") * moveSpeed, ref forwardSpeedV, moveSpeedSmooth);
currentMovement = new Vector3 (0, currentMovement.y, currentForwardSpeed);
currentMovement = transform.rotation * currentMovement;
*/
Vector3 horizontalInput = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
if (horizontalInput.magnitude > 1)
horizontalInput.Normalize();
Vector3 targetHorizontalMovement = horizontalInput;
targetHorizontalMovement = cameraTransform.TransformDirection(targetHorizontalMovement);
//targetHorizontalMovement = cameraTransform.rotation * targetHorizontalMovement;
targetHorizontalMovement.y = 0;
targetHorizontalMovement.Normalize();
targetHorizontalMovement *= horizontalInput.magnitude;
//currentMovement = targetHorizontalMovement * moveSpeed;
currentMovement = Vector3.SmoothDamp(currentMovement, targetHorizontalMovement * moveSpeed, ref currentMovementV, moveSmoothUse);
//Quaternion targetRotationQ = Quaternion.LookRotation(Vector3.forward);
if (new Vector3(currentMovement.x, 0, currentMovement.z).magnitude > 1)
{
targetRotation = Mathf.Atan2(currentMovement.x, currentMovement.z) * Mathf.Rad2Deg;
//transform.rotation = Quaternion.Lerp(transform.rotation, targetRotationQ, rotateSpeed * Time.deltaTime);
transform.rotation = Quaternion.Euler(0, Mathf.SmoothDampAngle(transform.rotation.eulerAngles.y, targetRotation, ref rotationV, rotateSpeedSmooth), 0);
}
if (!controller.isGrounded)
{
moveSmoothUse = airControlSmooth;
verticalSpeed -= gravity * Time.deltaTime;
jumpAllowTimeTrack -= Time.deltaTime;
waitToLandTrack = waitToLand;
}
else
{
moveSmoothUse = moveSpeedSmooth;
jumpAllowTimeTrack = jumpAllowTime;
waitToLandTrack -= Time.deltaTime;
}
if(waitToLandTrack <= 0)
verticalSpeed = 0;
if (jumpAllowTimeTrack >= 0 && Input.GetButtonDown ("Jump"))
verticalSpeed = jumpSpeed;
currentMovement.y = verticalSpeed;
controller.Move (currentMovement * Time.deltaTime);
}
}