Luis Rodrigo Alvarez Herrera
Germán Arevalo Jerez
Adrián Vinaroz Zaplana
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?
¿Por qué GetKey causa múltiples saltos?
¿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?
// En tu script del jugador public LayerMask 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. bool IsTouchingTheGround() { // 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:
¿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?
void Jump() { if (Input.GetKeyDown(KeyCode.Space) && IsTouchingTheGround()) { // Aplica un impulso vertical una sola vez. rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse); } }
Buenas prácticas:
¿Qué parámetros ajustar: fuerza y masa?
Ideas clave que consolidar:
¿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.
Luis Rodrigo Alvarez Herrera
Germán Arevalo Jerez
Adrián Vinaroz Zaplana
Cristian Mauricio Arias Mayorga
Pedro Muñoz Becerra
Cruz Ángel Núñez Martínez
Juan Carlos Frausto Arias
Victor Alfonso Rodriguez Galeano
Victor Alfonso Rodriguez Galeano
Miguel Prada
Kevin Patricio Campos Venegas
Julián Camilo Velandia
Luis Rodrigo Alvarez Herrera
Julio César Noguera López
Luis Rodrigo Alvarez Herrera
Johan Leandro Cifuentes Moreno
Sebastian Ignacio Hurtado
Altair Barahona
Cesar Hernández Ramírez
Ericson Luis Quispe Abad
Ricardo Rito Anguiano
Luis Lira
Camilo Andres Agudelo
Antonio Demarco Bonino
Felix Carrasco
Ricardo Andres Gomez Villalobos
Jahiro Bojorquez Uriarte
Jesús David Rodríguez Varela
Erik Elyager
Piero Massera
des.92
Hermes Aguilar
Carlos David Zamora Gutiérrez
Andrés Mauricio Rodríguez González
Para poder ver el raycast, use el drawcast desde el centro del personaje y asi damr una idea para el salto usne este comando en en metodo update
Debug.DrawRay(this.transform.position, Vector2.down * 1.5f, Color.green);
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
Simplificación de código
bool IsTouchingTheGround(){ return Physics2D.Raycast(this.transform.position, Vector2.down, 2.0f, groundMask); }
Se puede simplificar aún más:
bool IsTouchingTheGround() => Physics2D.Raycast(transform.position, Vector2.down, 2f, groundMask);
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:
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
mi personaje no salta que hice mal??
Podrías compartir tu código para así el alguién de la comunidad pueda ayudarte @aeonics
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { //variables para el movimiento del personaje public float jumpForce = 6f; private Rigidbody2D playerRigidBody; public LayerMask groundMask; //detectar objetos con los cuales se puede colisionar void Awake() { playerRigidBody = GetComponent<Rigidbody2D>(); } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() //se pregunta, si la tecla espacio o tecla del boton del mouse es apretada hara ciera accion { if(Input.GetKey(KeyCode.Space) || (Input.GetMouseButtonDown(0))) { JumpPlayer(); } } void JumpPlayer() //permite el salto del perosnaje añadiendo una fuerza en el vector2 * jumpforce y definiendo si es de impulso, es decir si la fuerza aplicada es de una sola vez o permanece { if (IsTouchingTheGound()) { playerRigidBody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse); } } bool IsTouchingTheGound() // el personaje esta o no en el suelo { if(Physics2D.Raycast(this.transform.position, Vector2.down, 1, groundMask)) { //TODO: programas logica de contacto con el suelo return true; } else { //todo: programa logoca de no contacto return false; } } }
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; public class PlayerController : MonoBehaviour { //Variables del movimiento del personaje [SerializeField]public float jumpForce = 6f; Rigidbody2D playerRigidBody; public LayerMask groundMask; void Awake() { playerRigidBody = GetComponent<Rigidbody2D>(); } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if(Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0)) { Jump(); } } void Jump() { 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 suelo return true; } else { //TODO: Programar lógica de no contacto return false; } } }
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;
public LayerMask groundMask; void Awake() { //Obtener el componenente Rigidbody2D - Física del personaje PlayerRigidBody = GetComponent<Rigidbody2D>(); } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0)) //Señala los botones con los que salta el personaje { Jump(); } } //Función de salto del personaje void Jump() { if (IsTouchingTheGround()) { PlayerRigidBody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse); } } //Nos indica si el personaje está tocando o no el suelo bool IsTouchingTheGround() { if(Physics2D.Raycast(this.transform.position, Vector2.down, 1.5f, groundMask)) { //TODO: Programar lógica de contacto con el suelo return true; } else() { //TODO: Programar lógica de no contacto return false; } }
}
Hola Eric :D
bool IsTouchingTheGround() { if(Physics2D.Raycast(this.transform.position, Vector2.down, 1.5f, groundMask)) { //TODO: Programar lógica de contacto con el suelo return true; } else { //TODO: Programar lógica de no contacto return false; } }
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.
if(Physics2D.Raycast(this.transform.position, Vector2.down, 1.5f, groundMask)) return true; else return false
if(Physics2D.Raycast(this.transform.position, Vector2.down, 1.5f, groundMask)) ? true : false;
O también, si el método Raycast te devuelve un valor boolean, puedes, puedes hacer:
retun Physics2D.Raycast(this.transform.position, Vector2.down, 1.5f, groundMask)
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)) return true;//contacto con el suelo else return false;//no hay contacto con el suelo }```
Comparto algunos de mis apuntes en el código:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerControl : MonoBehaviour { // Variables del movimiento del personaje public float jumpForce = 6f; Rigidbody2D rigidBody; public LayerMask groundMask; void Awake() { rigidBody = GetComponent<Rigidbody2D>(); } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { //Indica con qué teclas salta el personaje if(Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0)) { Jump(); } } //Indica de qué manera salta el personaje void Jump () { 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 { return true; } //Programar lógica de no contacto con el suelo else { return false; } } }
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; public class PlayerControler : MonoBehaviour { public float jumpForce = 6f; private Rigidbody2D rigidBody; public LayerMask groundMask; void Awake() { rigidBody = GetComponent<Rigidbody2D>(); } // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if(Input.GetKey(KeyCode.Space) || Input.GetMouseButtonDown(0)) { Jump(); } } void Jump() { 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 return true; }else{ //TODO:programar logica de no contacto return false; } } }
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