Nuestro primer videojuego con Scratch (2): detección de paredes

La semana pasada comenzaba una serie en PItando en la que trataba de crear con vosotros un videojuego muy básico en Sratch. Constaba de, a priori, cuatro partes:

  1. Mover al gato en las cuatro direcciones y hacer que rebote en los bordes del escenario. Conseguido la semana pasada.
  2. Programar la lógica de rebote para cuando el gato se encuentre con una pared roja.
  3. Programar la lógica de “gato encuentra a ratón”
  4. Proponer un esquema de puntuación para poder competir con nuestros amigos.

En esta entrada de hoy, segunda de la serie, vamos a ir un paso más allá y vamos a programar sobre un escenario básico la detección de paredes que, además, consistía en el ejercicio de la semana pasada.

Si te animas, que espero que sí, sigue leyendo 🙂

Recuperar el proyecto del ordenador

Como venimos haciendo y para dar cabida a todos los lectores, tengáis o no una Raspberry Pi, haremos este ejercicio sobre el navegador web. Abre la página del editor on-line de Scratch y sube el fichero que guardábamos la semana pasada. Si no lo tienes, ¿qué mejor momento el de ahora para hacer la primera parte del ejercicio? 😉

Para ello, ve al menú Archivo y pulsa la opción Subir de tu computadora.

Opción para subir un proyecto al editor Web de Scratch
Opción para subir un proyecto al editor Web de Scratch

Localiza el fichero y confirma la selección, tras lo cual el editor te pedirá confirmación en inglés (Replace contents of the current project?). Confirma la carga con el botón OK y, si todo ha ido bien, verás el editor cargado con el estado en el que lo dejamos la semana pasada; en mi caso es el de la siguiente imagen.

Proyecto cargado
Proyecto cargado

Detección de pared

Para detectar una pared hay que colocar un sensor que se active si el gato, esto es, el Sprite1, toca una de ellas. Para hacerlo, Scratch proporciona un sensor de detección de colores. Esto es muy conveniente porque permite detectar patrones en el escenario de fondo sin tener que definir las paredes como objetos propiamente dichos, o Sprites.

Como ya nos dice la experiencia, los sensores son bloques que encajan como sentencias para ser evaluadas en bloques condicionales y bucles. Por lo tanto, un enfoque que podemos seguir es que, siempre que se pulse una tecla, comprobar si se toca un color. 

Sensor de detección de color en un bloque condicional activado por la pulsación de cualquier tecla
Sensor de detección de color en un bloque condicional activado por la pulsación de cualquier tecla

Configurar el bloque de comprobación de si se toca un color es sencillo: al colocar el sensor en el bloque condicional, haremos click con el botón izquierdo en el recuadro que expresa el color a detectar. En ese momento, el cursor cambia de forma a una mano (normalmente es una flecha), lo que significa que Scratch está esperando a que le señalemos el color a evaluar. Lo que haremos será hacer click con el botón izquerdo en una pared, y veremos que el recuadro cambia al color de la pared.

Reacción a la detección de una pared

Lo que queda por hacer ahora es codificar la reacción a la colisión con una pared. Se nos pueden ocurrir dos alternativas:

  1. Parar de mover el gato
  2. Programar un rebote

La primera de las alternativas no va a funcionar, o al menos todo lo bien que quisiéramos, y formará parte de “los deberes” tratar de razonar por qué. La segunda la programaremos ahora, pero será laboriosa porque Scratch no proporciona un bloque “rebotar” genérico, por lo que tendremos que detectar la dirección en la que el gato se está moviendo y actuar en cada caso específico, deslizando al gato una determinada distancia en dirección contraria.

Veamos en detalle el código Scratch para el rebote en dirección abajo. Como es lógico, dentro del bloque condicional deberemos detectar que la dirección de desplazamiento es hacia abajo, hecho que demuestra que la tecla que se está pulsando es “flecha abajo”, ↓. Una vez claro esto, lo que habrá que hacer será desplazarlo hacia arriba una distancia prudencial.

Programación de rebote en dirección descendente.
Programación de rebote en dirección descendente.

Vamos a probarlo moviendo al gato de forma que podamos “atacar a una pared” pulsando la flecha hacia abajo: verás que funciona como debería pero… ahora hazlo de una forma exigente: mantén la tecla pulsada todo el tiempo, ¡sin piedad!, ¡EL GATO TIENE QUE ATRAVESAR LA PARED!

Verás que en algunas ocasiones, el gato (en mi caso, la cabeza del gato) consigue penetrar en la pared, quedando enmedio:

A veces el gato, a fuerza de cabezonería, consigue hacer mella en la pared :-)
A veces el gato, a fuerza de cabezonería, consigue hacer mella en la pared 🙂

