1715 lines
52 KiB
Plaintext
1715 lines
52 KiB
Plaintext
# tutorial uxn: día 6, hacia el pong
|
|
|
|
esta es la sexta sección del {tutorial de 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.
|
|
|
|
## configurando
|
|
|
|
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 a 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 solo 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 ( incrementa `y` )
|
|
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 ( incrementar y )
|
|
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 ( incrementar y )
|
|
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 iniciación:
|
|
|
|
```
|
|
;dibuja-fondo JSR2
|
|
```
|
|
|
|
=> ./img/screenshot_uxn-background-full.png captura de pantalla que muestra la pantalla 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 este 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 cómo dibujar las palas en base a estos parámetros y también recapitularemos cómo 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 00 00 06 06 ]
|
|
&tile2 [ c3 c3 c3 e7 ff ff ff 3c 3c 3c 3c 18 00 00 00 ]
|
|
&tile3 [ ff ff ff ff ff ff 06 06 06 06 06 06 06 06 06 ]
|
|
&tile4 [ ff ff ff ff ff 7f 3f 00 00 00 00 00 00 00 00 ]
|
|
&tile5 [ ff ff ff ff ff fe fc 06 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 escribir 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á borrar la pala antes de moverla, como hemos hecho en días anteriores.
|
|
|
|
en principio la subrutina debería ser directa: 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 por 10 (16 en decimal) para llegar a la dirección del siguiente tile. para esto, o para los cambios de coordenadas, podemos aprovechar el byte auto de la 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 mantendré 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 } ( borrar 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-en-movimiento.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-cuadro 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 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 ]
|
|
```
|
|
|
|
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 borrar 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, solo 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:
|
|
|
|
* borrar 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 situarse a lo largo de los procedimientos equivalentes para las palas dentro de la subrutina en-cuadro:`
|
|
|
|
```
|
|
( dentro de en-cuadro )
|
|
( 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 decrementarlos.
|
|
|
|
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 de la variable 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 )
|
|
.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 iniciamos 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 )
|
|
.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 actualiza-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 )
|
|
.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 solo 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:
|
|
|
|
```
|
|
(dentro de actualiza-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
|
|
```
|
|
|
|
### actualiza-pelota hasta ahora
|
|
|
|
nuestra subrutina actualiza-pelota tiene el siguiente aspecto hasta el momento:
|
|
|
|
```
|
|
@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 cuálf 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
|
|
( ¿es la coordenada derecha de la pelota mayor que el ancho de la pantalla - margen - ancho-pala? )
|
|
GTH2
|
|
,&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 actualiza-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 también cambiar la velocidad: tal vez basado en el cuentafotogramas, 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 los 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 )
|
|
;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
|
|
|
|
( 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-cuadro
|
|
|
|
```
|
|
@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
|
|
|
|
( 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
|
|
( ¿es pelota-x + tamaño-pelota mayor que la anchura de la pantalla - margen - ancho-pala? )
|
|
GTH2
|
|
,&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 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 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 fechahora 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} :)
|