Menu
CoddyTech

Nunca plano

Lección 10 de 19 del curso Juego en Unity: Rompe ladrillos de Coddy.

Un rebote en la esquina de un ladrillo puede dejar la pelota casi horizontal, cruzando el campo una y otra vez mientras el jugador espera. La solución está en la corrección de velocidad: si la componente y de la dirección es demasiado pequeña, establece un mínimo y vuelve a normalizar.

Mathf.Abs da el valor absoluto de un número, sin su signo, así que una sola comprobación detecta las pelotas que van hacia arriba y hacia abajo. Mathf.Sign da -1 para un número negativo y 1 en caso contrario, por lo que una pelota que va hacia abajo sigue yendo hacia abajo.

Debug.Log(Mathf.Abs(-0.12f));   // 0.12
Debug.Log(Mathf.Sign(-0.12f));  // -1

Con un mínimo de 0.4, la dirección (0.99, -0.12, 0) obtiene y -0.4 y, normalizada, aproximadamente (0.93, -0.37, 0): sigue yendo hacia la derecha y hacia abajo, pero ahora con suficiente inclinación para volver. Las direcciones más inclinadas no se modifican.

challenge icon

Desafío

Intermedio

Evita que la pelota cruce el campo casi en horizontal.

  1. En Ball, añade public float minVertical = 0.3f;
  2. En FixedUpdate, toma Vector3 direction = rb.linearVelocity.normalized;. Si Mathf.Abs(direction.y) < minVertical, establece direction.y = Mathf.Sign(direction.y) * minVertical; y vuelve a normalizar.
  3. Establece rb.linearVelocity = direction * speed;

Pulsa «Jugar»: la pelota nunca cruza el campo en horizontal durante mucho tiempo. Después, pulsa «Comprobar».

Pruébalo tú mismo

using UnityEngine;
using UnityEngine.InputSystem;

public class Ball : MonoBehaviour
{
    public Transform paddle;
    public float speed = 9f;

    Rigidbody rb;
    bool launched;

    void Awake()
    {
        rb = GetComponent<Rigidbody>();
        PhysicsMaterial bouncy = new PhysicsMaterial("Bouncy");
        bouncy.bounciness = 1f;
        bouncy.dynamicFriction = 0f;
        bouncy.staticFriction = 0f;
        bouncy.bounceCombine = PhysicsMaterialCombine.Maximum;
        bouncy.frictionCombine = PhysicsMaterialCombine.Minimum;
        GetComponent<Collider>().material = bouncy;
    }

    void Update()
    {
        if (launched) return;
        transform.position = paddle.position + Vector3.up * 0.5f;
        if (Keyboard.current.spaceKey.wasPressedThisFrame) Launch(new Vector3(0.3f, 1f, 0f));
    }

    public void Launch(Vector3 direction)
    {
        launched = true;
        rb.linearVelocity = direction.normalized * speed;
    }

    public void ResetToPaddle()
    {
        launched = false;
        rb.linearVelocity = Vector3.zero;
    }

    void FixedUpdate()
    {
        if (!launched) return;
        rb.linearVelocity = rb.linearVelocity.normalized * speed;
    }

    void OnCollisionEnter(Collision collision)
    {
        if (!collision.gameObject.CompareTag("Paddle")) return;
        float offset = transform.position.x - collision.transform.position.x;
        float halfWidth = collision.collider.bounds.size.x / 2f;
        float x = Mathf.Clamp(offset / halfWidth, -1f, 1f);
        rb.linearVelocity = new Vector3(x, 1f, 0f).normalized * speed;
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("DeathZone")) GameManager.Instance.BallLost(this);
    }
}

Todas las lecciones de Juego en Unity: Rompe ladrillos

Practica por tu cuenta: Playground de Unity