diff --git a/src/tutorial_de_uxn.gmo b/src/tutorial_de_uxn.gmo index c9c43fa..f542cc3 100644 --- a/src/tutorial_de_uxn.gmo +++ b/src/tutorial_de_uxn.gmo @@ -43,7 +43,25 @@ también hablamos del uso de la memoria del programa como un espacio para datos # día 5 -¡proximamente! +aquí introducimos el dispositivo de ratón varvara para explorar más posibles interacciones, y cubrimos los elementos restantes de uxntal y uxn: la pila de retorno, el modo de retorno y el modo de retención. + +también discutimos posibles estructuras para crear bucles y programas más complejos utilizando estos recursos. + +{tutorial de uxn día 5} + +# día 6 + +aquí hablamos de cómo podemos integrar todo lo que hemos cubierto para crear subrutinas y programas más complejos para el ordenador varvara. + +basamos nuestra discusión en una recreación del clásico juego pong. + +además de utilizar estrategias y fragmentos de código anteriores, cubrimos estrategias para dibujar y controlar sprites de varios tiles, y para comprobar las colisiones. + +{tutorial de uxn día 6} + +# día 7 + +¡próximamente! # índice tentativo diff --git a/src/tutorial_de_uxn_apéndice_a.gmo b/src/tutorial_de_uxn_apéndice_a.gmo new file mode 100644 index 0000000..6c93ea3 --- /dev/null +++ b/src/tutorial_de_uxn_apéndice_a.gmo @@ -0,0 +1,3 @@ +# apéndice A + +¡proximamente! traducción en proceso. :) \ No newline at end of file diff --git a/src/tutorial_de_uxn_día_2.gmo b/src/tutorial_de_uxn_día_2.gmo index e143824..3f65cea 100644 --- a/src/tutorial_de_uxn_día_2.gmo +++ b/src/tutorial_de_uxn_día_2.gmo @@ -1018,32 +1018,32 @@ por ejemplo, lo siguiente dividirá el ancho de la pantalla en dos, utilizando e #01 SFT2 ``` -### macros HALF +### macros MITAD -para seguir ilustrando el uso de las macros, podríamos definir unas macros HALF y HALF2, utilizando DIV o SFT. +para seguir ilustrando el uso de las macros, podríamos definir unas macros MITAD y MITAD2, utilizando DIV o SFT. usando DIV: ``` -%HALF { #02 DIV } ( numero -- numero/2 ) -%HALF2 { #0002 DIV2 } ( numero -- numero/2 ) +%MITAD { #02 DIV } ( numero -- numero/2 ) +%MITAD2 { #0002 DIV2 } ( numero -- numero/2 ) ``` usando SFT: ``` -%HALF { #01 SFT } ( numero -- numero/2 ) -%HALF2 { #01 SFT2 } ( numero -- numero/2 ) +%MITAD { #01 SFT } ( numero -- numero/2 ) +%MITAD2 { #01 SFT2 } ( numero -- numero/2 ) ``` and use any of them to calculate the center: ``` -.Pantalla/ancho DEI2 HALF2 .Pantalla/x DEO2 -.Pantalla/alto DEI2 HALF2 .Pantalla/y DEO2 +.Pantalla/ancho DEI2 MITAD2 .Pantalla/x DEO2 +.Pantalla/alto DEI2 MITAD2 .Pantalla/y DEO2 ``` -notemos que la macro HALF2 que utiliza SFT2 necesitará un byte menos que la que utiliza DIV2. esto puede o no ser importante dependiendo de tus prioridades :) +notemos que la macro MITAD2 que utiliza SFT2 necesitará un byte menos que la que utiliza DIV2. esto puede o no ser importante dependiendo de tus prioridades :) ## dibujando sprites en posiciones específicas diff --git a/src/tutorial_de_uxn_día_3.gmo b/src/tutorial_de_uxn_día_3.gmo index e71dfd0..2f100be 100644 --- a/src/tutorial_de_uxn_día_3.gmo +++ b/src/tutorial_de_uxn_día_3.gmo @@ -172,7 +172,7 @@ Controlador/tecla DEI ( lee la tecla y la introduce en la pila ) AND ( aplica un AND a las banderas de la pila, y empuja el resultado a la pila ) ``` -que la instrucción sea bitwise significa que aplica la operación AND a cada uno de los bits de los operandos. +que la instrucción sea a nivel de bit (o "bitwise") significa que aplica la operación AND a cada uno de los bits de los operandos. si ambas banderas fueran "verdaderas": @@ -569,7 +569,7 @@ dependiendo de nuestra aplicación, podríamos necesitar permitir que algunos de en ese caso, ¿cómo podríamos aislar cada uno de los bits para comprobar su estado individualmente? -conoce las máscaras AND a nivel de bits +conoce las máscaras AND a nivel de bits. ## máscara AND diff --git a/src/tutorial_de_uxn_día_4.gmo b/src/tutorial_de_uxn_día_4.gmo index 92ab6b4..c798a15 100644 --- a/src/tutorial_de_uxn_día_4.gmo +++ b/src/tutorial_de_uxn_día_4.gmo @@ -286,7 +286,7 @@ una posible desventaja es que podría ser menos legible. pero una posible ventaj ``` ( incrementar un short desde la página cero ) -%ZP-INC2 { DUP LDZ2 INC2 ROT STZ2 } ( zp-dirección -- ) +%PC-INC2 { DUP LDZ2 INC2 ROT STZ2 } ( pc-dirección -- ) ``` ## variables en direcciones relativas @@ -402,7 +402,7 @@ el siguiente programa ilustra los puntos anteriores, haciendo que nuestro cuadra |20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &pad $2 &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ] ( macros/constantes ) -%HALF2 { #01 SFT2 } ( desplazar un bit a la derecha ) ( corto -- corto/2 ) +%MITAD2 { #01 SFT2 } ( desplazar un bit a la derecha ) ( corto -- corto/2 ) %color-borrar { #40 } ( borrar 1bpp sprite del primer plano ) %color-2 { #4a } ( dibujar sprite de 1bpp con color 2 y transparencia ) @@ -418,7 +418,7 @@ el siguiente programa ilustra los puntos anteriores, haciendo que nuestro cuadra #2ce5 .Sistema/b DEO2 ( fijar la Pantalla/y a la mitad de la pantalla, menos 4 ) - .Pantalla/alto DEI2 HALF2 #0004 SUB2 .Pantalla/y DEO2 + .Pantalla/alto DEI2 MITAD2 #0004 SUB2 .Pantalla/y DEO2 ( fijar la dirección del sprite ) ;cuadrado .Pantalla/direc DEO2 @@ -503,7 +503,7 @@ el siguiente programa nos permite controlar la posición horizontal de nuestro c |80 @Controlador [ &vector $2 &boton $1 &tecla $1 ] ( macros/constantes ) -%HALF2 { #01 SFT2 } ( desplazar un bit a la derecha ) ( corto -- corto/2 ) +%MITAD2 { #01 SFT2 } ( desplazar un bit a la derecha ) ( corto -- corto/2 ) %color-borrar { #40 } ( borrar 1bpp sprite del primer plano ) %color-2 { #4a } ( dibujar sprite de 1bpp con color 2 y transparencia ) @@ -519,7 +519,7 @@ el siguiente programa nos permite controlar la posición horizontal de nuestro c #2ce5 .Sistema/b DEO2 ( fijar la Pantalla/y a la mitad de la pantalla, menos 4 ) - .Pantalla/alto DEI2 HALF2 #0004 SUB2 .Pantalla/y DEO2 + .Pantalla/alto DEI2 MITAD2 #0004 SUB2 .Pantalla/y DEO2 ( fijar la dirección del sprite ) ;cuadrado .Pantalla/direc DEO2 @@ -826,7 +826,7 @@ la sección "borrar el sprite" no es realmente necesaria en este caso debido a l |20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &pad $2 &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ] ( macros/constantes ) -%HALF2 { #01 SFT2 } ( desplazar un bit a la derecha ) ( corto -- corto/2 ) +%MITAD2 { #01 SFT2 } ( desplazar un bit a la derecha ) ( corto -- corto/2 ) %8MOD { #07 AND } ( byte -- byte%8 ) %A-CORTO { #00 SWP } ( byte -- corto ) @@ -845,8 +845,8 @@ la sección "borrar el sprite" no es realmente necesaria en este caso debido a l #2ce5 .Sistema/b DEO2 ( fijar Pantalla/x y y a la mitad de la pantalla menos 4 ) - .Pantalla/ancho DEI2 HALF2 #0004 SUB2 .Pantalla/x DEO2 - .Pantalla/alto DEI2 HALF2 #0004 SUB2 .Pantalla/y DEO2 + .Pantalla/ancho DEI2 MITAD2 #0004 SUB2 .Pantalla/x DEO2 + .Pantalla/alto DEI2 MITAD2 #0004 SUB2 .Pantalla/y DEO2 ( establecer la dirección del sprite ) ;animación .Pantalla/direc DEO2 diff --git a/src/tutorial_de_uxn_día_5.gmo b/src/tutorial_de_uxn_día_5.gmo index 6c9f66a..b194b8c 100644 --- a/src/tutorial_de_uxn_día_5.gmo +++ b/src/tutorial_de_uxn_día_5.gmo @@ -562,7 +562,7 @@ para llamar a la subrutina, podrías hacer algo como lo siguiente ``` #0008 ( empujar x inicial ) - .Pantalla/altura DEI2 HALF2 ( empujar y ) + .Pantalla/altura DEI2 MITAD2 ( empujar y ) #ff ( empujar longitud de la línea ) ;dibujar-linea-horizontal JSR2 ( llamar subrutina ) ``` diff --git a/src/tutorial_de_uxn_día_6.gmo b/src/tutorial_de_uxn_día_6.gmo index 08f9e7c..0147f1c 100644 --- a/src/tutorial_de_uxn_día_6.gmo +++ b/src/tutorial_de_uxn_día_6.gmo @@ -1 +1,1713 @@ -Traducción en proceso. ¡Proximamente! +# tutorial uxn: día 6, hacia el pong + +esta es la sexta sección del {tutorial uxn}! aquí hablamos de cómo podemos integrar todo lo que hemos cubierto para crear subrutinas y programas más complejos para el ordenador varvara. + +basamos nuestra discusión en una recreación del clásico juego pong. + +además de utilizar estrategias y fragmentos de código anteriores, cubrimos estrategias para dibujar y controlar sprites de varios tiles, y para comprobar las colisiones. + +# lógica general + +aunque pong pueda parecer sencillo y fácil de programar, una vez que lo analicemos nos daremos cuenta de que hay varios aspectos a tener en cuenta. afortunadamente, la mayoría de ellos se pueden dividir como diferentes subrutinas que podemos discutir por separado. + +abordaremos los siguientes elementos en orden: + +* dibujo del fondo +* dibujo y movimiento de las palas +* dibujo y rebote de la pelota + +# dibujando el fondo: repitiendo un tile + +en el {tutorial de uxn día 5} hablamos de una forma de crear un bucle para repetir un tile de 1bpp varias veces seguidas. + +aquí ampliaremos ese procedimiento para que también se repita verticalmente en toda la pantalla. + +## configuración + +empecemos con el siguiente programa como plantilla. incluye los datos para un sprite de 1bpp que consiste en líneas diagonales. + +``` +( hola-pong.tal ) + +( dispositivos ) +|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ] +|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &auto $1 &pad $1 &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ] +|80 @Controlador [ &vector $2 &boton $1 &tecla $1 ] + +( macros ) +%RTN { JMP2r } + +( programa principal ) +|0100 +@configuracion + ( establecer los colores del sistema ) + #2ce9 .Sistema/r DEO2 + #01c0 .Sistema/g DEO2 + #2ce5 .Sistema/b DEO2 + +BRK + +@tile-fondo 1122 4488 1122 4488 +``` + +## repetir un tile en una fila + +¿qué procedimiento podríamos seguir para repetir el dibujo de un tile empezando por x, y terminando en un límite correspondiente a x+ancho? + +una forma sería algo así como: + +* dibujar tile en x +* sumar 8 (el tamaño del tile) a x +* ¿es x menor que el límite? si lo es, repite el procedimiento, si no, termina + +### una primera versión + +digamos que nuestra x inicial es 0000, nuestro ancho es el ancho de la pantalla, y el tile que estamos dibujando es tile-fondo. + +el límite del bucle, x+ancho, sería también el ancho de la pantalla. + +el primer paso, dibujar el tile en x sería: + +``` +;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile ) +#0000 .Pantalla/x DEO2 ( establecer la x inicial ) + +#03 .Pantalla/sprite DEO ( dibujar sprite de 1bpp con color 3 y 0 ) +``` + +el segundo paso, añadir 8 a x, ya lo sabemos: + +``` +.Pantalla/x DEI2 #0008 ADD2 ( añadir 8 a x ) +.Pantalla/x DEO2 ( guardar la nueva x ) +``` + +comprobar si x es menor que el límite, saltando si lo es, sería algo así: + +``` +.Pantalla/x DEI2 ( obtener x ) +.Pantalla/ancho DEI2 ( obtener el límite ) +LTH2 ,&bucle JCN ( saltar si x es menor que el límite ) +``` + +integrando todo ello, podríamos obtener: + +``` +;tile-fondo .Pantalla/direc DEO2 ( fijar la dirección del tile ) +#0000 .Pantalla/x DEO2 ( establecer la x inicial ) + + &bucle-x + #03 .Pantalla/sprite DEO ( dibujar sprite de 1bpp con color 3 y 0 ) + .Pantalla/x DEI2 #0008 ADD2 ( añadir 8 a la x ) + DUP2 ( duplicar la nueva x ) + .Pantalla/x DEO2 ( guardar la nueva x ) + .Pantalla/ancho DEI2 ( obtener el límite ) + LTH2 ,&bucle-x JCN ( saltar si x es menor que el límite ) +``` + +nótese el uso de DUP2 para evitar releer el valor de x. + +esto debería funcionar ahora! pero vamos a discutir una forma más agradable de hacerlo :) + +### una segunda versión, usando la pila + +en lugar de leer el ancho de pantalla y la coordenada x cada vez, podríamos usar la pila para almacenar y manipular esos valores. + +después de establecer la dirección del tile, podemos empujar nuestro límite (el ancho de la pantalla) y el valor inicial hacia abajo en la pila: + +``` +.Pantalla/ancho DEI2 #0000 ( establecer límite y x inicial ) +``` + +usaremos ese valor en la parte superior de la pila como coordenada x. + +dentro del bucle, podemos duplicarlo para establecerlo como la x de la pantalla, e incrementarlo. + +entremedio, podemos enviar nuestro byte de sprite para dibujar el tile. + +``` + &bucle-x + DUP2 .Pantalla/x DEO2 ( establecer coordenada x ) + #03 .Pantalla/sprite DEO ( dibujar sprite de 1bpp con color 3 y 0 ) + #0008 ADD2 ( incrementar x ) +``` + +en este punto, la pila tiene la nueva x en la parte superior de la pila, y el ancho de la pantalla debajo. + +podemos compararlos en modo mantener para conservar esos valores en la pila, y hacer nuestro salto como antes: + +``` +GTH2k ( ¿es ese ancho mayor que x?, o también, ¿es x menor que el ancho? ) +,&bucle-x JCN ( salta si x es menor que el límite ) +``` + +cuando terminamos el bucle, tenemos que hacer POP de ambos valores. + +usando esta estrategia, obtendríamos el siguiente bucle: + +``` +;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile ) + +.Pantalla/ancho DEI2 #0000 ( empujar límite y x inicial ) + &bucle-x + DUP2 .Pantalla/x DEO2 ( establecer coordenada x ) + + #03 .Pantalla/sprite DEO ( dibujar sprite de 1bpp con color 3 y 0 ) + + #0008 ADD2 ( incrementar x ) + GTH2k ( ¿es ese ancho mayor que x? o también, ¿es x menor que el ancho? ) + ,&bucle-x JCN ( salta si x es menor que el límite ) +POP2 POP2 ( eliminar x y el límite ) +``` + +no sólo es un código más corto, sino que también es más rápido porque realiza menos operaciones dentro del bucle. + +¡es bueno tenerlo en cuenta! + +### programa completo + +lo siguiente muestra nuestro programa en contexto, llenando completamente la primera fila de nuestra pantalla con nuestro tile: + +=> ./img/screenshot_uxn-background-row.png captura de pantalla mostrando la primera fila de la pantalla varvara rellenada con líneas diagonales + +``` +( hola-pong.tal ) + +( dispositivos ) +|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ] +|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &auto $1 &pad $1 &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ] +|80 @Controlador [ &vector $2 &boton $1 &tecla $1 ] + +( macros ) +%RTN { JMP2r } + +( programa principal ) +|0100 +@configuracion + ( establecer los colores del sistema ) + #2ce9 .Sistema/r DEO2 + #01c0 .Sistema/g DEO2 + #2ce5 .Sistema/b DEO2 + + ( dibujar fondo ) + ;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile ) + + .Pantalla/ancho DEI2 #0000 ( establecer límite y x inicial ) + &bucle-x + DUP2 .Pantalla/x DEO2 ( fijar coordenada x ) + #03 .Pantalla/sprite DEO ( dibujar sprite de 1bpp con color 3 y 0 ) + #0008 ADD2 ( incrementar x ) + GTH2k ( ¿es ese ancho mayor que x? o también, ¿es x menor que el ancho? ) + ,&bucle-x JCN ( salta si x es menor que el límite ) + POP2 POP2 ( eliminar x y el límite ) +BRK + +@tile-fondo 1122 4488 1122 4488 +``` + +## repitiendo una fila + +similar a lo que acabamos de hacer: ¿cuál es el procedimiento que podríamos seguir para repetir verticalmente una fila empezando por y, y terminando en un límite correspondiente a y+altura? + +siguiendo la misma estrategia, podríamos hacer: + +* dibujar la fila en y +* añadir 8 (el tamaño del tile) a y +* ¿es `y` menor que el límite? si lo es, repetir el procedimiento, en caso contrario terminar + +### el bucle vertical + +para ilustrar un pequeño cambio, supongamos que queremos tener un margen en la parte superior e inferior de la pantalla. podemos definir este margen como una macro: + +``` +%MARGEN-PARED { #0010 } ( margen en la parte superior e inferior ) +``` + +nuestra y inicial sería MARGEN-PARED, y nuestro límite sería la altura de la pantalla menos MARGEN-PARED. + +podemos usar la misma estructura que antes, pero usando y: + +``` +;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile ) + +.Pantalla/alto DEI2 MARGEN-PARED SUB2 ( establecer límite ) +MARGEN-PARED ( establecer `y` inicial ) + &bucle-y + DUP2 .Pantalla/y DEO2 ( establecer coordenada y ) + + ( - dibujar fila aquí - ) + + + #0008 ADD2 ( ) + GTH2k ( ¿es ese límite mayor que y? o también, ¿es `y` menor que el límite? ) + ,&bucle-y JCN ( salta si `y` es menor que el límite ) +POP2 POP2 ( eliminar `y` y el límite ) +``` + +### bucles anidados + +ahora que tenemos esta estructura, podemos sustituir el comentario "dibujar fila aquí" por nuestro bucle horizontal anterior: + +``` +;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile ) + +.Pantalla/alto DEI2 MARGEN-PARED SUB2 ( establecer límite ) +MARGEN-PARED ( establecer `y` inicial ) + &bucle-y + DUP2 .Pantalla/y DEO2 ( establecer coordenada y ) + + ( dibujar fila ) + .Pantalla/ancho DEI2 #0000 ( establecer límite y x inicial ) + &bucle-x + DUP2 .Pantalla/x DEO2 ( fijar coordenada x ) + + ( dibujar sprite de 1bpp con color 3 y 0 ) + #03 .Pantalla/sprite DEO + + #0008 ADD2 ( incrementar x ) + GTH2k ( ¿es ese ancho mayor que x? o también, ¿es x menor que el ancho? ) + ,&bucle-x JCN ( salta si x es menor que el límite ) + POP2 POP2 ( eliminar x y el límite ) + + #0008 ADD2 ( ) + GTH2k ( ¿es ese límite mayor que y? o también, ¿es `y` menor que el límite? ) + ,&bucle-y JCN ( salta si `y` es menor que el límite ) +POP2 POP2 ( eliminar `y` y el límite ) +``` + +observa cómo, al asegurarnos de que nuestro bucle interno deja limpia la pila al terminar, podemos ponerlo allí sin problemas: los valores correspondientes al bucle externo permanecen intactos en la pila. + +## subrutina dibuja-fondo + +ahora podemos envolver estos bucles anidados dentro de una subrutina: + +``` +@dibuja-fondo ( -- ) + ;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile ) + + .Pantalla/alto DEI2 MARGEN-PARED SUB2 ( establecer límite ) + MARGEN-PARED ( establecer `y` inicial ) + &bucle-y + DUP2 .Pantalla/y DEO2 ( establecer coordenada y ) + + ( dibujar fila ) + .Pantalla/ancho DEI2 #0000 ( establecer límite y x inicial ) + &bucle-x + DUP2 .Pantalla/x DEO2 ( establecer coordenada x ) + + ( dibujar sprite de 1bpp con color 3 y 0 ) + #03 .Pantalla/sprite DEO + + #0008 ADD2 ( incrementar x ) + GTH2k ( ¿es ese ancho mayor que x? o también, ¿es x menor que el ancho? ) + ,&bucle-x JCN ( salta si x es menor que el límite ) + POP2 POP2 ( eliminar x y el límite ) + + #0008 ADD2 ( ) + GTH2k ( ¿es ese límite mayor que y? o también, ¿es `y` menor que el límite? ) + ,&bucle-y JCN ( salta si `y` es menor que el límite ) + POP2 POP2 ( eliminar `y` y el límite ) +RTN +``` + +que podemos llamar simplemente desde nuestra subrutina de inicialización: + +``` +;dibuja-fondo JSR2 +``` + +=> ./img/screenshot_uxn-background-full.png captura de pantalla que muestra la pantalla de varvara cubierta de líneas diagonales excepto por un margen en la parte superior e inferior. + +¡lindo! + +en el {tutorial de uxn apéndice a} puedes encontrar una discusión detallada de cómo generalizar un procedimiento como éste en una subrutina dibuja-tiles que dibuje un rectángulo arbitrario rellenado con un tile dado. + +se habla de varias posibilidades para usar uxntal de esa manera abstracta: yo diría que es muy interesante, pero está definitivamente fuera del alcance para hacer el juego :) + +# las palas + +podemos pensar en las dos palas del juego como dos rectángulos, cada uno con sus propias coordenadas x e y, y ambos con la misma anchura y altura. + +la coordenada x de cada pala puede ser constante, y la coordenada y debe ser de seguro una variable. + +en esta parte veremos como dibujar las palas en base a estos parámetros, y también recapitularemos como cambiar sus coordenadas `y` con el controlador. + +## dibujar las palas multi-tile + +¡quiero usar las palas como ejemplo de cómo dibujar un sprite compuesto por múltiples tiles! + +### los datos + +he utilizado nasu para dibujar una pala compuesta por tiles de 2x3 en modo 2bpp. las numeraré de izquierda a derecha y de arriba a abajo de la siguiente manera + +``` +0 1 +2 3 +4 5 +``` + +=> https://100r.co/site/nasu.html 100R - nasu + +los datos resultantes son los siguientes: + +``` +@pala + &tile0 [ 3f 7f e7 c3 c3 c3 c3 00 00 18 3c 3c 3c 3c 3c ] + &tile1 [ fc fe ff ff ff ff ff 00 00 00 00 06 06 ] + &tile2 [ c3 c3 c3 e7 ff ff ff 3c 3c 3c 18 00 00 00 ] + &tile3 [ ff ff ff ff ff ff 06 06 06 06 06 06 ] + &tile4 [ ff ff ff ff ff 7f 3f 00 00 00 00 00 00 ] + &tile5 [ ff ff ff ff ff fe fc 06 06 06 06 1e 3c 00 ] +``` + +se pueden obtener estos números leyendo la notación hexadecimal en nasu en la parte superior derecha, primero la columna de la izquierda y luego la de la derecha, o utilizando una herramienta como hexdump con el archivo chr correspondiente: + +``` +$ hexdump -C pong.chr +``` + +he dibujado el sprite usando el modo de mezcla 85 como indica nasu, pero lo cambiaré a c5 para dibujarlo en el primer plano. + +### subrutina de dibujo de la pala + +construyamos una subrutina que dibuje las 6 fichas de la pala en el orden correspondiente. + +podríamos tener la subrutina recibiendo como argumentos la posición x e y de su esquina superior izquierda: + +``` +@dibuja-pala ( x^ y^ -- ) +``` + +pero añadamos también un byte de color para el byte del sprite: + +``` +@dibuja-pala ( x^ y^ color -- ) +``` + +recordemos que estamos utilizando la convención de añadir un signo de intercalación (^) después del nombre de un valor para indicar que es un corto, y un asterisco (*) para indicar que es un corto que funciona como un puntero (es decir, una dirección en la memoria del programa) + +por un lado esta segunda versión nos permitiría cambiar de color cuando, por ejemplo, le demos a la pelota, pero lo más importante es que esto nos permitirá limpiar la pala antes de moverla, como hemos hecho en días anteriores. + +en principio la subrutina debería ser sencilla: tenemos que establecer las coordenadas x e y de cada una de las fichas, relativas a las coordenadas x e y dadas, y dibujarlas con el color dado. + +hay muchas maneras de hacerlo, dependiendo del gusto. + +podríamos por ejemplo dibujar los tiles en el siguiente orden, con las siguientes operaciones: + +* dibujar el tile 0, luego añadir 8 a x +* dibujar el tile 1, luego añadir 8 a y +* dibujar el tile 3, luego restar 8 a x +* dibujar el tile 2, luego añadir 8 a y +* dibujar el tile 4, luego añadir 8 a x +* dibujar el tile 5 + +o podríamos hacerlo de forma más tradicional: + +* dibujar el tile 0, luego añadir 8 a x +* dibujar el tile 1, luego restar 8 a x, y añadir 8 a y +* dibujar el tile 2, luego añadir 8 a x +* dibujar el tile 3, luego restar 8 a x, y añadir 8 a y +* dibujar el tile 4, luego añadir 8 a x +* dibujar el tile 5 + +en lugar de restar podríamos recuperar x de la pila de retorno, o de una variable relativa. + +una posible ventaja de ir en orden es que podemos incrementar la dirección del sprite en 10 (16 en decimal) para llegar a la dirección del siguiente tile. para esto, y/o para los cambios de coordenadas, podemos aprovechar el auto byte de pantalla. + +sin embargo, en este caso voy a ir por la primera opción, y voy a establecer manualmente la dirección para cada tile. + +adicionalmente, guardaré el color en la pila de retorno: + +``` +@dibuja-pala ( x^ y^ color -- ) + ( guardar color ) + STH + + ( establecer `y` y x iniciales ) + .Pantalla/y DEO2 + .Pantalla/x DEO2 + + ( dibujar tile 0 ) + ;pala/tile0 .Pantalla/direc DEO2 + ( copiar color de la pila de retorno: ) + STHkr .Pantalla/sprite DEO + + ( añadir 8 a x: ) + .Pantalla/x DEI2 #0008 ADD2 .Pantalla/x DEO2 + + ( dibujar tile 1 ) + ;pala/tile1 .Pantalla/direc DEO2 + STHkr .Pantalla/sprite DEO + + ( añadir 8 a y: ) + .Pantalla/y DEI2 #0008 ADD2 .Pantalla/y DEO2 + + ( dibujar tile 3 ) + ;pala/tile3 .Pantalla/direc DEO2 + STHkr .Pantalla/sprite DEO + + ( sub 8 a x: ) + .Pantalla/x DEI2 #0008 SUB2 .Pantalla/x DEO2 + + ( dibujar tile 2 ) + ;pala/tile2 .Pantalla/direc DEO2 + STHkr .Pantalla/sprite DEO + + ( añadir 8 a y: ) + .Pantalla/y DEI2 #0008 ADD2 .Pantalla/y DEO2 + + ( dibujar tile 4 ) + ;pala/tile4 .Pantalla/direc DEO2 + STHkr .Pantalla/sprite DEO + + ( añadir 8 a x: ) + .Pantalla/x DEI2 #0008 ADD2 .Pantalla/x DEO2 + + ( dibujar tile 5 ) + ;pala/tile5 .Pantalla/direc DEO2 + ( obtener y no mantener el color de la pila de retorno: ) + STHr .Pantalla/sprite DEO +RTN +``` + +¡eso es todo! + +ahora podemos llamarlo, por ejemplo, de la siguiente manera y obtener nuestra pala dibujada: + +``` +#0008 #0008 #c5 ;dibuja-pala JSR2 +``` + +=> ./img/screenshot_uxn-pong-paddle.png captura de pantalla de la pala dibujada sobre el fondo + +es posible considerar formas más eficientes de dibujarla. por ejemplo, podríamos tener un dibuja-sprite generalizado que reciba la dirección inicial de un conjunto de tiles, y la anchura y altura en términos de número de tiles: + +``` +@dibuja-sprite ( x^ y^ ancho alto direc* color ) +``` + +crear eso podría ser un buen ejercicio para probar! en este caso me quedaré con el método manual. + +lo bueno de que este proceso esté en una subrutina es que podemos "olvidarnos" de su funcionamiento interno y simplemente usarlo :) + +## variables y constantes para las palas + +reservemos un espacio en la página cero para las coordenadas x e y de cada pala. + +``` +( página cero ) +|0000 + @izquierda [ &x $2 &y $2 ] + @derecha [ &x $2 &y $2 ] +``` + +mencionamos al principio que la coordenada x es constante; sin embargo, si la convertimos en una variable entonces podemos asignar dinámicamente la posición x de las palas (especialmente la derecha) dependiendo del tamaño de la pantalla. + +podemos tener un par de macros para mantener las dimensiones y el color de las palas para usarlas después: + +``` +%ANCHO-PALA { #0010 } ( 2 tiles ) +%ALTO-PALA { #0018 } ( 3 tiles ) +%COLOR-PALA { #c5 } +``` + +un margen para separar las palas de los bordes podría ser agradable también: + +``` +%MARGEN { #0010 } +``` + +por último, recuperemos nuestra macro MITAD2 de días anteriores: + +``` +%MITAD2 { #01 SFT2 } ( corto -- corto/2 ) +``` + +### iniciar posiciones + +ahora podemos iniciar las posiciones de las palas. + +para la x izquierda, podemos simplemente asignar un valor constante: + +``` +MARGEN .izquierda/x STZ2 +``` + +para la x derecha, podemos restar el margen y el ancho de la pala del ancho de la pantalla: + +``` +.Pantalla/ancho DEI2 +MARGEN SUB2 ANCHO-PALA SUB2 +.derecha/x STZ2 +``` + +para centrar las coordenadas `y` podemos restar la altura de la pala a la altura de la pantalla, y luego dividir entre dos: + +``` +.Pantalla/alto DEI2 ALTO-PALA SUB2 MITAD2 +DUP2 +.izquierda/y STZ2 +.derecha/y STZ2 +``` + +### dibujar palas + +para dibujar cada pala, podemos hacer el siguiente procedimiento dentro de nuestro vector de pantalla en-cuadro: + +``` +( dibujar palas ) +.izquierda/x LDZ2 .izquierda/y LDZ2 COLOR-PALA ;dibuja-pala JSR2 +.derecha/x LDZ2 .derecha/y LDZ2 COLOR-PALA ;dibuja-pala JSR2 +``` + +### el programa hasta ahora + +omitiendo la definición de las subrutinas dibuja-fondo y dibuja-pala, y como forma de tener un punto de comprobación, ahora mismo nuestro programa tendría el siguiente aspecto: + +=> ./img/screenshot_uxn-pong-paddles.png captura de pantalla de las dos palas, centradas verticalmente y con el mismo margen respecto a los lados + +``` +( hola-pong.tal ) + +( dispositivos ) +|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ] +|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &auto $1 &pad $1 &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ] +|80 @Controlador [ &vector $2 &boton $1 &tecla $1 ] + +( macros ) +%RTN { JMP2r } +%MITAD2 { #01 SFT2 } ( corto -- corto/2 ) + +( constantes ) +%ANCHO-PALA { #0010 } ( 2 tiles ) +%ALTO-PALA { #0018 } ( 3 tiles ) +%COLOR-PALA { #c5 } +%MARGEN { #0010 } +%MARGEN-PARED { #0010 } ( margen en la parte superior e inferior ) + +( página cero ) +|0000 + @izquierda [ &x $2 &y $2 ] + @derecha [ &x $2 &y $2 ] + +( programa principal ) +|0100 +@configuracion + ( establecer los colores del sistema ) + #2ce9 .Sistema/r DEO2 + #01c0 .Sistema/g DEO2 + #2ce5 .Sistema/b DEO2 + + ( establecer vector de pantalla ) + ;en-cuadro .Pantalla/vector DEO2 + + ( dibujar fondo ) + ;dibuja-fondo JSR2 + + ( iniciar palas ) + MARGEN .izquierda/x STZ2 + .Pantalla/ancho DEI2 + MARGEN SUB2 ANCHO-PALA SUB2 + .derecha/x STZ2 + + .Pantalla/alto DEI2 ALTO-PALA SUB2 + MITAD2 DUP2 + .izquierda/y STZ2 + .derecha/y STZ2 + +BRK + +@en-cuadro ( -> ) + ( dibujar pala ) + .izquierda/x LDZ2 .izquierda/y LDZ2 COLOR-PALA ;dibuja-pala JSR2 + .derecha/x LDZ2 .derecha/y LDZ2 COLOR-PALA dibuja-pala JSR2 +BRK +``` + +## movimiento de la pala + +para el movimiento de la pala podemos volver a los ejemplos anteriores de mover un sprite. el proceso que hemos seguido es: + +* borrar el sprite de la posición actual +* actualizar la posición +* dibujar el sprite en la nueva posición + +### borrar o dibujar sprite + +ya tenemos el proceso para dibujar nuestras palas: + +``` +( dibujar palas ) +.izquierda/x LDZ2 .izquierda/y LDZ2 COLOR-PALA ;dibuja-pala JSR2 +.derecha/x LDZ2 .derecha/y LDZ2 COLOR-PALA ;dibuja-pala JSR2 +``` + +para borrarlos, podemos hacer lo mismo pero usando un byte del sprite correspondiente a borrar el tile en el primer plano: + +``` +( borrar palas ) +.izquierda/x LDZ2 .izquierda/y LDZ2 COLOR-BORRAR ;dibuja-pala JSR2 +.derecha/x LDZ2 .derecha/y LDZ2 COLOR-BORRAR ;dibuja-pala JSR2 +``` + +donde COLOR-BORRAR en este caso sería: + +``` +%COLOR-BORRAR { #40 } ( limpiar el sprite del primer plano ) +``` + +¡este es un buen recordatorio para revisar las tablas de los bytes de los sprites en el {tutorial de uxn día 2}! + +### actualizar posición + +para actualizar la posición de nuestras palas, podemos recurrir al ejemplo hola-sprite-enmovimiento.tal del {tutorial de uxn día 4}. + +podemos usar las flechas arriba y abajo para cambiar la posición de la pala izquierda, y los botones ctrl y alt (A y B) para cambiar la posición de la pala derecha. + +podemos tener una macro para definir la velocidad de la pala, es decir, cuánto sumaremos o restaremos al mover cada cuadro: + +``` +%VEL-PALA { #0001 } +``` + +todo esto puede ir dentro de su propia subrutina para facilitar la lectura: + +``` +@actualiza-palas ( -- ) + &izquierda + ( pala izquierda: botones arriba y abajo ) + .Controlador/boton DEI + DUP #10 AND ( comprobar bit para arriba ) + ,&izquierda-arriba JCN + DUP #20 AND ( comprobar bit para abajo ) + &izquierda-abajo JCN + + &derecha JMP ( salta si no se ha pulsado ninguno de los dos ) + + &izquierda-arriba + .izquierda/y LDZ2 VEL-PALA SUB2 .izquierda/y STZ2 + ,&derecha JMP + &izquierda-abajo + .izquierda/y LDZ2 VEL-PALA ADD2 .izquierda/y STZ2 + ,&derecha JMP + + &derecha + ( pala derecha: botones ctrl/A y alt/B ) + DUP #01 AND ( comprobar bit para A ) + ,&derecha-arriba JCN + DUP #02 AND ( comprobar bit para B ) + &derecha-abajo JCN + + &fin JMP ( salta si no se ha pulsado ninguno de los dos ) + + &derecha-arriba + .derecha/y LDZ2 VEL-PALA SUB2 .derecha/y STZ2 + ,&fin JMP + &derecha-abajo + .derecha/y LDZ2 VEL-PALA ADD2 .derecha/y STZ2 + + &fin + POP ( hacer POP al valor duplicado del botón ) +RTN +``` + +### procedimiento completo + +integrando todo, nuestra subrutina en el marco se vería como lo siguiente. + +¡ahora somos capaces de mover nuestras palas! + +``` +@en-cuadro ( -> ) + ( borrar palas ) + .izquierda/x LDZ2 .izquierda/y LDZ2 COLOR-BORRAR ;dibuja-pala JSR2 + .derecha/x LDZ2 .derecha/y LDZ2 COLOR-BORRAR ;dibuja-pala JSR2 + + ( actualizar palas ) + ;actualiza-palas JSR2 + + ( dibujar palas ) + .izquierda/x LDZ2 .izquierda/y LDZ2 COLOR-PALA ;dibuja-pala JSR2 + .derecha/x LDZ2 .derecha/y LDZ2 COLOR-PALA ;dibuja-pala JSR2 +BRK +``` + +nota que somos capaces de mover las palas más allá de los límites de la pantalla. + +te invito a que modifiques la subrutina actualiza-palas para que haya un límite en el movimiento de las palas. en el {tutorial de uxn día 4} discutimos algunas posibles estrategias para lograrlo :) + +# la pelota + +¡ahora vamos a poner la pelota en marcha! + +aquí trabajaremos de nuevo con un sprite multi-tile dibujado en relación a las variables x e y para su esquina superior izquierda. + +adicionalmente, usaremos esta sección para hablar de las estrategias para la detección de colisiones, con las paredes y las palas. + +## dibujando la pelota + +usé nasu para dibujar una pelota compuesta por tiles de 2x2 2bpp, ordenados de la siguiente manera: + +``` +0 1 +2 3 +``` + +estos son sus datos: + +``` +@pelota-sprite + &tile0 [ 03 0f 1f 39 70 70 f9 ff 00 00 00 06 0f 0f 06 00 ] + &tile1 [ c0 f0 f8 fc fe ff ff 00 00 00 08 0c 06 06 ] + &tile2 [ ff ff 7f 7f 3f 1f 0f 03 00 00 00 00 18 0f 01 00 ] + &tile3 [ ff ff fe fc f8 f0 c0 06 06 0c 1c 38 f0 c0 00 ] +``` + +podemos definir un par de macros para referirse a sus parámetros: + +``` +%TAM-PELOTA { #0010 } ( tamaño: 2 tiles por lado ) +%COLOR-PELOTA { #c5 } +``` + +### subrutina para dibujar la pelota + +como vamos a dibujar una sola pelota, podemos escribir su subrutina de dibujo de manera que tome sus coordenadas de la página cero en lugar de obtenerlas como argumentos en la pila. + +en nuestra página cero podemos definir las etiquetas para las coordenadas: + +``` +@pelota [ &x $2 &y $2 ] +``` + +y luego en nuestra subrutina de configuración podemos asignarles valores, por ejemplo, en el centro de la pantalla: + +``` +( dentro de configuración ) +( iniciar la pelota ) +.Pantalla/ancho DEI2 TAM-PELOTA SUB2 +MITAD2 +.pelota/x STZ2 + +.Pantalla/alto DEI2 TAM-PELOTA SUB2 +MITAD2 +.pelota/y STZ2 +``` + +las coordenadas están listas, así que ahora podemos usarlas dentro de nuestra subrutina. + +hagamos que la subrutina reciba el color como argumento, para poder limpiar la pelota como hacemos con las palas: + +``` +@dibuja-pelota ( color -- ) + ( fijar x e y iniciales ) + .pelota/x LDZ2 .Pantalla/x DEO2 + .pelota/y LDZ2 . pantalla/y DEO2 + + ( dibujar tile 0 ) + ;pelota-sprite/tile0 .Pantalla/direc DEO2 + ( el byte de color ya estaba en la pila ) + DUP .Pantalla/sprite DEO + + ( mover a la derecha ) + .Pantalla/x DEI2 #0008 ADD2 .Pantalla/x DEO2 + + ( dibujar tile 1 ) + ;pelota-sprite/tile1 .Pantalla/direc DEO2 + DUP .Pantalla/sprite DEO + + ( mover hacia abajo ) + .Pantalla/y DEI2 #0008 ADD2 .Pantalla/y DEO2 + + ( dibujar tile 3 ) + ;pelota-sprite/tile3 .Pantalla/direc DEO2 + DUP .Pantalla/sprite DEO + + ( mover a la izquierda ) + .Pantalla/x DEI2 #0008 SUB2 .Pantalla/x DEO2 + + ( dibujar tile 2 ) + ;pelota-sprite/tile2 .Pantalla/direc DEO2 + .Pantalla/sprite DEO +RTN +``` + +para dibujarla, sólo tendríamos que hacer: + +``` +( dibujar pelota ) +COLOR-PELOTA ;dibuja-pelota JSR2 +``` + +=> ./img/screenshot_uxn-pong-paddles-and-ball.png captura de la pantalla mostrando las palas en su posición horizontal pero a diferentes alturas, y la pelota completamente centrada en la pantalla. + +## movimiento de la pelota + +para el movimiento de la pelota, seguiremos la misma estructura que antes: + +* despejar la pelota en la posición actual +* actualizar su posición +* dibujar la pelota en la nueva posición + +se vería algo como lo siguiente, y podría sentarse a lo largo de los procedimientos equivalentes para las palas dentro de la subrutina en-cuadro: + +``` +( dentro de en-marco ) +( borrar pelota ) +COLOR-BORRAR ;dibuja-pelota JSR2 + +( actualizar pelota ) +;actualiza-pelota JSR2 + +( dibujar pelota ) +COLOR-PELOTA ;dibuja-pelota JSR2 +``` + +ahora vamos a discutir cómo construir esa subrutina actualiza-pelota :) + +### contabilizando el cambio de dirección + +además de nuestras variables para llevar la cuenta de la posición de la pelota, deberíamos poder llevar la cuenta de la dirección por-eje en la que se mueve. + +un enfoque podría ser tener una bandera para cada x e y que indique si debemos incrementarlos o disminuirlos. + +otro enfoque podría ser tener una variable de velocidad para cada x e y, que se cambia de acuerdo a la dirección que queremos que la pelota vaya. + +utilizaremos este último enfoque con la velocidad, ya que nos ayudará a discutir algunas ventajas de la aritmética de enteros sin signo. + +incluimos estas variables en nuestra página cero, complementando las x e y que ya teníamos: + +``` +@pelota [ &x $2 &y $2 &vel-x $2 &vel-y $2 ] +``` + +### diferentes direcciones + +si, por ejemplo, iniciamos vel-x con 1 + +``` +#0001 .pelota/vel-x STZ2 +``` + +podemos hacer que la pelota se mueva hacia la derecha haciendo: + +``` +( dentro de actualiza-pelota ) +.pelota/vel-x LDZ2 ( obtener vel-x ) +.pelota/x LDZ2 ( obtener x ) +ADD2 ( sumar ambas cosas ) +.pelota/x STZ2 ( almacenar nueva x ) +``` + +para moverse hacia la izquierda, podríamos pensar que deberíamos sustituir ADD2 por SUB2. y sí, podríamos hacerlo. + +pero, dejando nuestro código como está ahora: ¿hay algún valor de velocidad-x que haga que x se haga más pequeño al sumarlos? + +en otros contextos, uno podría decir, ¡"-1"! + +pero no hemos utilizado signos negativos en uxn; ¡no podemos! + +entonces, ¿existe un valor positivo de vel-x que haga que x se reduzca al sumarlos? + +¡normalmente pensaríamos que no lo hay y que la pregunta no tiene sentido! + +sin embargo, aquí estamos limitados por 8 o 16 bits. ¿y qué implica eso? + +por ejemplo, si tenemos el número ffff (16 bits, todos son unos), y sumamos 0001, ¿qué obtenemos? + +``` + 1111 1111 1111 1111 + + 0000 0000 0000 0001 + --------------------- + 1 0000 0000 0000 0000 +``` + +de acuerdo, es un número mayor, pero el 1 de la izquierda queda fuera de los 16 bits. en otros contextos se llamaría bit de acarreo. + +en uxn, el resultado de sumar ffff y 0001 es 0000: decimos que estamos desbordando los 16 bits. + +veámoslo al revés: si tenemos 0001, y sumamos ffff, obtenemos 0000, ¡es decir, 1 menos que 1! + +si tenemos 0002, y añadimos ffff: + +``` + 0000 0000 0000 0010 + + 1111 1111 1111 1111 + -------------------- + 1 0000 0000 0000 0001 +``` + +¡obtenemos 0001, que es 1 menos que 2! + +en general, si sumamos ffff a un número de 16 bits, obtendremos un valor que es 1 menos que él mismo. + +¡por lo tanto podemos pensar que ffff es como un "-1"! + +para obtener otros "números negativos", observemos lo siguiente: si restamos 1 a ffff, obtenemos fffe. ¿qué ocurre si lo sumamos a 2? + +``` + 0000 0000 0000 0010: 0002 + + 1111 1111 1111 1110: fffe + --------------------- + 1 0000 0000 0000 0000: 0000 +``` + +obtenemos 0! fffe funciona efectivamente como "-2"! + +podríamos continuar así obteniendo más y más números "negativos" que funcionan gracias al tamaño restringido de la memoria del ordenador. + +volviendo a nuestro código, si inicializamos nuestra velocidad con: + +``` +#ffff .pelota/vel-x STZ2 +``` + +y luego usamos exactamente el mismo código para actualizar la posición: + +``` +( dentro de actualiza-pelota ) +.pelota/vel-x LDZ2 ( obtener vel-x ) +.pelota/x LDZ2 ( obtener x ) +ADD2 ( sumar ambas cosas ) +.pelota/x STZ2 ( almacenar nueva x ) +``` + +¡habremos disminuido la posición por 1! + +podría tener sentido establecer estos valores como macros: + +``` +%PELOTA-VEL-POS { #0001 } ( +1 ) +%PELOTA-VEL-NEG { #ffff } ( -1 ) +``` + +### implementando el movimiento de la pelota + +basándonos en lo que acabamos de discutir, podemos empezar nuestra subrutina de actualización de la pelota con lo siguiente: + +``` +@actualiza-pelota ( -- ) + ( obtener velocidad-x y x ) + .pelota/vel-x LDZ2 .pelota/x LDZ2 + ADD2 ( sumar ambas cosas ) + .pelota/x STZ2 ( guarda la nueva x ) + + ( obtener velocidad-y e y ) + .pelota/vel-y LDZ2 .pelota/y LDZ2 + ADD2 ( sumar ambas cosas ) + .pelota/y STZ2 ( almacenar nueva y ) +RTN +``` + +si complementamos nuestra rutina de configuración con las velocidades iniciales, podremos ver cómo se mueve la pelota: + +``` +( dentro de configuración ) +( iniciar pelota ) +.Pantalla/ancho DEI2 TAM-PELOTA SUB2 +MITAD2 .pelota/x STZ2 +.Pantalla/alto DEI2 TAM-PELOTA SUB2 +MITAD2 .pelota/y STZ2 + +( iniciar la velocidad de la pelota ) +PELOTA-VEL-POS .pelota/vel-x STZ2 +PELOTA-VEL-POS .pelota/vel-y STZ2 +``` + +woohoo! se mueve, pero de momento sale volando :) + +## colisiones con las paredes + +hemos definido la forma general de actualizar la posición de la pelota dada su velocidad en x e y. + +¡ahora veamos cómo implementar el clásico "rebote"! + +primero, empecemos con las paredes en la parte superior e inferior de la pantalla; recordando que hay un margen (MARGEN-PARED) entre el borde real de la pantalla, y las paredes. + +para realizar estas detecciones de colisión, tendríamos que comprobar sólo la coordenada y de la pelota. + +como siempre, hay muchas maneras de lograr esto. una podría ser: + +* comprobar si la pelota está golpeando alguna de las paredes +* si lo hace, entonces invierte la velocidad + +otra: + +* comprobar si la pelota está golpeando la pared superior +* si lo hace, entonces pon una velocidad positiva +* si no es así, comprueba si la pelota golpea la pared inferior +* si lo hace, entonces establece una velocidad negativa + +en otros lenguajes probablemente sea más fácil escribir lo primero, pero aquí nos quedaremos con lo segundo: por claridad y por la forma en que tendremos que hacer las comprobaciones. + +¡en cualquier caso, puede ser un buen ejercicio para ti intentar averiguar cómo "invertir" la velocidad con una sola operación aritmética o lógica! + +pista: mira de nuevo las máscaras a nivel de bit discutidas en el {tutorial de uxn día 3} :) + +### pared superior + +si la pelota golpea la pared superior, significa que su coordenada y es menor que la coordenada y de la pared. + +considerando que hay un margen en la parte superior, podemos hacer esta comprobación de la siguiente manera: + +``` +( dentro de actualiza-pelota ) + &verif-pared-sup + .pelota/y LDZ2 + MARGEN-PARED + LTH2 ( ¿es pelota-y menos que el margen? ) + ,&establecer-vel-pos JCN + ,&verif-pared-inf JMP + + &establecer-vel-pos + PELOTA-VEL-POS .pelota/vel-y STZ2 + &continuar JMP + &verif-pared-inf +``` + +### pared inferior + +aquí el procedimiento sería similar, pero considerando el tamaño de la pelota. + +queremos saber si la coordenada y, más el tamaño de la pelota, es mayor que la coordenada y de la pared inferior. + +la coordenada y de la pared inferior sería la altura de la pantalla, menos el margen de la pared: + +``` +(actualización del interior de la pelota ) + &verif-pared-inf + .pelota/y LDZ2 TAM-PELOTA ADD2 ( y + tamaño de la pelota ) + .Pantalla/alto DEI2 + MARGEN-PARED SUB2 ( altura - margen ) + GTH2 ( ¿es la pelota-y mayor que la pared-y? ) + ,&establecer-vel-neg JCN + &continuar JMP + + &establecer-vel-neg + PELOTA-VEL-NEG .pelota/vel-y STZ2 + &continuar +``` + +### código de actualización de la pelota hasta ahora + +nuestra subrutina de actualización de la pelota tiene el siguiente aspecto ahora mismo: + +``` +@actualiza-pelota ( -- ) + ( actualizar x ) + ( obtener velocidad-x y x ) + .pelota/vel-x LDZ2 .pelota/x LDZ2 + ADD2 ( sumar ambas cosas ) + .pelota/x STZ2 ( guardar la nueva x ) + + ( actualizar y ) + ( obtener velocidad-y e y ) + .pelota/vel-y LDZ2 .pelota/y LDZ2 + ADD2 ( sumar ambas cosas ) + .pelota/y STZ2 ( almacenar nueva y ) + + ( comprueba las colisiones con las paredes ) + &verif-pared-sup + .pelota/y LDZ2 + MARGEN-PARED + LTH2 ( ¿es la pelota-y menor que el margen? ) + ,&establecer-vel-pos JCN + ,&verif-pared-inf JMP + + &establecer-vel-pos + PELOTA-VEL-POS .pelota/vel-y STZ2 + &continuar JMP + + &verif-pared-inf + .pelota/y LDZ2 TAM-PELOTA ADD2 ( y + tamaño de la pelota ) + .Pantalla/alto DEI2 + MARGEN-PARED SUB2 ( altura - margen ) + GTH2 ( ¿es la pelota-y mayor que la pared-y? ) + ,&establecer-vel-neg JCN + &continuar JMP + + &establecer-vel-neg + PELOTA-VEL-NEG .pelota/vel-y STZ2 + &continuar +RTN +``` + +puedes probarlo usando diferentes velocidades iniciales dentro de la configuración. ¡la pelota debería estar rebotando en la parte superior e inferior ahora! :) + +## colisiones con las palas + +¡trabajemos con lo que acabamos de hacer, y adaptémoslo para rebotar con las palas! + +### pala izquierda + +en primer lugar, podemos identificar si la coordenada x de la pelota estaría golpeando la pala izquierda. + +para ello, podemos comprobar si x es menor que la suma del margen y el ancho de la pala. + +``` +( dentro de actualiza-pelota ) + &verif-pala-izq + .pelota/x LDZ2 + MARGEN ANCHO-PALA ADD2 + LTH2 ( ¿es la pelota-x menor que el margen + el ancho-de-pala? ) + ,&x-en-izquierda JCN + ,&verif-pala-der JMP + + &x-en-izquierda + ( ... ) + + &verif-pala-der +``` + +una vez que sabemos que eso es cierto, podemos ver si la pelota está dentro del alcance vertical de la pala; la coordenada y de la pelota tiene que estar dentro de un cierto rango relativo a la coordenada y de la pelota. + +en especifico, si queremos que la pelota pueda rebotar cuando cualquier parte de la pelota golpee cualquier parte de la pala, la coordenada y de la pelota tiene que ser: + +* mayor que la coordenada y de la pala menos la altura AND de la pelota +* menor que la coordenada y de la pala más la altura de la pala + +si esas dos condiciones se cumplen, entonces podemos establecer una velocidad positiva para x: + +``` +( dentro de actualiza-pelota ) + &x-en-izquierda + .pelota/y LDZ2 DUP2 + .izquierda/y LDZ2 TAM-PELOTA SUB2 GTH2 ( primera bandera ) STH + .izquierda/y LDZ2 ALTO-PALA ADD2 LTH2 ( segunda bandera ) + STHr ( recupera la primera bandera ) + AND ( hacer AND en ambas banderas ) + &rebote-izquierda JCN +``` + +donde rebote-izquierda sería: + +``` + &rebote-izquierda + PELOTA-VEL-POS .pelota/vel-x STZ2 + ,&fin JMP +``` + +¿y qué pasa si no se cumplen las dos condiciones a la vez? + +podemos dejar que la pelota siga moviéndose, pero comprobando que no ha cruzado la pared izquierda, comparando con 0000. + +todo el código x-en-izquierda terminaría pareciendo: + +``` +( dentro de actualiza-pelota ) + &x-en-izquierda + .pelota/y LDZ2 DUP2 + .izquierda/y LDZ2 TAM-PELOTA SUB2 GTH2 ( primera bandera ) STH + .izquierda/y LDZ2 ALTO-PALA ADD2 LTH2 ( segunda bandera ) + STHr ( recupera la primera bandera ) + AND ( hacer AND en ambas banderas ) + ,&rebote-izquierda JCN + + .pelota/x LDZ2 #0000 NEQ2 ( ¿ha llegado a la pared? ) + ,&fin JCN + + &reset-izquierda + ( aquí puedes aumentar la puntuación de + la pala derecha ) + ;reset JSR2 + ,&fin JMP + + &rebote-izquierda + PELOTA-VEL-POS .pelota/vel-x STZ2 + ,&fin JMP + + &verif-pala-der +``` + +fin sería una etiqueta al final de la subrutina, y reset es una subrutina de la que hablaremos más adelante. + +esta aproximación de comparar con 0000 es la más fácil, pero ten en cuenta que podría no funcionar si cambias la velocidad de la pelota: podría ocurrir que cruzara la pared pero con una coordenada x que nunca fuera igual a 0. + +realmente no podemos comprobar si la coordenada x es menor que 0, porque como hemos comentado anteriormente, eso sería en realidad un número cercano a ffff. + +si comprobáramos que la coordenada x es menor que ffff, ¡entonces cada valor posible activaría la bandera de comparación! + +este puede ser otro buen ejercicio para ti: ¿cómo comprobarías si la pelota ha cruzado la pared izquierda independientemente de su velocidad? + +### pala derecha + +para la pala derecha haremos lo mismo que arriba, pero cambiando las comparaciones relativas a la coordenada x de la pelota: usaremos el ancho de la pantalla como referencia para la pared derecha, y a partir de ahí le restaremos el margen y el ancho. + +``` + &verif-pala-der + .pelota/x LDZ2 TAM-PELOTA ADD2 ( pelota-x + tamaño-pelota ) + .Pantalla/ancho DEI2 MARGEN SUB2 ANCHO-PALA SUB2 + GTH2 ( ¿es la coordenada derecha de la pelota mayor que el ancho de la pantalla - margen - ancho de la pala? ) + ,&x-en-derecha JCN + &fin JMP + + &x-en-derecha + .pelota/y LDZ2 DUP2 + .derecha/y LDZ2 TAM-PELOTA SUB2 GTH2 ( primera bandera ) STH + .derecha/y LDZ2 ALTO-PALA ADD2 LTH2 ( segunda bandera ) + STHr ( recupera la primera bandera ) + AND ( hacer AND en ambas banderas ) + ,&rebote-derecha JCN + + .pelota/x LDZ2 + .Pantalla/ancho DEI2 NEQ2 ( ¿ha llegado a la pared? ) + ,&fin JCN + + &reset-derecha + ( aquí puedes aumentar la puntuación + de la pala izquierda ) + ;reset JSR2 + ,&fin JMP + + &rebote-derecha + PELOTA-VEL-NEG .pelota/vel-x STZ2 + ,&fin JMP + + &fin +RTN +``` + +¡eso debería ser todo! ¡puedes encontrar la subrutina de actualización de la pelota completa a continuación! + +¡para poder ensamblar y ejecutar el juego, vamos a definir la subrutina reset! + +## reset + +aquí solo definiremos una subrutina de reinicio o "reset" que devuelva la pelota al centro de la pantalla sin alterar su velocidad: + +``` +@reset ( -- ) + ( iniciar pelota ) + .Pantalla/ancho DEI2 TAM-PELOTA SUB2 + MITAD2 .pelota/x STZ2 + .Pantalla/alto DEI2 TAM-PELOTA SUB2 + MITAD2 .pelota/y STZ2 +RTN +``` + +sería interesante tener algún mecanismo para cambiar también la velocidad: tal vez basado en el conteo de cuadros, en la posición de las palas, o cualquier otra cosa que elijas. + +# el programa completo + +aquí está todo el código que hemos escrito hoy: + +=> ./img/screencap_uxn-pong.gif animación que muestra el pong en acción: las palas se mueven, la pelota rebota en las paredes superior e inferior y en las palas, y la pelota se reinicia desde el centro cuando la pelota golpea cualquier lado. + +## configuración + +``` +( hola-pong.tal ) + +( dispositivos ) +|00 @Sistema [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ] +|20 @Pantalla [ &vector $2 &ancho $2 &alto $2 &auto $1 &pad $1 + &x $2 &y $2 &direc $2 &pixel $1 &sprite $1 ] +|80 @Controlador [ &vector $2 &boton $1 &tecla $1 ] + +( macros ) +%RTN { JMP2r } +%MITAD2 { #01 SFT2 } ( corto -- corto/2 ) +%DOBLE2 { #10 SFT2 } + +( constantes ) +%ANCHO-PALA { #0010 } ( 2 tiles ) +%ALTO-PALA { #0018 } ( 3 tiles ) +%COLOR-PALA { #c5 } +%VEL-PALA { #0001 } +%TAM-PELOTA { #0010 } ( 2 tiles ) +%COLOR-PALA { #c5 } +%PELOTA-VEL-POS { #0001 } +%PELOTA-VEL-NEG { #ffff } +%COLOR-BORRAR { #40 } +%MARGEN { #0010 } ( izquierda y derecha ) +%MARGEN-PARED { #0010 } ( arriba y abajo ) + +( página cero ) +|0000 + @izquierda [ &x $2 &y $2 ] + @derecha [ &x $2 &y $2 ] + @pelota [ &x $2 &y $2 &vel-x $2 &vel-y $2 ] + +( configuración ) +|0100 +@configuracion ( -> ) + ( establecer colores del sistema ) + #2ce9 .Sistema/r DEO2 + #01c0 .Sistema/g DEO2 + #2ce5 .Sistema/b DEO2 + + ( establecer el vector de la pantalla ) + ;en-cuadro .Pantalla/vector DEO2 + + ( dibujar fondo ) + ;dibujar fondo JSR2 + + ( iniciar palas ) + MARGEN .izquierda/x STZ2 + .Pantalla/ancho DEI2 + MARGEN SUB2 ANCHO-PALA SUB2 + .derecha/x STZ2 + + .Pantalla/alto DEI2 ALTO-PALA SUB2 + MITAD2 DUP2 + .izquierda/y STZ2 + .derecha/y STZ2 + + ( iniciar pelota ) + ;reset JSR2 + + ( iniciar velocidad de la pelota ) + PELOTA-VEL-NEG .pelota/vel-x STZ2 + PELOTA-VEL-POS .pelota/vel-y STZ2 + +BRK +``` + +## en-marco + +``` +@en-marco ( -> ) + ( borrar palas ) + .izquierda/x LDZ2 .izquierda/y LDZ2 COLOR-BORRAR ;dibuja-pala JSR2 + .derecha/x LDZ2 .derecha/y LDZ2 COLOR-BORRAR ;dibuja-pala JSR2 + + ( borrar pelota ) + COLOR-BORRAR ;dibuja-pelota JSR2 + + ( actualizar palas ) + ;actualiza-palas JSR2 + + ( actualizar pelota ) + ;actualiza-pelota JSR2 + + ( dibujar palas ) + .izquierda/x LDZ2 .izquierda/y LDZ2 COLOR-PALA ;dibuja-pala JSR2 + .derecha/x LDZ2 .derecha/y LDZ2 COLOR-PALA ;dibuja-pala JSR2 + + ( dibujar pelota ) + COLOR-PELOTA ;dibuja-pelota JSR2 +BRK +``` + + +## reset + +``` +@reset ( -- ) + ( iniciar pelota ) + .Pantalla/ancho DEI2 TAM-PELOTA SUB2 + MITAD2 .pelota/x STZ2 + .Pantalla/alto DEI2 TAM-PELOTA SUB2 + MITAD2 .pelota/y STZ2 +RTN +``` + +## relacionado con la pelota + +### actualiza-pelota + +``` +@actualiza-pelota ( -- ) + ( obtener velocidad-x y x ) + .pelota/vel-x LDZ2 .pelota/x LDZ2 ( obtener x ) + ADD2 ( sumar ambas cosas ) + .pelota/x STZ2 ( guardar la nueva x ) + + ( obtener velocidad-y e y ) + .pelota/vel-y LDZ2 .pelota/y LDZ2 ( obtener y ) + ADD2 ( sumar ambas cosas ) + .pelota/y STZ2 ( guardar la nueva y ) + + ( comprobar colisiones con las paredes ) + &verif-pared-sup + .pelota/y LDZ2 + MARGEN-PARED + LTH2 ( ¿es la pelota-y menor que el margen? ) + ,&establecer-vel-pos JCN + ,&verif-pared-inf JMP + + &establecer-vel-pos + PELOTA-VEL-POS .pelota/vel-y STZ2 + &continuar JMP + + &verif-pared-inf + .pelota/y LDZ2 TAM-PELOTA ADD2 ( y + tamaño-pelota ) + .Pantalla/alto DEI2 MARGEN-PARED SUB2 ( altura - margen ) + GTH2 + ,&establecer-vel-neg JCN + ,&continuar JMP + + &establecer-vel-neg + PELOTA-VEL-NEG .pelota/vel-y STZ2 + &continuar + + ( comprobar colisiones con las palas ) + &verif-pala-izq + .pelota/x LDZ2 + MARGEN ANCHO-PALA ADD2 + LTH2 ( ¿es la pelota-x menor que el margen + ancho-pala? ) + ,&x-en-izquierda JCN + ,&verif-pala-der JMP + + &x-en-izquierda + .pelota/y LDZ2 DUP2 + .izquierda/y LDZ2 TAM-PELOTA SUB2 GTH2 ( primera bandera ) STH + .izquierda/y LDZ2 ALTO-PALA ADD2 LTH2 ( segunda bandera ) + STHr ( recuperar la primera bandera ) + AND ( hacer AND en ambas banderas ) + ,&rebote-izquierda JCN + + .pelota/x LDZ2 #0000 NEQ2 ( ¿ha llegado a la pared? ) + ,&fin JCN + + &reset-izquierda + ( aquí puedes añadir un punto a la pala derecha ) + ;reset JSR2 + ,&fin JMP + + &rebote-izquierda + PELOTA-VEL-POS .pelota/vel-x STZ2 + ,&fin JMP + + &verif-pala-der + .pelota/x LDZ2 TAM-PELOTA ADD2 + .Pantalla/ancho DEI2 MARGEN SUB2 ANCHO-PALA SUB2 + GTH2 ( ¿es pelota-x + tamaño-pelota mayor que la anchura de la pantalla - margen - ancho-pala? ) + ,&x-en-derecha JCN + &fin JMP + + &x-en-derecha + .pelota/y LDZ2 DUP2 + .derecha/y LDZ2 TAM-PELOTA SUB2 GTH2 ( primera bandera ) STH + .derecha/y LDZ2 ALTO-PALA ADD2 LTH2 ( segunda bandera ) + STHr ( recuperar la primera bandera ) + AND ( hacer AND en ambas banderas ) + ,&rebote-derecha JCN + + .pelota/x LDZ2 + .Pantalla/ancho DEI2 NEQ2 ( ¿ha llegado a la pared? ) + ,&fin JCN + + &reset-derecha + ( aquí puedes añadir un punto a la pala izquierda ) + ;reset JSR2 + ,&fin JMP + + &rebote-derecha + PELOTA-VEL-NEG .pelota/vel-x STZ2 + ,&fin JMP + + &fin +RTN +``` + +### dibuja-pelota + +``` +@dibuja-pelota ( color -- ) + ( fijar x e y iniciales ) + .pelota/x LDZ2 .Pantalla/x DEO2 + .pelota/y LDZ2 .Pantalla/y DEO2 + + ( dibujar tile 0 ) + ;pelota/sprite0 .Pantalla/direc DEO2 + ( el byte de color ya estaba en la pila ) + DUP .Pantalla/sprite DEO + + ( mover a la derecha ) + .Pantalla/x DEI2 #0008 ADD2 .Pantalla/x DEO2 + + ( dibujar tile 1 ) + ;pelota-sprite/sprite1 .Pantalla/direc DEO2 + DUP .Pantalla/sprite DEO + + ( mover hacia abajo ) + .Pantalla/y DEI2 #0008 ADD2 .Pantalla/y DEO2 + + ( dibujar tile 3 ) + ;pelota-sprite/tile3 .Pantalla/direc DEO2 + DUP .Pantalla/sprite DEO + + ( mover a la izquierda ) + .Pantalla/x DEI2 #0008 SUB2 .Pantalla/x DEO2 + + ( dibujar tile 2 ) + ;pelota-sprite/tile2 .Pantalla/direc DEO2 + .Pantalla/sprite DEO +RTN +``` + +## relacionado-a-palas + +### actualiza-palas + +``` +@actualiza-palas ( -- ) + &izquierda + ( pala izquierda: botones arriba 10 y abajo 20 ) + .Controlador/boton DEI + DUP #10 AND ( comprobar bit para arriba ) + ,&izquierda-arriba JCN + DUP #20 AND ( comprobar bit para abajo ) + &izquierda-abajo JCN + + &derecha JMP ( salta si no se ha pulsado ninguno de los dos ) + + &izquierda-arriba + .izquierda/y LDZ2 VEL-PALA SUB2 .izquierda/y STZ2 + ,&derecha JMP + &izquierda-abajo + .izquierda/y LDZ2 VEL-PALA ADD2 .izquierda/y STZ2 + ,&derecha JMP + + &derecha + ( pala derecha: botones ctrl/A 01 y alt/B 02 ) + DUP #01 AND ( comprobar bit para A ) + ,&derecha-arriba JCN + DUP #02 AND ( comprobar bit para B ) + &derecha-abajo JCN + + &fin JMP ( salta si no se ha pulsado ninguno de los dos ) + + &derecha-arriba + .derecha/y LDZ2 VEL-PALA SUB2 .derecha/y STZ2 + ,&fin JMP + &derecha-abajo + .derecha/y LDZ2 VEL-PALA ADD2 .derecha/y STZ2 + + &fin + POP ( hacer POP al valor duplicado del botón ) +RTN +``` + +## dibuja-pala + +``` +@dibuja-pala ( x^ y^ color -- ) + ( guardar color ) + STH + + ( establecer `y` y x iniciales ) + .Pantalla/y DEO2 + .Pantalla/x DEO2 + + ( dibujar tile 0 ) + ;pala-sprite/tile0 .Pantalla/direc DEO2 + ( copiar color de la pila de retorno: ) + STHkr .Pantalla/sprite DEO + + ( añadir 8 a x: ) + .Pantalla/x DEI2 #0008 ADD2 .Pantalla/x DEO2 + + ( dibujar tile 1 ) + ;pala-sprite/tile1 .Pantalla/direc DEO2 + STHkr .Pantalla/sprite DEO + + ( añadir 8 a y: ) + .Pantalla/y DEI2 #0008 ADD2 .Pantalla/y DEO2 + + ( dibujar tile 3 ) + ;pala-sprite/tile3 .Pantalla/direc DEO2 + STHkr .Pantalla/sprite DEO + + ( restar 8 a x: ) + .Pantalla/x DEI2 #0008 SUB2 .Pantalla/x DEO2 + + ( dibujar tile 2 ) + ;pala-sprite/tile2 .Pantalla/direc DEO2 + STHkr .Pantalla/sprite DEO + + ( añadir 8 a y: ) + .Pantalla/y DEI2 #0008 ADD2 .Pantalla/y DEO2 + + ( dibujar tile 4 ) + ;pala-sprite/tile4 .Pantalla/direc DEO2 + STHkr .Pantalla/sprite DEO + + ( añadir 8 a x: ) + .Pantalla/x DEI2 #0008 ADD2 .Pantalla/x DEO2 + + ( dibujar tile 5 ) + ;pala-sprite/tile5 .Pantalla/direc DEO2 + ( obtener y no mantener el color de la pila de retorno: ) + STHr .Pantalla/sprite DEO +RTN +``` + +## dibuja-fondo + +``` +@dibuja-fondo ( -- ) + ;tile-fondo .Pantalla/direc DEO2 ( establecer la dirección del tile ) + + .Pantalla/alto DEI2 MARGEN-PARED SUB2 ( establecer límite ) + MARGEN-PARED ( establecer `y` inicial ) + &bucle-y + DUP2 .Pantalla/y DEO2 ( establecer coordenada `y` ) + + ( dibujar fila ) + .Pantalla/ancho DEI2 #0000 ( establecer límite `y` x inicial ) + &bucle-x + DUP2 .Pantalla/x DEO2 ( fijar coordenada x ) + #03 .Pantalla/sprite DEO ( dibujar sprite de 1bpp con color 3 y 0 ) + #0008 ADD2 ( incrementar x ) + GTH2k ( ¿es la anchura mayor que x? o también, ¿es x menor que la anchura? ) + ,&bucle-x JCN ( salta si x es menor que el límite ) + POP2 POP2 ( eliminar x y el límite ) + + #0008 ADD2 ( incrementar y ) + GTH2k ( ¿es el límite mayor que `y`? o también, ¿es `y` menor que el límite? ) + ,&bucle-y JCN ( salta si `y` es menor que el límite ) + POP2 POP2 ( eliminar `y` y el límite ) +RTN +``` + +## data + +``` +@tile-fondo 1122 4488 1122 4488 + +@pala-sprite + &tile0 [ 3f 7f e7 c3 c3 c3 c3 c3 00 00 18 3c 3c 3c 3c 3c ] + &tile1 [ fc fe ff ff ff ff ff ff 00 00 00 00 00 00 06 06 ] + &tile2 [ c3 c3 c3 c3 e7 ff ff ff 3c 3c 3c 3c 18 00 00 00 ] + &tile3 [ ff ff ff ff ff ff ff ff 06 06 06 06 06 06 06 06 ] + &tile4 [ ff ff ff ff ff ff 7f 3f 00 00 00 00 00 00 00 00 ] + &tile5 [ ff ff ff ff ff ff fe fc 06 06 06 06 06 1e 3c 00 ] + +@pelota-sprite + &tile0 [ 03 0f 1f 39 70 70 f9 ff 00 00 00 06 0f 0f 06 00 ] + &tile1 [ c0 f0 f8 fc fe fe ff ff 00 00 00 00 08 0c 06 06 ] + &tile2 [ ff ff 7f 7f 3f 1f 0f 03 00 00 00 00 18 0f 01 00 ] + &tile3 [ ff ff fe fe fc f8 f0 c0 06 06 0c 1c 38 f0 c0 00 ] +``` + +¡wiuf! :) + +# más posibilidades + +aquí hay algunas posibilidades extra para que practiques y trates de implementar: + +* contar y dibujar algún tipo de puntuación +* cambiar el color de la pelota y/o de la pala cuando chocan +* cambiar la dirección o el tipo de rebote dependiendo de la parte de la pala que golpea la bola +* iniciar el juego cuando se pulsa un botón +* posición "aleatoria" inicial de la pelota +* velocidad variable de la pelota y/o de las palas +* etc! + +¡comparte lo que termines creando en base a todo esto! :) + +# día 7 + +en el {tutorial de uxn día 7} hablamos de los dispositivos del ordenador varvara que aún no hemos cubierto: audio, archivo y fechatiempo o "datetime". + +este debería ser un final ligero y tranquilo de nuestro recorrido, ya que tiene que ver menos con la lógica de programación y más con las convenciones de entrada y salida en estos dispositivos. + +¡primero, te invito a tomar un descanso! + +después, ¡sigue explorando y comparte tus descubrimientos! + +# apoyo + +si te ha gustado este tutorial y te ha resultado útil, considera compartirlo y darle tu {apoyo} :) diff --git a/src/tutorial_de_uxn_día_7.gmo b/src/tutorial_de_uxn_día_7.gmo new file mode 100644 index 0000000..21a8ba3 --- /dev/null +++ b/src/tutorial_de_uxn_día_7.gmo @@ -0,0 +1,3 @@ +# día 7 + +¡proximamente! traducción en proceso. :) \ No newline at end of file diff --git a/src/uxn_tutorial_day_6.gmo b/src/uxn_tutorial_day_6.gmo index 121a882..327c1d6 100644 --- a/src/uxn_tutorial_day_6.gmo +++ b/src/uxn_tutorial_day_6.gmo @@ -346,6 +346,7 @@ i used nasu to draw a paddle consisting of 2x3 tiles in 2bpp mode. i will number 2 3 4 5 ``` + => https://100r.co/site/nasu.html 100R - nasu the resulting data is the following: @@ -956,7 +957,7 @@ we get 0! fffe works effectively as "-2"! we could continue in that way getting more and more "negative" numbers that works thanks to the constrained size of computer memory. -going back to our code, if we initialize our speed with +going back to our code, if we initialize our speed with: ``` #ffff .ball/speed-x STZ2 @@ -1079,7 +1080,7 @@ the y coordinate of the bottom wall would be the height of the screen, less the .ball/y LDZ2 BALL-SIZE ADD2 ( y + ball size ) .Screen/height DEI2 WALL-MARGIN SUB2 ( height - margin ) - GTH2 ( is the ball y greater than the wall y? ) + GTH2 ( is the ball-y greater than the wall-y? ) ,&set-negative-speed JCN ,&continue JMP @@ -1090,7 +1091,6 @@ the y coordinate of the bottom wall would be the height of the screen, less the ### update-ball code so far - our update-ball subroutine looks like the following right now: ``` @@ -1192,7 +1192,6 @@ and what happens if both conditions are not met at the same time? we can let the ball keep moving, but checking that it hasn't crossed the left wall, by comparing with 0000. - the whole x-in-left code would end up looking like: ``` @@ -1223,7 +1222,6 @@ the whole x-in-left code would end up looking like: finish would be a label at the end of the subroutine, and reset is a subroutine that we will discuss later. - this approach of comparing with 0000 is the easiest, but keep in mind that it might not work if you change the ball speed: it could happen that it crosses the wall but with an x coordinate that is never equal to 0. we can't really check if the x coordinate is less than 0, because as we discussed above, that would actually be a number close to ffff.