Un salto preciso en Unity empieza por entender el motor de físicas y la entrada del teclado. Aquí verás cómo pasar de un salto descontrolado a un comportamiento sólido usando raycast, capas y entradas discretas. Todo está enfocado a corregir el problema típico de “salta sin parar” y a que controles la lógica con confianza.
¿Por qué el personaje salta indefinidamente en Unity?
Cuando mantienes pulsada la tecla de salto dentro de update, el código se ejecuta en cada frame. Si el juego corre a 60 frames por segundo, estás aplicando fuerza vertical 60 veces por segundo. Resultado: el astronauta acumula fuerza y tarda mucho en bajar. La clave es simple: solo se debe saltar si estás cerca del suelo.
¿Qué hace update a 60 fps?
Llama tu lógica 60 veces por segundo si el juego va a 60 fps.
Repite la aplicación de fuerza si usas GetKey.
Genera saltos enormes por acumulación de impulsos.
¿Por qué GetKey causa múltiples saltos?
GetKey mantiene la acción mientras la tecla está pulsada.
Se comporta como un arma automática: cada frame suma impulso.
Necesitas una entrada discreta: un solo salto por pulsación.
¿Cómo detectar el suelo con raycast y layer mask?
Para que el salto ocurra solo cerca del suelo, usa un rayo invisible con Physics2D.Raycast y limita la detección a una Layer Mask de “suelo”. Así el personaje “pregunta” al motor de físicas si hay suelo bajo sus pies a una distancia máxima.
¿Cómo crear la capa ground en Unity?
Crea una capa nueva: Ground.
Asigna esa capa a todo lo que sea suelo: floor, plataformas pequeña/mediana/grande y rocas.
En el Player, expón una variable pública tipo LayerMask y asígnale la capa Ground desde el Inspector.
Beneficio: filtras colisiones solo contra el suelo, evitando falsos positivos.
// En tu script del jugadorpublicLayerMask groundMask;// capa de suelo asignada en el Inspector
¿Cómo programar el método isTouchingTheGround?
La función debe responder con un booleano: “¿estoy tocando el suelo?”. Usamos un rayo desde la posición del personaje hacia abajo, hasta una distancia razonable. Si el rayo choca con algo en la groundMask, devolvemos true.
// Nos indica si el personaje está o no tocando el suelo.boolIsTouchingTheGround(){// Distancia de detección ajustable: 1.5 m para personajes grandes.float checkDistance =1.5f;var hit = Physics2D.Raycast(transform.position, Vector2.down, checkDistance, groundMask);return hit.collider !=null;// true si hay suelo a esa distancia.}
Puntos clave:
Distancia muy corta (0.2f) puede fallar si el personaje es grande.
Prueba 1.0f, 1.5f o 2.0f y verifica la consistencia del salto.
Documenta tu código con comentarios y usa // TODO para pendientes.
¿Qué ajustes clave estabilizan el salto?
Además del raycast, evita la repetición de la fuerza con una entrada discreta. Cambia GetKey por GetKeyDown para detectar solo la bajada de la tecla.
¿Qué cambiar a GetKeyDown evita el salto continuo?
Sustituye la lectura continua por una pulsación única.
Combínalo con la comprobación del suelo para emitir un único impulso.
voidJump(){if(Input.GetKeyDown(KeyCode.Space)&&IsTouchingTheGround()){// Aplica un impulso vertical una sola vez. rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);}}
Buenas prácticas:
Guarda el script antes de probar. Si no, no verás cambios.
Mantén el código indentado y legible. Mejora la depuración.
¿Qué parámetros ajustar: fuerza y masa?
Ajusta jumpForce y la masa del Rigidbody2D para modular altura y respuesta.
Recuerda la segunda ley de Newton: fuerza = masa por aceleración.
Si el salto es excesivo al mantener espacio, revisa: distancia del raycast y uso de GetKeyDown.
Más adelante, visualiza el rayo con Gizmos para afinar la distancia con precisión.
Ideas clave que consolidar:
Raycast hacia abajo con Physics2D limita el salto al estar cerca del suelo.
Layer Mask “Ground” organiza qué colisiona como suelo.
Entrada discreta con GetKeyDown elimina impulsos repetidos.
Documentación con // y TODO acelera el aprendizaje y la corrección.
Calibración: distancia del rayo, jumpForce y masa determinan el resultado final.
¿Te funcionó este enfoque o te encuentras con un caso especial de colisiones o tamaños? Cuéntalo en comentarios y vemos cómo afinar tu configuración.
una vez que den play, pasen a la vista de editor, veran una linea verde desde el centro del personaje
Buen aporte.
Otro truco para saber en tiempo real la distancia del rayo y acertar y probar diferentes distancias es Serializar una variable y luego podremos cambiar el valor desde el inspector hasta que demos con uno que nos guste y se adapte al jugador
[SerializeField] float rayTracing = 1.0f;
.
en Update()
Debug.DrawRay(this.transform.position, Vector2.down * rayTracing, Color.green);
.
en bool IsTouchingTheGround()
if (Physics2D.Raycast(this.transform.position, Vector2.down, rayTracing, groundMask))
.
y así de paso evitamos de paso crear números fantasma
Resumen de la clase. Physics2D el método RayCast tiene los siguientes parámetros:
Origen : el punto de donde se origina el rayo 2d (ejemplo this.transform.position)
Dirección: Un vector que representa la dirección
Distancia : la distancia máxima en la que se lanza el rayo
LayerMask: es un filtro que detectan los colliders en ciertas capas
Notas de clase;
public LayerMask groundMask: es para saber quien es el suelo.
**LayerMask **es la capa que se encargara de tener un registro de quien forma parte del suelo, quien es toqueteable a nivel suelo.
**transform **es para indicar posicion, rotacion y escala.
**Physics **es para invocar el motor de fisicas
Raycast: trazar un rayo invisible
Alguien podria ayudarme por favor, coloque el codigo como esta explicado pero nunca salta. Es como si nunca tocara el suelo, no importa cuanto mueva la 20f nunca me arroja un true.
if (Physics2D.Raycast(this.transform.position,
Vector2.down,
20f,
groundMask))
{
//programar lógica de contacto con el suelo
return true;
}
else
{
//programar lógica de no contacto
return false;
}
Ya encontre mi error, Olvide colocar la capa ground al player por eso no saltaba.
Un corto análisis del tema de GetKey y GetKeyDown:
Al usar GetKey sí se nos va “volando” el personaje si se mantiene presionado Space; una solución inicial aparente es usar GetKeyDown (sin hacer aún lo del Layer) no obstante,
Si se usa GetKeydown sin usar la solución del Layer, se está la complicación de que al saltar, si estando en el aire a cualquier altura, se sigue presionando Space o el mouse, nuestro personaje sigue saltando; por eso se requiere combinar GetKeyDown y la técnica del Layer con Raycast
porque usamos el raycast para detectar el piso envés de un
onCollisionEnter2d como en el videojuego del curso anterior ?
Hola, supongo que para aprender una nueva herramienta, pero creo que seria más eficiente solo usar un if a la hora de saltar que mire la posición en y del personaje, porque usar mucho las físicas puede hacer más lento el juego
En lugar de iterar la altura del personaje, alguien sabe como medirlo?
Mi personaje no salta e hice todo al pie de la letra, que es lo que está mal?
using System.Collections;using System.Collections.Generic;using UnityEngine;publicclassPlayerController:MonoBehaviour{//Variables del movimiento del personaje[SerializeField]public float jumpForce = 6f;Rigidbody2D playerRigidBody;publicLayerMask groundMask;voidAwake(){ playerRigidBody =GetComponent<Rigidbody2D>();}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){if(Input.GetKeyDown(KeyCode.Space)||Input.GetMouseButtonDown(0)){Jump();}}voidJump(){if(IsTouchingTheGround()){ playerRigidBody.AddForce(Vector2.up*jumpForce,ForceMode2D.Impulse);}}//Nos idnica si el personaje esta o no tocando el suelo bool IsTouchingTheGround(){if(Physics2D.Raycast(this.transform.position,Vector2.down,1.5f, groundMask)){//TODO: Programar lógica de contacto con el sueloreturntrue;}else{//TODO: Programar lógica de no contactoreturnfalse;}}}
Tal vez no creaste y/o no le diste el tag de "ground" al GameObject que representa el suelo
O tampoco se lo pusiste en el desplegable del Player
Assets\Scripts\PlayerController.cs(51,14): error CS1525: Invalid expression term ')'
Alguien me ayuda?? :(
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
//Variables del movimiento del personaje
public float jumpForce = 6f;
Rigidbody2D PlayerRigidBody;
publicLayerMask groundMask;voidAwake(){//Obtener el componenente Rigidbody2D - Física del personajePlayerRigidBody=GetComponent<Rigidbody2D>();}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){if(Input.GetKeyDown(KeyCode.Space)||Input.GetMouseButtonDown(0))//Señala los botones con los que salta el personaje{Jump();}}//Función de salto del personajevoidJump(){if(IsTouchingTheGround()){PlayerRigidBody.AddForce(Vector2.up* jumpForce,ForceMode2D.Impulse);}}//Nos indica si el personaje está tocando o no el suelobool IsTouchingTheGround(){if(Physics2D.Raycast(this.transform.position,Vector2.down,1.5f, groundMask)){//TODO: Programar lógica de contacto con el sueloreturntrue;}else(){//TODO: Programar lógica de no contactoreturnfalse;}}
}
Hola Eric :D
bool IsTouchingTheGround(){if(Physics2D.Raycast(this.transform.position,Vector2.down,1.5f, groundMask)){//TODO: Programar lógica de contacto con el sueloreturntrue;}else{//TODO: Programar lógica de no contactoreturnfalse;}}
Esa es la parte de tu codigo corregido, el else no debe llevar parentesis :D
Puedes revisar tus errores leyendo el codigo.
En este caso decia que habia un caracter invaliod ')'
Saludos :D
Hola!
Por lo que veo en tu código es que se debe a que en el else estás usando paréntesis. Los paréntesis sólo los usarás en el if y en el caso que tengas un else if, de lo contrario, tendrás que poner directamente las llaves.
Además, como solo estás haciendo un return, puedes hacer lo de diferentes maneras.
En C# si pones antes de cada método 3 vences el /// te crear de manera automática la estructura de comentarios para métodos.
/// <summary>/// Nos indica si el personaje esta o no tocando el suelo /// </summary>/// <returns></returns> bool IsTouchingTheGround(){if(Physics2D.Raycast(this.transform.position,Vector2.down,2.0f, groundMask))returntrue;//contacto con el sueloelsereturnfalse;//no hay contacto con el suelo}```
Comparto algunos de mis apuntes en el código:
using System.Collections;using System.Collections.Generic;using UnityEngine;publicclassPlayerControl:MonoBehaviour{// Variables del movimiento del personajepublic float jumpForce = 6f;Rigidbody2D rigidBody;publicLayerMask groundMask;voidAwake(){ rigidBody =GetComponent<Rigidbody2D>();}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){//Indica con qué teclas salta el personajeif(Input.GetKeyDown(KeyCode.Space)||Input.GetMouseButtonDown(0)){Jump();}}//Indica de qué manera salta el personajevoidJump(){if(IsTouchingTheGround()){ rigidBody.AddForce(Vector2.up*jumpForce,ForceMode2D.Impulse);}}//Nos idica si está o no tocando el suelo bool IsTouchingTheGround(){if(Physics2D.Raycast(this.transform.position,Vector2.down,2.0f, groundMask))//Programar lógica de contacto con el suelo{returntrue;}//Programar lógica de no contacto con el sueloelse{returnfalse;}}}
Hola! La verdad supongo que esto ya esta previsto para aprender un nuevo conocimiento, pero una simple forma de resolver este "bug" seria establecer el "GetKey" En "GetKeyDown" Al hacer eso el personaje solo tendria 1 impulso al tocar space
El error que tendría ese método (sea que lo apliques al mouse o a la barra espaciadora), es que si presionas cualquiera de los dos consecutivamente, el personaje seguiría recibiendo impulsos hacia arriba, prácticamente podría volar, lo cual no es lo que se desea.
Lo que se desea es que este reciba un impulso, y no pueda recibir otro, hasta que el personaje toque de nuevo el suelo.
No es mejor tomar el tamaño del collider para poder hacer la función de IsTouchingTheGround ? Y así no andar adivinando que tanto se le tiene que dar.
aparece este error incluso descargue el ejemplo del profe lo pegue y aun asi aparece este error: error CS1061: 'PlayerController' does not contain a definition for 'tranform' and no accessible extension method 'tranform' accepting a first argument of type 'PlayerController' could be found (are you missing a using directive or an assembly reference?) podriadecirme si alguien sabe como solucionarlo
muchas gracias.
El error CS1061 Ocurre cuando tratas de llamar a un método o accesar a un miembro de clase que no existe. En este caso transform asegúrate de que la instancia de clase que trata de llamar el transform tiene ese método o lo hereda de alguna otra clase.
Al cambiar el GetKey por GetKeyDown mi personaje deja de saltar con la tecla Space, porque sera esto?
De todas formas, si lo dejo con GetKey funciona bien. Esta mal dejarlo asi?
Yo tengo problema con el salto, al agregar el Down al GetKey o GetMouseButton no salta.
¿Qué será?
The name 'Physics2d' does not exist in the current context No se porque pasa eso. Alguien me puede ayudar?
using System.Collections;using System.Collections.Generic;using UnityEngine;publicclassPlayerControler:MonoBehaviour{public float jumpForce = 6f;privateRigidbody2D rigidBody;publicLayerMask groundMask;voidAwake(){ rigidBody =GetComponent<Rigidbody2D>();}// Start is called before the first frame updatevoidStart(){}// Update is called once per framevoidUpdate(){if(Input.GetKey(KeyCode.Space)||Input.GetMouseButtonDown(0)){Jump();}}voidJump(){if(IsTouchingTheGround()){ rigidBody.AddForce(Vector2.up*jumpForce,ForceMode2D.Impulse);}}//Nos indica si el personaje esa o no tocando el suelo bool IsTouchingTheGround(){if(Physics2d.Raycast(this.transform.position,Vector2.down,1.5f, groundMask)){//TODO: programar logica de contacto con el suelo returntrue;}else{//TODO:programar logica de no contactoreturnfalse;}}}
Creo que podría ser que escribiste "Physics2d", cuando la "D" debería ser en mayúscula, para que Unity lo pueda reconocer correctamente.
Hola acabo de hacer el codigo tal cual y el RAYCAST no me funciona como deberia no esta tocando con ninguna capa y sigue funcionando
Hola. Tengo problemas de configuración de Unity con respecto a la cámara.
Primero: Cuando el personaje cae a la playaforma, se detiene ahí pero la cámara continua bajando hasta que los objetos desaparecen hacia arriba.
Segundo: En la pestaña "#Scene", no me muestra en el recuadro los objetos que coloqué, permanecen invisibles a menos que vaya a la pestaña "Game".
Agradecería la ayuda que me puedan brindar al respecto, Saludos.
Primero: revisa si as colocado un Rigidbody a la camara y eliminalo
segundo: Selecciona en la pestaña Layers (esquina superior derecha) la opcion ''Everyhing''