Jump to content
Alex Ametlle

Tengo algún problemilla de físicas...

Recommended Posts

Hola a todos!

Continuo realizando pequeños avances en mi clon de SuperMario, la mayor parte la he dedicado a los objetos (que interactúen lo mas parecido al original posible).

Tengo varios problemas visibles, al pasar por encima de las 3 primeras cajas, el player no detecta bien las colisiones y no detecta el suelo como es debido, también hay un problema con las físicas de movimiento, tanto por parte del player como por la seta que se instancia al activar la ultima caja "?".

Podéis acceder a mi proyecto para ver lo que os comento:

(SE SALTA CON LA "Z")

https://simmer.io/@MrFerret/practica-clon-mario-nes

El movimiento es como si se fuera incrementando cada vez mas (efecto hielo), este es el código del srcipt del player (el de la seta tiene el mismo principio):

Quote

public class PlayerController : MonoBehaviour
{
    public bool canJump;
    public float velocidad = 120f;
    public float fuerza = 5000f;


    void Start()
    {

    }

    void Update()
    {

        if (Input.GetKey("right"))
        {
            gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(velocidad, 0f * Time.deltaTime));
            gameObject.GetComponent<Animator>().SetBool("moving", true);
            gameObject.GetComponent<SpriteRenderer>().flipX = false;
        }

        if (Input.GetKey("left"))
        {
            gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(-velocidad, 0f * Time.deltaTime));
            gameObject.GetComponent<Animator>().SetBool("moving", true);
            gameObject.GetComponent<SpriteRenderer>().flipX = true;
        }

        if (!Input.GetKey("right") && !Input.GetKey("left"))
        {
            gameObject.GetComponent<Animator>().SetBool("moving", false);
        }

        if (Input.GetKeyDown("z") && canJump)
        {                                                                          
            gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0, fuerza));
            gameObject.GetComponent<AudioSource>().Play();
        }

    }
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.transform.tag == "Ground")
        {
            canJump = true;
            gameObject.GetComponent<Animator>().SetBool("aire", false);
        }

    }

    private void OnCollisionExit2D(Collision2D descollision)
    {
        if (descollision.transform.tag == "Ground")
        {
            canJump = false;
            gameObject.GetComponent<Animator>().SetBool("aire", true);
        }
    }
}

 

Edited by Alex Ametlle

Share this post


Link to post
Share on other sites
public class move : MonoBehaviour
{
    public bool canJump;
    public float velocidad = 120f;
    public float fuerza = 5000f;
   // public SpriteRenderer Spr;
   // public Animator anim;
    public Rigidbody2D rb;  // pon el rb de esta manera te va a ser mas facil. al igual que animator., public o private Animator anim; 

    void Start()
    {

    }

    void Update()
    {

        if (Input.GetKey("right"))
        {
            rb.AddForce(new Vector2(velocidad, 0f * Time.deltaTime));
            //anim.SetBool("moving", true);
            gameObject.GetComponent<Animator>().SetBool("moving", true);
              //Spr.flipX = false;
            gameObject.GetComponent<SpriteRenderer>().flipX = false;
        }



        if (Input.GetKey("left")) //if si pulso x ...
        {
            rb.AddForce(new Vector2(-velocidad, 0f * Time.deltaTime));
            gameObject.GetComponent<Animator>().SetBool("moving", true);
            gameObject.GetComponent<SpriteRenderer>().flipX = true;
        }
        else //els de lo contrario x ...
        {
            rb.velocity = new Vector2(0, rb.velocity.y); // esto frena la velocidad cuando dejas de apretar la tecla;
        }

        if (!Input.GetKey("right") && !Input.GetKey("left"))
        {
            gameObject.GetComponent<Animator>().SetBool("moving", false);
        }
        else
        {
            rb.velocity = new Vector2(0, rb.velocity.y);
        }

        if (Input.GetKeyDown("z") && canJump)
        {
            gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0, fuerza));
            gameObject.GetComponent<AudioSource>().Play();
        }

    }
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.transform.tag == "Ground")
        {
            canJump = true;
            gameObject.GetComponent<Animator>().SetBool("aire", false);
        }

    }

    private void OnCollisionExit2D(Collision2D descollision)
    {
        if (descollision.transform.tag == "Ground")
        {
            canJump = false;
            gameObject.GetComponent<Animator>().SetBool("aire", true);
        }
    }
}

 

Espero puedas terminar tu proyecto te deseo lo mejor 💪👍

  • Thanks 1

Share this post


Link to post
Share on other sites

Algunas veces funciona bien y otras no, revisa los tag de los gameobjects.

En la cuarta caja funciona bien siempre (al menos a mi no me ha fallado), mira ver que tiene esta caja que la hace distinta a las otras.

En cuanto a la velocidad, no tienes un limite definido, así que al aplicarle la fuerza aumentará a medida que avanzas. Pone en una variable un máximo a la velocidad y un condicional para que cuando llegue, se mantenga en ese máximo.

Saludos

Share this post


Link to post
Share on other sites

Hola @Alex Ametlle, algunas consejos que quizás ayuden:

1. Evita los GetComponent en Update/FixedUpdate. Si es posible cachea dichos componentes:

Rigidbody rb;

void Awake()
{
	rb = GetComponent<Rigidbody>();
}
      
      
void FixedUpdate()
{
    // Ejemplo
	rb.velocity = ...;      
}

2. Nunca modificar propiedades físicas en Update (o LateUpdate). La razón por la que debés usar FixedUpdate es porque este callback corre antes de la simulación física (está preparado para brindarte esa "lógica" previa a la simulación). La otra es por un tema de estabilidad, en general los motores usan pasos fijos (fixed step) para mejorar la "estabilidad" en los resultados. Además, si tu framerate es bajo, es posible que FixedUpdate (+ simulación) corran varias veces para compensar este tiempo de frame grande.

En resumen, físicas/movimiento de cuerpos = FixedUpdate , inputs/transforms puros = Update/LateUpdate.

3. Esto:

rb.AddForce(new Vector2(velocidad, 0f * Time.deltaTime));
            

...está fundamentalmente mal, por dos razones:

3.1 estás multiplicando un valor por cero.

3.2 Siempre tienes que incluir el dt, incluso para FixedUpdate (ni hablar para Update como en tu caso, vas a encontrar que depende del framerate)

rb.AddForce(new Vector2(velocidad * Time.deltaTime , 0f ));

(Por cierto, usar deltaTime o fixedDeltaTime es lo mismo dentro de FixedUpdate)

Si necesitas ignorar la componente y podés multiplicar por Vector2.right ( = <1,0>)

rb.AddForce( Vector2.right * velocidad * Time.deltaTime );

velocidad * dt te va a dar aceleración, pero el modo de la fuerza está en Force (por defecto), es decir que la masa del objeto va a influir en tus calculos.

https://docs.unity3d.com/ScriptReference/ForceMode2D.Force.html

Un objeto de masa 1 no va a acelerar lo mismo que uno con masa 10 (diez veces menos aceleración). Así que, incluye la masa en los calculos, al menos si quieres coherencia en tus resultados.

rb.AddForce( Vector2.right * velocidad * Time.deltaTime * rb.mass );

Para 3D podés optar por usar un ForceMode.acceleration (2D no tiene un modo así 😞 )

 

4. Usa los ejes (axis) para las inputs. Esto:

if (Input.GetKey("right"))
{
	rb.AddForce(new Vector2(velocidad, 0f * Time.deltaTime));
  	animator.SetBool("moving", true);
  	spriteRenderer.flipX = false;
}

if (Input.GetKey("left"))
{
    rb.AddForce(new Vector2(-velocidad, 0f * Time.deltaTime));
    animator.SetBool("moving", true);
    spriteRenderer.flipX = true;
} 

Se puede transformar en esto:

float horizontalAxis = Input.GetAxis("Horizontal");
float acceleration = velocity * Time.deltaTime;
rb.AddForce( Vector2.right * horizontalAxis * acceleration );

// horizontalAxis != 0 = true si es distinto de cero, false caso contrario.
animator.SetBool("moving", horizontalAxis != 0 );

if( horizontalAxis != 0f )
	spriteRenderer.flipX = horizontalAxis < 0;

 

5. Grounding, o detección de suelo. El tema es complejo, pero siempre debés tener una variable grounded dando vueltas, te va a ayudar para todo el código (físicas, animaciones, sonidos, etc), es una parte vital de todo character. Siendo 100% amigables con las físicas (podés hacerlo de otras maneras), la forma de detectar un suelo es mediante los contactos. Basicamente, si uno de tus contactos (puede haber muchos) es parte del suelo, entonces estás "grounded". El "es parte del suelo" se resuelve de forma simple. Una vez que tengas la normal del contacto, tienes que hacer lo siguiente (en OnCollisionXXX):

float slopeAngle = Vector3.Angle( Vector3.up , contact.normal ); //slope = pendiente del suelo

// Es parte del suelo?
// puse 85f como ej, puede ser menos si queres, siempre que sea menor a 90 está bien
if( slopeAngle < 85f ) 	
	grounded = true;

 

Edited by lightbug
  • Like 1

Share this post


Link to post
Share on other sites

×
×
  • Create New...