[Unity3D] CharacterController manteniendo inercia

Nucklear

Buenas, estoy haciendo unas pruebas para un control de personaje que use únicamente el Character Controller y donde implemente yo sus físicas. Ahora mismo estoy implementando el salto y tengo un problema que seguro que es una tontearía pero no doy encontrado la solucion.

Es un juego VR y estoy usando el XR Interaction Toolkit de Unity asi que cuando se llama a la accion Jump hago lo siguiente:

    private void OnJump(InputAction.CallbackContext obj)
    {
        //Find a way to keep momentum
        
if (!character.isGrounded) return; horizontalVelocity = character.velocity; _directionY = 1.0f * jumpSpeed; _directionX = horizontalVelocity.x; _directionZ = horizontalVelocity.z; isJumping = true; }

Y luego en el FixedUpdate añado el X y Z del vector velocity almacenado a la dirección del movimiento:

    void FixedUpdate()
    {
        
//Update collider position to player CapsuleFollowPlayer(); if (character.isGrounded) { _continuousMovement.enabled = true; _directionX = 0; _directionZ = 0; isJumping = false; } else { _continuousMovement.enabled = false; if (isJumping) { _direction.x = _directionX; _direction.z = _directionZ; } } _directionY -= gravity * Time.deltaTime; _direction.y = _directionY; character.Move(_direction * Time.deltaTime); }

El problema que tengo el que character.isGrounded devuelve true en el mismo frame que cargo la inercia y la setea a 0. Seguro que hay una forma mucho mejor de hacer esto de lo que estoy intentando y alguien con mas experiencia me puede arrojar luz.

Otra cosa que pense es anadir un CoyoteTime y saltarme un par de frames antes, pero no me convence la idea.

totespare

El problema es lo que dices, que cuando haces el jump, durante un tiempo todavía estás isGrounded, así que como el FixedUpdate te lo pisa (recuerda que se ejecuta varias veces por frame), tu fixedupdate piensa que sigues grounded y te machaca los valores. Prueba a cambiar el FixedUpdate por un Update (o al menos la lógica respectiva al .isGrounded) y mira a ver si te sigue pasando (yo creo que en un frame al character controller le ha dado tiempo a cambiar el isGrounded, pero no te sabría decir 100%).

Por cierto estando en el fixed update no necesitas multiplicar el direction o la gravedad por el deltaTime, pero si pruebas a cambiarlo por Update, déjalos ahí xd. Y otra cosa, veo ahí un CapsuleFollowPlayer. No usas el collider del character controller?

1 respuesta
Nucklear

#2 Buenas, he probado varias soluciones, primero lo que dices, cambiar el FixedUpdate por el Update. También he probado a usar el propio GroundCheck que viene con el Character Controller y implementar mi propia función para usarla independientemente dentro del Update pero sigue haciendo lo mismo.

La secuencia es siempre la misma:

- Salto
- isGrounded = True
- direction X y Z = 0
- isGrounded = False
[...]

Estoy seguro de que esto es un problema muy común pero ni encuentro info online ni se me ocurre como hacerlo decentemente sin meterme a hacer guarradas.

Lo de CapsuleFollowPlayer es solo para actualizar el collider a la altura y posición del headset por si el jugador en lugar de moverse con el mando se pone a caminar por la habitación que no pueda meterse dentro de las paredes.

1 respuesta
Hukha

#3 Hello!
Así a simple vista parece que no estás esperando al final del frame nunca...
Prueba con LateUpdate o WaitForEndOfFrame, isGrounded debería ya estar seteado correctamente y puedes hacer tus cálculos ahí

1 respuesta
Nucklear

#4 Ahí le has dado, esa es la clave, he movido la lógica de resetear los valores a 0 al LateUpdate y ahora funciona como debería.

void LateUpdate()
    {
        if (character.isGrounded)
        {
            _continuousMovement.enabled = true;
            _direction.x = 0f;
            _direction.z = 0f;
            isJumping = false;
        }
        else
        {
            _continuousMovement.enabled = false;
            if (isJumping)
            {

            _direction.x = -_directionX;
            _direction.z = _directionZ;

        }
    }
}

Muchas gracias, a ver si lo pongo bonito y lo dejo por aquí por si a alguien mas le pasa.

1
Quake_

Yo creo que el problema también está en cómo gestiona el Input System sus eventos, que por defecto no están en el mismo ciclo de Updates de MonoBehaviour. Digo esto porque es probable que en el frame donde ejecutas el salto, el método OnJump() se esté ejecutando siempre antes del check de isGrounded, por lo que irremediablemente al final del frame, isJumping será false, y las componentes x,z de _direction serán 0. Para solucionarlo, el check de isGrounded ha de ir antes del evento de salto.

En Project Settings > Input System deberias > Update Mode, puedes configurar en qué momento del frame quieres que se llame a los eventos del Input System.
Puedes cambiarlo al modo manual y después de hacer el check de isGrounded, llamar a los eventos con InputSystem.Update()

1 respuesta
Nucklear

#6 También es correcto, y esta solución me parece mas elegante porque así no tengo que separar las cosas. Muchas gracias

Usuarios habituales

  • Nucklear
  • Quake_
  • Hukha
  • totespare