Ya hay partida. La bola rebota en las paredes, rebota en la pala, y si se te escapa por abajo vuelve a salir desde el centro.
Una bola es una posición y una velocidad
let velocityX = BALL_SPEED * 0.55;
let velocityY = -BALL_SPEED;Dos números: cuánto avanza por segundo en horizontal y cuánto en vertical. velocityY
empieza en negativo porque (capítulo 2) restar de la Y es subir.
Y moverla es el mismo dt de siempre, dos veces:
ball.transform.x += velocityX * dt;
ball.transform.y += velocityY * dt;Rebotar es cambiar un signo
Contra la pared izquierda:
if (ball.transform.x <= 0) {
ball.transform.x = 0;
velocityX = Math.abs(velocityX);
}Dos cosas, y las dos importan.
Recolocarla en el borde (x = 0). La bola se mueve a saltos, así que rara vez cae
exactamente en 0: lo normal es que un fotograma la deje en −3. Si no la devuelves al
borde, se queda un pelín fuera.
Forzar el signo con Math.abs, en vez de velocityX = -velocityX. Aquí está el fallo
clásico de este juego, y merece un párrafo entero.
Si la bola se queda pegada al borde, la condición x <= 0 se cumple otra vez al fotograma
siguiente. Con -velocityX, le das la vuelta otra vez, y otra, y otra. La bola tirita
en el borde, atrapada. Con Math.abs, dices lo que de verdad quieres: a partir de ahora,
hacia la derecha, esté como esté. Repetirlo no hace daño.
La pala: colisión de cajas
const onPaddle =
velocityY > 0 &&
ball.transform.y + BALL_SIZE >= PADDLE_Y &&
ball.transform.y <= PADDLE_Y + PADDLE_H &&
ball.transform.x + BALL_SIZE >= paddle.transform.x &&
ball.transform.x <= paddle.transform.x + PADDLE_W;Cuatro comparaciones que preguntan si dos rectángulos se solapan. Se llama AABB (axis-aligned bounding box, caja alineada con los ejes) y es la colisión más simple que existe. Con rectángulos que no rotan, sobra.
La quinta condición, la primera, es la que evita otro fallo desagradable: velocityY > 0
significa solo si la bola va bajando. Sin eso, una bola que roza la pala por el lateral
mientras sube rebotaría hacia arriba estando ya por debajo, y se quedaría enganchada en el
borde inferior de la pala.
Y cuando la toca, pasa lo mismo que con las paredes: se la recoloca justo encima
(ball.transform.y = PADDLE_Y - BALL_SIZE) antes de mandarla hacia arriba, para que no se
dibuje ni un fotograma metida dentro de la pala.
El golpe no es siempre igual
const offset = (ball.transform.x + BALL_SIZE / 2 - paddle.transform.x) / PADDLE_W - 0.5;
velocityX = offset * BALL_SPEED * 1.6;Esto es lo que convierte el juego en un juego. offset mide por dónde le has dado,
como un número entre −0,5 (el extremo izquierdo de la pala) y +0,5 (el derecho). El centro
da 0.
Y esa medida se convierte en la velocidad horizontal de salida. Dale con la punta izquierda y la bola sale disparada hacia la izquierda; dale con el centro y sale casi recta.
Sin esta línea la bola rebotaría siempre con el mismo ángulo, y no tendrías forma de apuntar. Un rompeladrillos donde no puedes apuntar es un salvapantallas.
¿Qué significa `&&`?
&& es «y». Una condición con && solo es cierta si todas sus partes lo son:
if (tengoHambre && hayComida) comer();Aquí hay cinco partes encadenadas, cada una en su línea para poder leerlas. La bola toca la pala solo si se cumplen las cinco a la vez: va bajando, y su borde de abajo llega a la pala, y no se ha pasado de largo, y su borde derecho llega al izquierdo de la pala, y su borde izquierdo no se ha pasado del derecho.
También existe ||, que es «o» y basta con que se cumpla una.
Ahora tú
Cambia el 1.6 del último trozo por 3 y fíjate en lo salvaje que se vuelve el ángulo.
Ponlo a 0 y verás el salvapantallas del que te hablaba.
Y prueba a poner velocityX = -velocityX en lugar de Math.abs(velocityX) en los rebotes
laterales. Puede que tarde un rato en pasar, pero acabará tiritando.