Una explicación profunda de este fenómeno que, si estudiamos con detenimiento el código sabremos que no debería suceder, puede llevarnos varias páginas. En resumen, lo que ocurre en este tipo de sistemas es que Scratch reparte el código de nuestro programa en unos recursos especiales del ordenador, llamados hilos. Además de nuestro código, hay hilos ocupados en actualizar la pantalla para reflejar la posición de los Sprites, e interpretar eventos como los del teclado. El intérprete de Scratch, de una forma más o menos literal, reparte su atención a partes iguales por todos los hilos, pero desafortunadamente ese reparto hace aguas cuando se trata de atender a eventos del teclado si éstos vienen en grupo.

¿Por qué los eventos de teclado vienen en grupo a veces?

Seguro que te has fijado que en ocasiones, si el ordenador va lento, puedes seguir tecleando porque, de alguna forma, el teclado tiene cierta cantidad de memoria llamada buffer, que almacena las teclas pulsadas. Gracias a ello,  cuando el ordenador vuelve en sí, de repente aparecen todas las teclas que habías pulsado o, al menos, hasta cierto punto.

Este comportamiento se vuelve en nuestra contra cuando pulsamos demasiado tiempo la tecla “flecha abajo”. Lo que ocurre es que el buffer de teclado se llena, y cuando eso ocurre, el sistema operativo intenta enviar a Scratch todas las teclas almacenadas, de golpe y porrazo. Cuando eso ocurre, y debido a cómo Scratch está programado internamente, de alguna forma el control de eventos se satura y pueden ocurrir efectos no deseados como éste.

¿Cómo podemos evitarlo?

Lo que tenemos que hacer es que, la primera vez que se detecta una pared, paremos de mover al gato en dirección hacia abajo mientras se produce el rebote. Eso lo podremos hacer con una variable, que llamaremos mover, y que se comprobará en el bloque de movimiento hacia abajo. Como una imagen vale más que mil palabras, os enseñaré primero el código y luego lo analizamos.

Bloqueando el procesado de la tecla "flecha hacia abajo" mientras se produce el rebote
Bloqueando el procesado de la tecla “flecha hacia abajo” mientras se produce el rebote

Vamos a ver lo que he hecho.

  1. Bloque de movimiento (el superior): condiciono el movimiento hacia abajo al estado de una variable llamada parar. Si parar vale 0, el gato se mueve. Es decir, asocio 0 al valor “no“, y “si no hay que parar”, muevo al gato. Esto tiene truco y es que por defecto, en Scratch, las variables numéricas tienen como valor inicial el 0.
  2. Bloque de detección de pared (el inferior):
    1. Si el gato toca la pared, fijo la variable parar a 1. Esto hace que el bloque de movimiento, el superior, deje de funcionar. A la siguiente pulsación de tecla que llegue a Scratch, el bloque de movimiento encontrará la condición desfavorable y no se ejecutará.
    2. Cuando termino de “hacer que el gato rebote”, vuelvo a fijar la variable parar a 0. Esto, como estoy seguro de que podréis intuir, deshace lo hecho y consigue que el bloque de movimiento vuelva a funcionar. Fíjate que he puesto una espera del mismo tiempo en el que el rebote tarda en ejecutarse: lo hago para asegurarme de que la variable cambia su valor sólo cuando el gato deja de moverse.

Prueba esto ahora. Verás que el gato rebota tan pronto como la punta de sus orejas roza la pared. Las orejas, porque en mi caso el gato va de cabeza debido al “efecto rebote” contra los bordes del escenario y con el que experimentaba la semana pasada.

Esto sí que es lo que queremos. Ya sólo queda generalizarlo a las cuatro direcciones.

Código Scratch para generalizarlo a las cuatro direcciones

Sin más, voy a dejar aquí el código, pero es bueno que intentes recrearlo por ti mismo, para coger soltura.

Solución al artículo de hoy. Pincha en la imagen para verlo en todos su esplendor.
Solución al artículo de hoy. Pincha en la imagen para verlo en todo su esplendor.

Propina: hacer que el gato no ande de cabeza al rebotar en los bordes del escenario

Esto es opcional, porque el que el gato gire al rebotar le da un aspecto curioso al resultado. Sólo tienes que reorientarlo después del bloque de rebote, así:

Enderezar al gato
Enderezar al gato

Consideraciones y ejercicios

Si experimentas lo suficiente verás que puedes hacer que una pared se sitúe en zonas “angostas” del Sprite. Por ejemplo, que un extremo de una pared se cuele entre las orejas o los bigotes de nuestro protagonista. También ocurre que, si dejas demasiado tiempo pulsada una misma tecla, el programa acaba por fallar 😉 . Se comporta mejor que cuando pusimos la solución de la variable, sí… pero no del todo bien, y te toca explicar por qué y encontrar una solución.

Ejercicio 1: reproduce esos comportamientos y ofrece una explicación a los mismos.

Ejercicio 2: propón y construye al menos dos soluciones a estos comportamientos.

Ejercicio 3: Si, al detectar una pared, paramos el gato en vez de hacer que rebote, el programa funciona mal. ¿Por qué?

Un comentario en “Nuestro primer videojuego con Scratch (2): detección de paredes

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *