¡¡Nuestro compañero Chema no está quieto!!

Avatar de Usuario
luiscoco
Mensajes: 2328
Registrado: 15 May 2011 04:23
Ubicación: Caracas, Venezuela
Agradecido : 30 veces
Agradecimiento recibido: 44 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor luiscoco » 20 Dic 2013 17:07

Último mensaje de la página anterior:

Excelente, ni la coco ni la dragon pueden hacer ese chiste de cambiar los caracteres, pero las últimas coco tiene un chip de vídeo que permite hacer eso, incluso tienen minúsculas, no recuerdo si hay zona de Rom para caracteres, tampoco recuerdo ningún juego que use esto

Enviado desde mi HTC Desire C mediante Tapatalk

Avatar de Usuario
Chema
Mensajes: 1536
Registrado: 21 Jun 2012 20:13
Ubicación: Gijón
Agradecido : 441 veces
Agradecimiento recibido: 186 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor Chema » 23 Dic 2013 21:27

Otro vídeo con algunos avances más. Todavía tiene pequeños errores, cosas a medias, mucho por hacer... Pero vamos por el buen camino... Creo.

Aún no se aprecia el objetivo central del juego, así que en un momento dado paso de nivel con una tecla... Pero eso es lo siguiente en mi lista (después de arreglar y ajustar algunas cosas, como centrar la nave, que lo estropeé en algún momento, pero ya está localizado).

Espero que os guste. Cualquier comentario o sugerencia será bienvenida :)

http://youtu.be/mmNyjg_0qhI

jltursan
Mensajes: 1891
Registrado: 20 Sep 2011 13:59
Agradecido : 49 veces
Agradecimiento recibido: 142 veces

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor jltursan » 23 Dic 2013 22:18

La verdad es que si que tiene muy buen aspecto -drinks

Y ahora la pregunta del millón, ¿en que formato aparecerá, entrará en una única carga desde cinta?.

Avatar de Usuario
Chema
Mensajes: 1536
Registrado: 21 Jun 2012 20:13
Ubicación: Gijón
Agradecido : 441 veces
Agradecimiento recibido: 186 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor Chema » 24 Dic 2013 01:13

Si, o eso pretendo. Será un juego basado en cinta. De hecho ahora mismo me quedan más de 10K libres... O sea que pinta muy bien en ese sentido. Y eso que, si me descuido, enseguida me como la memoria :)

Avatar de Usuario
luiscoco
Mensajes: 2328
Registrado: 15 May 2011 04:23
Ubicación: Caracas, Venezuela
Agradecido : 30 veces
Agradecimiento recibido: 44 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor luiscoco » 24 Dic 2013 04:11

Hay un problemilla Chema, cuando giras, la nave no se puede quedar quieta hasta que haces scroll, pues te matan sin poder hacer nada

Avatar de Usuario
Chema
Mensajes: 1536
Registrado: 21 Jun 2012 20:13
Ubicación: Gijón
Agradecido : 441 veces
Agradecimiento recibido: 186 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor Chema » 24 Dic 2013 10:16

Supongo que te refieres al scroll que desplaza la nave un poco lateralmente. El scroll se hace sólo mientras se ejecuta la animación del giro, así que es igual que si no hubiese ese desplazamiento; durante el giro no puedes hacer nada igualmente. Eso si, durante un rato la nave está elevada, así que no te pueden matar.

Avatar de Usuario
Silicebit
Mensajes: 1325
Registrado: 16 May 2011 21:13
Ubicación: La buhardilla del silicio.
Agradecido : 30 veces
Agradecimiento recibido: 78 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor Silicebit » 24 Dic 2013 11:30

¡Esto tiene cada vez mejor pinta! ¡Un lujazo de juego, Chema! :-)
El 6809 es el Rolls-Royce de los 8bits, el 6502 es el Mercedes, y el Z80 el SEAT 850. Sorry, but... I think different. :-P -0r1c -m3s3x -t4nd1 -cbmja YouTube

Avatar de Usuario
ron
Mensajes: 17175
Registrado: 28 Oct 2010 14:20
Ubicación: retrocrypta
Agradecido : 508 veces
Agradecimiento recibido: 532 veces

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor ron » 24 Dic 2013 12:15

Silicebit escribió:¡Esto tiene cada vez mejor pinta! ¡Un lujazo de juego, Chema! :-)


Estaba posteando lo mismo y me has quitado las palabras del teclado. -0r1c

Chema es mucho Chema !!!

Avatar de Usuario
Chema
Mensajes: 1536
Registrado: 21 Jun 2012 20:13
Ubicación: Gijón
Agradecido : 441 veces
Agradecimiento recibido: 186 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor Chema » 13 Ene 2014 20:41

Vale. Como he estado liado y el poco tiempo que tuve lo empleé en arreglar bugs y otras cosillas menores, voy a ver si puedo ir contando cómo estoy haciendo las cosas. Alguno lo pidió, así que no se me quejen del enorme -bRick que se avecina.

¡Y por capítulos!

Eso sí, el código que pongo sigue el formato del ensamblador del OSDK, el XA. Supongo que si no se conoce pueda despistar un poco, pero seguro que basta para hacerse una idea. Se me ha quedado algún comentario en inglés, igual que los nombres de algunas variables. Lo suelo hacer así para que la gente de Defence Force siga mejor el código. Cualquier comentario será bienvenido, claro.

Allá vamos:

El problema
El Oric no tiene un chip de video como tal, ni ninguna capacidad hardware para manejar sprites, hacer doble búfer, modificar el área de memoria donde se almacenan la pantalla, etc. Esto hace que todo deba realizarse por software, usando el 6502 (que tampoco da para mucho) y teniendo en cuenta la organización del modo de video (donde sólo se utilizan 6 bits de cada byte), así que la actualización de los gráficos en pantalla es todo un reto.

En Space:1999 ya se hace un uso intensivo del procesador para el renderizado isométrico y se actualiza sólo la zona de la pantalla donde hay movimiento. Aunque el código se podría haber optimizado más, es difícil obtener una animación rápida en cuanto hay unos pocos sprites moviéndose en la pantalla simultáneamente.

En 1337 la cosa es peor porque hay que actualizar toda la pantalla (el área de juego) continuamente, no sólo por trozos: hay un campo de estrellas, hay objetos en 3D, disparos, etc. potencialmente en toda la pantalla, de modo que es inútil intentar actualizar sólo las áreas necesarias. El implementar un doble buffer aquí representa como un 30% del tiempo total de pintado (incluyendo todo el cálculo 3D), por mucha optimización que se aplique. Esto hace que a veces el framerate baje casi hasta 5 fps (aunque la mayoría del tiempo es mucho mayor).

Por supuesto es posible pasar del doble búfer y utilizar otras técnicas (pintado de líneas con eor para borrar las anteriores, como hace el Elite original), pero eso produce un parpadeo, a mi juicio, muy feo. Y el resultado con doble búfer es perfectamente jugable y relativamente suave de todas formas.

En Skool Daze el reto era diferente. Aquí hay muchos sprites moviéndose por toda la pantalla, pero el fondo es bastante estático, así que basta con marcar las áreas que necesitan redibujado y optimizar al máximo el mismo. En este caso todo el juego estaba implementado usando “tiles” de 6x8, así que se tiene un campo de bits que indica cuándo un tile necesita ser redibujado. En cada frame se recorre el campo y cuando se encuentra un bit a 1 se calcula el gráfico de 6x8 que se debe volcar en pantalla en esa posición. El resultado es muy bueno y no se necesita demasiada memoria extra.

La cuestión es entonces: ¿es posible tener un juego que funcione con un scroll de toda la pantalla y con varios sprites a la vez? ¿Algo como el Uridium?. Ese era el reto.

Dejar que la ULA trabaje por nosotros.

La respuesta inmediata a la pregunta anterior es no. Al menos no para un área grande de juego (como del 70-80% de la pantalla). O no por software. Es posible hacer un scroll lateral de la pantalla en alta resolución más o menos rápido (como hace Skool Daze), pero sólo si movemos todo el contenido en un momento dado y el juego luego sigue sobre un área fija (o sea, pasamos del doble búfer y movemos directamente el contenido de la pantalla). Eso no es lo que queremos (queremos algo continuo mientras se juega) y, además, no es visualmente suave ni permite parallax (hacer scroll a diferentes velocidades para diferentes planos).

El truco aquí está en aprovecharse del modo TEXT del Oric. Sí, el modo de texto. En ese modo la pantalla se organiza en 40 columnas y 27 filas. Cada celda contiene un byte que indica un carácter. La ULA lee el código y pinta el carácter asociado del juego de caracteres en pantalla. Es decir, hace el renderizado del bloque de 6x8 por hardware. Vale. Ese va a ser el truco. Sólo tendremos que borrar/pintar/volcar un área de 40x27 bytes como máximo, lo que puede hacerse muy rápido y la ULA hace el resto.

Por supuesto tendremos que redefinir el juego de caracteres para que contenga los gráficos con los que componer todo el fondo (con la nave nodriza enemiga).
Lo primero es ver de cuántos caracteres (al final serán nuestros “tiles”) disponemos. Lamentablemente no tenemos 256. Los códigos por debajo del 32 son considerados por la ULA como atributos (para cambiar colores, modo de pantalla, etc). Los que tienen el bit de más peso a 1 son considerados en vídeo inverso. Así que nos quedan sólo 96. Cualquier gráfico un poquito complicado va a necesitar más. Ya tenemos un problema.

Eso sí, disponemos de un juego alternativo (el LORES 1), así que ahí tenemos otros 96, pero no podemos intercalarlos de cualquier manera con el estándar porque requieren de un atributo serie para cambiar de modo. Lo que podemos hacer es disponer nuestra área de juego de modo que se alternen ambos modos en cada fila. Esto es mejor que nada.
Cada fila de la pantalla, pues, comenzará por un código serie que indique el color de la tinta a usar, y otro que indique si usamos el juego de caracteres estándar o el alternativo.

Cada fila entonces tiene:

Código: Seleccionar todo

Byte 0        Byte 1           Byte 2 ….. Byte 37       Byte 38 Byte 39
COLOR      8 (par)/9 (impar)    Códigos de los gráficos      No usados


Esta técnica va a tener una desventaja clara: todo el movimiento y el scroll va a ser carácter a carácter. Se podría intentar paliar esto con un esquema complejo que realice el scroll sub-carácter sobre los gráficos, pero requeriría más “tiles” y tiempo de CPU, cosas de las que, en principio, no disponemos. Esto, sin embargo, lo haremos con el fondo de estrellas, como veremos más adelante.

Gráficos, “tiles” y organización en memoria

El área de juego será de 36 columnas (las dos primeras las necesitamos para los atributos y las dos últimas no las usaremos para dejarlo todo centrado) por 20 filas.
El mapa de un nivel es de 256 columnas y 20 filas y contiene el dibujo de la nave nodriza. El diseño de la misma se hace en base al siguiente juego de bloques gráficos (“tiles”):

tilesetreducido.png
Gráficos
tilesetreducido.png (6.3 KiB) Visto 2485 veces


Los gráficos de la fila inferior van al juego estándar (56, del 32 al 87) y los de la superior al alternativo (54). Por supuesto ciertos gráficos habrá que alinearlos debidamente en el mapa para que coincidan bien y el resultado sea correcto.

Los dos bloques que quedan libres del juego alternativo se va a usar para las estrellas y las bolas de energía que pueden recogerse sobre la superficie de la nave (y que están animadas).

En ambos juegos, el primer código (que es el 32) se usa para indicar que está libre (un espacio transparente, a través del cual se ven las estrellas) y el 33 para un bloque en negro pero no transparente (para las sombras de los muros).

El mapa se sitúa en los últimos 5K disponibles y cada fila de 256 bloques ocupa exactamente una página, así que acceder al tile correspondiente a una coordenada global (fila,columna) es muy sencillo y rápido. Por ejemplo, para obtener el código del tile basta con acceder a la página <(fila+base)*256> indexando con X = columna. Es decir la fila es el byte alto de la dirección, siendo el bajo 0; de este modo (por ejemplo):

Código: Seleccionar todo

   lda fila
   clc
   adc #>_start_rows ; Dirección de comienzo del mapa (fila cero)
   sta smc_address+2 ; Modificamos el código de abajo
   ldx columna
smc_address
   ; Esto es código automodificable, el byte alto de $1200 se modifica
;antes para que el lda lo haga de la dirección correcta
   lda $1200,x   


Doble búfer

Nuestro doble búfer (o backbuffer) tiene que contener el área visible en pantalla. La idea es simple, en cada frame borramos el búfer, pintamos todo lo necesario sobre él y lo volcamos en la pantalla.

Vamos a hacerlo un poco más grande de lo necesario (que sería 36x20) para poder olvidarnos de hacer clipping al pintar, es decir, de determinar qué partes de un objeto (sprite) se ven y cuales no). Al hacerlo más grande podemos pintar siempre que el objeto sea visible (aunque sea parcialmente) y luego volcaremos sólo lo necesario.
Como los sprites son como mucho de 2x2 tiles, en este caso necesitamos dos columnas y dos filas extra. En mi caso he puesto un buffer de 40x22, porque tengo una tabla para multiplicar por 40 rápidamente, aunque bien podría haberse hecho de 38x22.

Vamos a ver el proceso paso a paso:

Para borrar el buffer, ponemos todo el contenido al carácter 32 (espacio). Tenemos que hacerlo rápido, así que desenrollamos el bucle parcialmente. Además tengo dos etiquetas definidas para indicar las columnas que no se usan tanto por la derecha como por la izquierda (PCOLSL y PCOLSR). Hay que tener en cuenta que los bucles son más eficientes si se usan decrementos en lugar de incrementos, porque podemos chequear simplemente la bandera de cero (Z) o de signo (N):

Código: Seleccionar todo

_clear_backbuffer
.(
   lda #32
   ldy #39-PCOLSL-PCOLSR
loop
   sta _backbuffer+40*0+PCOLSL,y
   sta _backbuffer+40*1+PCOLSL,y
   sta _backbuffer+40*2+PCOLSL,y
   …
   sta _backbuffer+40*18+PCOLSL,y
   sta _backbuffer+40*19+PCOLSL,y
   dey
   bpl loop
   rts
.)



Para dibujar el fondo la cosa es simple. Tenemos una variable _inicol con la columna inicial que se ve en pantalla (se modifica al hacer scroll):

Código: Seleccionar todo

_render_background
.(
   ; Start drawing the background
   ; From _inicol onwards.
   ; Unrolled to get speed.
   ; Takes the tile code from the map
   ; which is organized in memory
   ; so that every row lies on a page
   ; boundary, and an indexed addressing
   ; with x=column, gets the tile

   lda #39-PCOLSL-PCOLSR
   clc
   adc _inicol
   tax
   ldy #39-PCOLSL-PCOLSR
loop
   lda _start_rows+256*0,x
   sta _backbuffer+40*0+PCOLSL,y
   lda _start_rows+256*1,x
   sta _backbuffer+40*1+PCOLSL,y

   lda _start_rows+256*19,x
   sta _backbuffer+40*19+PCOLSL,y
   dex
   dey
   bpl loop

rts
.)


Listo. Para volcar su contenido es similar. Por supuesto sólo volcamos la parte que se ve en pantalla, no sobre los atributos ni las dos columnas sin usar de la derecha. Es decir los bytes del 2 al 37. SCR_ADDR es una etiqueta con la dirección de pantalla donde queremos empezar a volcar. Sería $bb80 para empezar por la parte superior. En el juego es más abajo, porque reservamos sitio para el panel de puntuaciones.

Código: Seleccionar todo

_dump_backbuffer
.(
   ldy #39-PCOLSL-PCOLSR
loop
   lda _backbuffer+40*0+PCOLSL,y
   sta SCR_ADDR+40*0+PCOLSL,y
   lda _backbuffer+40*1+PCOLSL,y
   sta SCR_ADDR+40*1+PCOLSL,y
   …
   lda _backbuffer+40*19+PCOLSL,y
   sta SCR_ADDR+40*19+PCOLSL,y

   dey
   bpl loop
   rts
.)


Esta parte es MUY eficiente, así que permite hacer un scroll de todo el mapa muy rápidamente (eso sí, carácter a carácter). Basta con actualizar el valor de _inicol y lanzar las rutinas anteriores. Es una buena base.

El borrado del búfer lleva 3830 ciclos, el renderizado son 8060 (incluyendo el campo de estrellas que todavía no hemos visto) y el volcado 6731. Todo ello son 18.621 ciclos, menos de los 20.000 que producirían 50 frames por segundo con el 6502 del Oric a 1Mhz.

Pero faltan cosas. Sobre el doble búfer hay que pintar también los diferentes sprites y, además, queremos un efecto parallax con las estrellas (que no están ni pintadas aun).

Comenzaremos por lo segundo. En la próxima entrega -grin

Avatar de Usuario
Chema
Mensajes: 1536
Registrado: 21 Jun 2012 20:13
Ubicación: Gijón
Agradecido : 441 veces
Agradecimiento recibido: 186 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor Chema » 15 Ene 2014 14:24

Ummm igual esto es demasiado técnico o denso... De todas formas me parece una buena idea ir documentando estas cosas. Me sirve para repasar las rutinas y dejar por escrito qué voy haciendo.

Oye, e igual alguien en un futuro lo encuentra útil.

Así que seguimos con el -bRick

El campo de estrellas y el efecto parallax

Es sencillo tener un campo de estrellas fijo. Basta con tener un array con las posiciones de las estrellas y pintarlo sobre el fondo. Digo sobre el fondo, porque lo hago después del bucle anterior, chequeando si hay algo o no, es decir si el backbuffer contiene un 32 (espacio) o no. De este modo me ahorro un montón de comprobaciones en el bucle anterior viendo si lo que se vuelca es un espacio o no y resulta más eficiente.

Pero vamos a hacer que las estrellas hagan scroll pixel a pixel y así lograr un efecto más chulo. No nos hace falta generar estrellas para todo el mapa, sino sólo para un pequeño trozo. Cuando hagamos scroll de una columna en el mapa, la estrella lo hará un pixel y no se moverá de la posición que tiene en el mapa (basado en caracteres) hasta que no hayamos hecho esto 6 veces.

Con un cálculo rápido vemos el área que hay que rellenar con estrellas: podemos hacer scroll como 256-40=216 veces (si suponemos que vemos 40 columnas, que ni eso). Esto dividido por 6 da 36. Así que tenemos que rellenar las 40 columnas que vemos, más 36; es decir 76. Como las dos últimas no se ven, en realidad son 74.

Lo que tenemos entonces es un array con el número de estrellas (ahora mismo 30) que contiene su posición (x,y) en el mapa, donde x va de 0 a 73 e y de 0 a 19 y que se rellena de forma aleatoria. Como sólo existe el gráfico para el juego de caracteres alternativo, las estrellas tienen que estar en filas impares. De todo eso se encarga la rutina de generación.

En realidad es más eficiente tener definidas tres tablas: dos para el puntero a la fila donde está la estrella y uno para la columna:

Código: Seleccionar todo

starsx       .dsb NSTARS
starshi       .dsb NSTARS
starslo       .dsb NSTARS


El gráfico correspondiente a una estrella es un solo punto en la tercera fila. Lo que hacemos en cada scroll es rotar ese byte (lo que rota la estrella en todas partes donde aparece) y si hace falta actualizamos su posición X y reiniciamos. Como sólo tenemos 6 pixels por byte, no se puede usar el Carry para ver si nos salimos y es algo más complicado. Por ejemplo este código hace scroll a la izquerda:

Código: Seleccionar todo

   ; Rotamos y miramos si nos
   ; hemos salido de los 6 bits menos
   ; significativos
lda _star_tile+3
   asl
   and #%00111111
   beq update

   ; No nos hemos salido, nada más que hacer
   sta _star_tile+3
   rts

   ; Nos hemos salido, movemos las estrellas y reiniciamos
   ; el gráfico
update
   ; Bucle para todas las estrellas, decrementando
   ; su posición X
   ldx #NSTARS-1
loop
   dec starsx,x
   dex
   bpl loop

   ; Ponemos el pixel 0 a 1
   lda #%00000001
   sta _star_tile+3
   rts


En realidad no se hace así del todo porque, para evitar errores visuales (tearing), se marca que hay que reiniciar el gráfico y se hace al final del volcado de toda la pantalla. Ya aclararemos este punto más adelante.

Para pintar las estrellas, la cosa es sencilla:

Código: Seleccionar todo

_plot_stars
.(
   ; Bucle para todas las estrellas (decrementando)
   ldx #NSTARS-1
loop
   ; Tomamos el puntero a la fila de la estrella y lo metemos en tmp (página cero)
   lda starslo,x
   sta tmp
   lda starshi,x
   sta tmp+1

   ; Tomamos la columna de la estrella
   ; y comprobamos que sea visible
   ; si no lo es saltamos a la siguiente
   ldy starsx,x
   bmi skip
   cpy #39
   bcs skip
   
   ; Es visible, miramos si en el backbuffer tenemos en
   ; la posición donde va la estrella un espacio libre o no
   lda (tmp),y
   cmp #32
   bne skip

   ; Lo tenemos, así que ponemos el código del gráfico
   ; de la estrella
   lda #STAR_TILE
   sta (tmp),y
skip
   dex
   bpl loop
   rts
.)


Hasta aquí vale, pero ¿y los marcianos?

Naturalmente queremos poner naves enemigas, objetos y otras cosas. Y queremos que sean sprites con sus máscaras y poder darles animaciones, etc.
Los sprites son de 2x2 caracteres, es decir: 12x16 pixeles. Los sprites por software se basan en la técnica clásica de tener por un lado el gráfico y por otro la máscara que indica la parte que ocluye el gráfico (como ceros y el resto unos). Se toma el fondo, se le hace un AND con la máscara y se aplica el gráfico con una operación OR. Por ejemplo la nave y su máscara son:

kk.png
Gráfico
kk.png (34.02 KiB) Visto 2447 veces
kk01m.png
Máscara
kk01m.png (15.07 KiB) Visto 2447 veces


Los datos de los sprites se almacenan en arrays (vectores) que contienen cosas como la fila y columna en que están situados, su velocidad, hacia dónde miran, su estado, velocidad, etc. También, por supuesto, punteros a su gráfico y máscara actuales.

Quizás es buen momento para recordar que para manejar arrays con el 6502, es más eficiente generar campos de 8 bit y ponerlos cada uno por separado. Así un puntero a un gráfico que es de 16 bits, es mejor tenerlo como dos vectores separados con la parte baja y la alta que un vector con entradas de 2 bytes.

Tenemos, pues, cosas como:

Código: Seleccionar todo

; Position of sprites in the overall map
sprite_cols      .dsb MAX_SPRITES+1 
sprite_rows      .dsb MAX_SPRITES+1   

; Movement speed
max_speed      .dsb MAX_SPRITES+1

; Pointers to graphic and mask data
sprite_grapl      .dsb MAX_SPRITES+1
sprite_graph      .dsb MAX_SPRITES+1
sprite_maskl      .dsb MAX_SPRITES+1
sprite_maskh      .dsb MAX_SPRITES+1

 


Todo esto está muy bien y en alta resolución (HIRES) lo hemos hecho otras veces. La cosa es hacer esta operación lo más rápido posible, porque es el cuello de botella. Ahora bien, ¿cómo hacemos esto cuando usamos modo texto?
Pues sobre caracteres, claro. Es como si manejásemos un modo de pantalla diferente, con otro direccionamiento (que es lo que es en realidad, nada de “como”).

La rutina básica es la que se encarga de renderizar un tile de 6x8. Necesita el código del gráfico de fondo y un par de punteros al gráfico y a la máscara. Además, como usamos dos juegos de caracteres diferentes, hay que tener en cuenta de cuál se trata. Para optimizar los punteros se modifican directamente en el código (otra vez código auto-modificable) y se usan tablas para las multiplicaciones.

El resultado del renderizado va a ir en alguno de los caracteres que aún no se han usado de los 96 y que están asignados a los sprites. Luego pondremos el código de ese carácter en el backbuffer en la posición correcta antes del volcado. Está claro que no podemos hacerlo sobre el original, pues modificaríamos el gráfico en sí, que puede estarse usando en otros sitios.

Vamos a intentar explicar la rutina que hace esto. El tile de fondo se pasa en el registro A, el tile de destino (donde va a ir el resultado final) en el registro Y. Los punteros al gráfico y máscara ya están actualizados y el juego de caracteres utilizado se pasa en una variable de página cero (tmp3+1) como el byte alto de donde empieza dicho juego, es decir $b4 o $b8.

Código: Seleccionar todo

render_tile
.(
   ; Obtener el puntero a los datos gráficos para
   ; el carácter en A:
; $b400+(regA*8) o $b800+(regA*8)
   ; Multiplicamos A*8
   tax
   lda tab_mul8_lo,x
; Guardamos modificando el código más abajo
   sta smc_bkg_p+1
; Vamos con la parte alta
   lda tmp3+1 ; contiene $b4 o $b8
   clc
   adc tab_mul8_hi,x
; Guardamos modificando el código más abajo
   sta smc_bkg_p+2

   ; Ahora lo mismo para el destino
   lda tab_mul8_lo,y
   sta smc_dest_p+1
   lda tab_mul8_hi,y ;tmp+1
   clc
   adc tmp3+1
   sta smc_dest_p+2


Ya tenemos los punteros preparados, ahora hay que hacer el bucle que vaya recorriendo los 8 bytes tomando el fondo, haciendo el AND con la máscara y el ORA con el gráfico y almacenando en el carácter de destino. No confundirse porque algunas etiquetas tengan un + delante: es para que sean accesibles desde otras funciones externas.

El código automodificable es engorroso pero ahorra muchos ciclos en este tipo de cosas.

Código: Seleccionar todo

   ldy #7   
loop
smc_bkg_p   
   lda $1234,y ; Esto lo acabamos de poner a la dirección correcta
+smc_mask_p
   and $1234,y ; Esto se puso fuera, antes de llamar a la función
+smc_sprite_p
   ora $1234,y ; Esto también
smc_dest_p
   sta $1234,y ; Esto lo acabamos de poner a la dirección correcta
   dey
   bpl loop
   rts
.)


Y listo. Ya tenemos el gráfico correcto en uno de los caracteres libres. En realidad estoy usando dos funciones idénticas, excepto que la segunda invierte el gráfico y la máscara antes de pintar. Esto es útil para no tener en memoria los gráficos de los objetos dos veces: una mirando a la derecha y otra a la izquierda. Para hacerlo rápido, se tiene una tabla de 64 entradas con los inversos de cada patrón de 6 bit y el bucle queda:

Código: Seleccionar todo

    
ldy #7   
loop
smc_bkg_p
   lda $1234,y
+smc_maski_p
   ldx $1234,y
   and inverse_table,x
+smc_spritei_p
   ldx $1234,y
   ora inverse_table,x
smc_dest_p
   sta $1234,y
   dey
   bpl loop


Para pintar un sprite completo primero necesitamos 4 caracteres libres (dos en el juego estándar y dos en el alternativo, porque usa dos filas). Para ello se tienen unas tablas que indican el código de los caracteres a utilizar. Cada sprite tiene asociados cuatro códigos donde dibujarse sprite_tile11 (fila1, columna1), sprite_tile12 (fila1, columna2), sprite_tile21 y sprite_tile22 (ídem para la fila 2).

Si un sprite puede tener asociados el 125 y el 126 para la fila 1 y también el 125 y el 126 para la fila 2, porque ambos van en juegos de caracteres diferentes. Tenemos libres del 88 al 127, lo que daría para unos 20 sprites. No está mal. Lástima que no vaya a ser así al final. Pero ya hablaremos de eso.

Vamos a ver cómo se renderiza un sprite de 2x2 cuyo código (un identificador) se pasa en el registro X. La idea es:
1-Lo primero es ver si el sprite está en la zona visible (restar a su columna la columna inicial que estamos viendo y ver que está en rango).
2-Calcular el puntero a la fila del backbuffer (fila_del_sprite*40+base_backbuffer). Luego pondremos la columna (menos la columna inicial) en el registro Y y accedemos al contenido por direccionamiento indirecto (ej: lda (puntero),y).
3-Poner los códigos de los caracteres asociados al sprite en el backbuffer, guardando primero el contenido que tenían para pintar luego el fondo.

Código: Seleccionar todo

render_sprite
.(
   ; Lo primero colocar los caracteres donde vamos a pintar
   ; sobre el backbuffer

   ; Es el sprite visible?
   lda sprite_cols,x   ; X es el id del sprite, para acceder a todos sus datos
   sec
   sbc _inicol
   clc
   adc #2
   bpl okhere
retme
   rts

okhere
   cmp #39
   bcs retme
okthere

   ; Vale lo es, aprovechamos para guardar la
   ; posición de su columna en el backbuffer (restada la
   ; columna inicial _inicol)
   sta tmp4

   ; Calcular row*40+base del backbuffer y guardar en tmp1
   ; Como el backbuffer está alineado a una página, basta
   ; con sumar en el byte alto
   ldy sprite_rows,x
   lda tab_m40lo,y
   sta tmp1
   lda #>_backbuffer
   clc
   adc tab_m40hi,y
   sta tmp1+1

   ; Ponemos los caracteres donde vamos a pintar
   ; y vamos salvando los códigos que había
   ldy tmp4   ; Aquí teníamos la columna del sprite-columna inicial (por el scroll)
   lda (tmp1),y   ; Contenido en esa posición
   sta t11+1   ; Lo guardamos (de nuevo código automodificable, ya veremos luego)
   lda sprite_tile11,x   ; Cogemos el código asociado al sprite para ese tile
   sta (tmp1),y   ; Lo ponemos en su sitio

; Siguiente columna de la fila 1
   iny
   lda (tmp1),y
   sta t12+1
   lda sprite_tile12,x
   sta (tmp1),y

   ; Vamos por la columna 1 de la fila 2
   tya
   clc
   adc #39
   tay   
   lda (tmp1),y
   sta t21+1
   lda sprite_tile21,x
   sta (tmp1),y

   ; Y la columna 2 de la fila 2
   iny
   lda (tmp1),y
   sta t22+1
   lda sprite_tile22,x
   sta (tmp1),y
   


Vale, eso ya está. Ahora basta con renderizar los gráficos en cada uno de los cuatro caracteres. La idea es simple usando la rutina que vimos antes, pero hay algunos detalles que complican el código. Lo primero es que hay que tener en tmp3+1 $b4 o $b8 dependiendo del juego de caracteres a usar, es decir, de si la fila es par o impar. Se usa una tabla para eso y el código es:

Código: Seleccionar todo

   lda sprite_rows,x
   and #%1
   tay
   lda base_charset_p,y
   sta tmp3+1


Bueno, ahora hay que obtener los punteros a los gráficos y máscara y ponerlos en el código automodificable de la rutina que renderiza. De nuevo dos versiones, dependiendo de si hay que invertir el gráfico, pero nada complicado. Los punteros se almacenan en tablas indexadas por el id del sprite (que sigue en el registro X):

Código: Seleccionar todo

   lda sprite_dir,x   ; Tomamos la dirección en la que mira el sprite
   bmi inversed_draw

   ; Versión que no invierte
   lda sprite_maskl,x
   sta smc_mask_p+1
   lda sprite_maskh,x
   sta smc_mask_p+2
   lda sprite_grapl,x
   sta smc_sprite_p+1
   lda sprite_graph,x
   sta smc_sprite_p+2
   jmp endinvcheck

   ; Versión que invierte
inversed_draw
   lda sprite_maskl,x
   sta smc_maski_p+1
   lda sprite_maskh,x
   sta smc_maski_p+2
   lda sprite_grapl,x
   sta smc_spritei_p+1
   lda sprite_graph,x
   sta smc_spritei_p+2
endinvcheck


Vale, ahora hay que cargar en A el código con el gráfico de fondo. Lo habíamos salvado antes en t11+1, t12+1, etc. ¿no? Pues se toma aquí. Es feo, pero más rápido que usar variables intermedias o la pila.

Esto se repite para los cuatro caracteres, así que basta con poner uno:

Código: Seleccionar todo

   ; Tomamos el código del carácter de fondo
t11   
   lda #0   ; Aquí lo hemos guardado antes

   ; ahora tomamos el carácter de destino para este sprite en la posición 1,1
   ldy sprite_tile11,x
   ; Reservamos X
   stx tmp3

   ; Y listos para llamar, por desgracia no es tan inmediato por culpa de
   ; tener en cuenta la versión que invierte… Esto está chapuceado, de momento.
   pha
   lda sprite_dir,x
   bmi inverse_draw1
   pla
   jsr render_tile
   jsr add8pointers ; Suma 8 a los punteros necesarios
   jmp t12
inverse_draw1
   jsr add8pointersi ; Suma 8 a los punteros necesarios, versión invertir
   pla
   jsr render_tilei
   jsr sub8pointersi ; Resta 8 a los punteros necesarios
   
   ; Siguiente
t12   
   …
.)


Hala, ahora a tomar un café que esto ha sido mortal para nuestras neuronas.

Eso sí, funciona y es rápido. Toma unos 1500 ciclos de CPU renderizar un sprite en pantalla. Pero si lo implementamos así nos encontramos con un montón de efectos espantosos en pantalla: “tearing,” “glitches” gráficos, etc. Inutilizable para nada decente.

Por ejemplo la siguiente imagen muestra el "tearing".
tearing.png
Ejemplo de tearing


Se produce porque estamos actualizando la memoria de pantalla mientras la ULA está refrescando la imagen, de forma que una parte tiene el gráfico antiguo y la otra el nuevo. Aquí se ve bastante claro (y vemos que queda horrible), pero también ocurre si actualizamos un sprite, su sombra, etc. mientras ya está parcialmente dibujado.

Existe otra versión más sutil, que ocurre cuando actualizamos el contenido gráfico de un carácter y su posición, pudiendo ocurrir que en la imagen tengamos el gráfico antiguo en la posición nueva o el nuevo en la antigua. Recordemos, además que los sprites llevan parte del fondo, que puede no ser el correcto en este caso. Lidiaremos con eso más adelante.

O sea que hay que librarse de ello. Para el “tearing” normal es algo simple: sólo hay que sincronizar con el barrido vertical del monitor para pintar mientras no se está actualizando la imagen. Un momento… Eso no se puede hacer con un Oric. No hay manera de detectar esa señal sin un mod (simple, pero que poca gente tiene).

Bueno, pues lo haremos por software. Menos mal que Fabrice Frances ya nos dijo cómo hacerlo en su día. Sólo hay que integrarlo. Para el resto de efectos raros… En fin, primero habrá que ver por qué se producen.

dancresp
Mensajes: 4993
Registrado: 13 Nov 2010 02:08
Agradecido : 14 veces
Agradecimiento recibido: 83 veces

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor dancresp » 15 Ene 2014 15:13

Buffff !!! -shock

Estas últimas aportaciones se merecen un copiar+pegar->PDF para una tranquila lectura...

-drinks

jltursan
Mensajes: 1891
Registrado: 20 Sep 2011 13:59
Agradecido : 49 veces
Agradecimiento recibido: 142 veces

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor jltursan » 15 Ene 2014 18:42

Pues sí, teniendo en cuenta que el Oric no es lo mío, toda esta información técnica es una lectura de lo más interesante.

¡Mil gracias!, pocos desarrolladores se animan a describir con detalle lo que programan (precisamente Dancresp es uno de ellos) y yo desgraciadamente no me incluyo entre ellos, ¡menuda envidia!

Avatar de Usuario
Silicebit
Mensajes: 1325
Registrado: 16 May 2011 21:13
Ubicación: La buhardilla del silicio.
Agradecido : 30 veces
Agradecimiento recibido: 78 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor Silicebit » 15 Ene 2014 22:03

¡Todo un lujazo esta información! ¡Chema, muchas gracias por el currazo que te estás dando! A la saca que ha ido en formato PDF para leerlo todo más tranquilamente, e intentar entenderlo bien todo. Más, más, que de ladrillo inside nada de nada. :-D
El 6809 es el Rolls-Royce de los 8bits, el 6502 es el Mercedes, y el Z80 el SEAT 850. Sorry, but... I think different. :-P -0r1c -m3s3x -t4nd1 -cbmja YouTube

Avatar de Usuario
Chema
Mensajes: 1536
Registrado: 21 Jun 2012 20:13
Ubicación: Gijón
Agradecido : 441 veces
Agradecimiento recibido: 186 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor Chema » 16 Ene 2014 10:21

Igual sí que me he pasado con los detalles... La verdad es que no me viene mal dejarlo escrito y cuando me pongo me sale la vena "profesor siesta"...

Por supuesto si queréis detalles sobre algo en particular o aclaraciones sobre lo que sea, decidlo y yo lo pongo. A ver si tengo un rato luego y sigo con más info :)

Por cierto, en cuanto al juego he avanzado un poquito y ya es posible destruir la nave enemiga. La idea es combinar el matamarcianos que veis en el vídeo con algo de puzle, de forma que sobre la nave hay cuatro interruptores que hay que pulsar para conseguir encender los cuatro leds del panel.

En los primeros niveles es cosa de pulsar los cuatro, pero luego unos interruptores no solo encienden un led, sino que invierten el estado de otros, así que la combinación ya no es tan sencilla. Y más adelante, además, el orden en que se pulsen también tendrá importancia. Tampoco es para preocuparse, porque con cuatro interruptores no es demasiado complicado, pero lo suficiente como para que no sea inmediato.

Y la cosa será, espero, bastante progresiva.

Estoy planeando, además, que tras cada nivel se ofrezca al jugador un código para que, entrándolo al principio de una partida, pueda seguir donde lo dejó. Creo que esto va a ser importante en un juego que puede tener (en principio) 99 niveles. Aunque podría tener más...

Avatar de Usuario
Chema
Mensajes: 1536
Registrado: 21 Jun 2012 20:13
Ubicación: Gijón
Agradecido : 441 veces
Agradecimiento recibido: 186 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor Chema » 16 Ene 2014 20:49

Hala, vamos con el siguiente capítulo.
-bRick

Sincronización con el barrido vertical

Para poder sincronizar con el barrido vertical necesitamos una interrupción que salte cada vez que se finalice un dibujado de la imagen (más o menos). En el Oric no se dispone de dicha señal, pero se puede modificar el contador de la vía T1 que decide cada cuánto se envía una interrupción a la CPU.

Fabrice Francés (el autor del emulador Euphoric y un monstruo de la comunidad) diseño una rutina de calibración simple para sincronizar ese contador con el barrido de pantalla. La rutina es simple: cambia el color de papel de la pantalla cuando se produce una interrupción, espera un tiempo y luego lo restaura. Si el cambio se produce en medio del dibujado de la imagen, sólo un trozo aparecerá con ese color de papel.

Además la rutina lee el teclado y usa una tecla para incrementar el contador de T1 y otra para decrementarlo. Una tercera tecla sirve para salir. Comenzamos con el contador a un valor que genere interrupciones a 50Hz. Si decrementamos el contador, se producen las interrupciones en menos tiempo del que tarda el barrido y la barra se desplaza hacia arriba. Si lo incrementamos pasa lo contrario.

Basta con que el usuario use las teclas para situar la barra (el trozo) de color de papel diferente al final de la pantalla (y que no se desplace) para tener las interrupciones sincronizadas con el barrido vertical.

Vamos a usar esa rutina para nuestros propósitos, pero primero vamos a echar unos números.

Tenemos que actualizar la memoria de pantalla (es decir, volcar nuestro backbuffer) mientras la imagen no se está enviando a la TV. Creo que la tele usa modo entrelazado, así que en un barrido se actualizan 312 líneas (creo que es así, sino corregidme, por favor). En el Oric sólo 224 de esas líneas tienen información, el resto es borde, así que tenemos:

20 ms por cuadro, 312 líneas, dan como 6,4 microsegundos por línea. El tiempo que el Oric pinta borde es de 88 líneas (312-224), o sea 5,6 milisegundos o 5600 ciclos. El volcado de nuestra área de juego duraba 6731 ciclos. Nos pasamos, pero es que no toda la pantalla es área de juego, sino solo 160 líneas (8x20). O sea que tenemos el tiempo de 152 líneas, más de 9700 ciclos. Eso está mejor. Podemos hacer que el usuario sincronice la interrupción cuando comienza el dibujado del radar y tenemos tiempo suficiente para volcar el backbuffer. En el vídeo que ya os había puesto antes se ve este proceso:

https://www.youtube.com/watch?v=7WeFgFSwEJ4 ">http://www.youtube.com/watch?= https://www.youtube.com/watch?v=7WeFgFSwEJ4

El código del bucle principal debe ahora hacer:

Código: Seleccionar todo

    
   …
   jsr _clear_backbuffer
   jsr _render_background
   
   ; Draw the sprites over the background
   jsr _put_sprites
   
   ; Now lets synchronize with the vbl
   jsr waitirq

   ; Done, now we are (hopefully) on the BLANK period, dump
   ; the double buffer quickly
   jsr _dump_backbuffer



La rutina waitirq simplemente espera a que se produzca una interrupción antes de volcar el backbuffer, eliminando completamente el tearing. ¡Listo! ¿o no?

Pues hay algunas cosas que decir. Lo primeo es que si hacemos un cálculo rápido con lo que nos llevaba borrar, renderizar el fondo y volcar, estábamos como en 18000 ciclos. Si pintamos, digamos, 5 ó 6 sprites nos vamos rápidamente a 7000 ciclos más. Acabamos de pasarnos de los 20000. Por muy poco que nos pasemos, perderemos una interrupción y la sincronización nos hará esperar al siguiente cuadro.

Resultado: tenemos el juego a 25 cuadros por segundo. No está mal, pero sin sincronizar estaríamos fácilmente en más de 30 ó 35. Es el precio que se paga.

Solo espero que no se dé el caso de emplear más de 40000 ciclos o bajamos a 16 cuadros por segundo inmediatamente. Eso sí es una pérdida.
Vale, lo implementamos todo y nos ponemos a probar. Parece que todo va bien hasta que, de pronto nuestro sprite corrompe el fondo durante un cuadro y luego se arregla. Vemos que eso se da cada cierto tiempo, así como otros fallos esporádicos en los gráficos. ¿Qué ocurre?
Pues ocurre que, cuando pintamos los sprites con su máscara estamos modificando el contenido de un carácter que está en pantalla y, como está fuera del sincronizado, puedes pillarlo en medio del volcado de la imagen.

Imaginemos que no ha sido pintado y resulta que nos ponemos a pintarlo en otro sitio. Lo primero que hace la rutina es colocar el carácter en el nuevo lugar, pero en el backbuffer, no en pantalla. Luego procedemos a dibujarlo enmascarado con el nuevo fondo. Si antes de volcar el backbuffer toca un barrido, el gráfico pensado para la nueva posición, se va a pintar en la antigua. Ahí está el fallo.

No estaría mal que nos diese tiempo a hacerlo todo en los 20000 ciclos, pero no se puede. Tampoco podemos dibujar los sprites tras detectar la interrupción, justo antes de volcar el backbuffer, porque nos pasamos del tiempo que calculamos antes (9700 ciclos).

Para solucionar esto hay que usar un pedazo de truco. Usar dos juegos de caracteres libres para dibujar los sprites de manera alternativa, así no corrompemos nunca el que pueda pintarse en pantalla.

Lo malo es que eso nos reduce el número de sprites que podemos tener a la vez a la mitad. De 20 a 10. De todas formas pintar 10 sprites a la vez en pantalla lleva ya demasiado tiempo, así que no es tan terrible.

Para los que quieren código, esto se hace justo después del jsr _reder_background, y cambia en la tabla que vimos los caracteres a usar. En la tabla aparecen inicialmente sólo pares y en cada bucle de juego se alterna con los impares:

Código: Seleccionar todo

   ; Update sprite tiles to use this frame
   ; Using two sets alternatively avoids having to draw them
   ; while the screen is not being refreshed, which was too
   ; tight when more than 5-6 sprites on screen.
   ; Drawback: we duplicate the tiles needed :/
   lda frame_counter
   and #1   
   beq ones
   lda #$de   ; Opcode for dec abs,x
   .byt $2c
ones
   lda #$fe    ; Opcode for inc abs,x
   sta loop
   ldx #(MAX_SPRITES+1)*4-1
loop
   inc sprite_tile11,x
   dex
   bpl loop


Vale, el código es algo ofuscado, pero funciona. También se deja para justo después del volcado el actualizar el gráfico de las estrellas. Eso lleva poco tiempo y cabe antes de que comience la actualización de pantalla. Si no lo hacemos aquí, no queda sincronizado con el barrido y, de nuevo, aparecen errores gráficos.

El motor ya está listo. Pero quedan mogollón de cosas....

dancresp
Mensajes: 4993
Registrado: 13 Nov 2010 02:08
Agradecido : 14 veces
Agradecimiento recibido: 83 veces

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor dancresp » 17 Ene 2014 00:03

Ciertamente Chema no está quieto, pero no seré yo el que le diga que pare.
Es más... sigue y no pares. :-)

Avatar de Usuario
Chema
Mensajes: 1536
Registrado: 21 Jun 2012 20:13
Ubicación: Gijón
Agradecido : 441 veces
Agradecimiento recibido: 186 veces
Contactar:

Re: ¡¡Nuestro compañero Chema no está quieto!!

Mensajepor Chema » 20 Ene 2014 11:50

Bueno, vamos con algunos aspectos de más alto nivel que, creo, son interesantes.

-bRick

Generación Procedural de la Nave Enemiga

Ya tenemos el motor funcionando y el siguiente paso que vamos a ver es el diseño de las naves nodriza enemigas para cada nivel. Es algo así como el fondo donde se mueve el jugador y el resto de elementos del juego.

El "mapa" del nivel ocupa 5K (256 celdas y 20 filas). Podríamos pensar en diseñar varias naves enemigas para el fondo y luego comprimir los datos. En cada nivel extraemos los datos correspondientes en la memoria y listo. Hay multitud de técnicas para guardar los datos comprimidos, y se pueden conseguir compresiones muy altas para guardar muchas pantallas o diseños.

Normalmente para poder comprimir mucho, necesitamos elementos que se repitan, así que a mayor compresión, menos variedad. Supongamos que conseguimos comprimir cada nave nodriza en... yo qué se, pongamos 1K. ¿Cuántos niveles podemos poner? Depende de la memoria libre, pero ya vemos que 10 niveles serían 10K, lo que es bastante. ¿Y si queremos tener 50 niveles? o ¿70?... Nos quedamos rápidamente sin memoria.

Pero hay una alternativa: la generación procedural (ver artículo en Wikipedia). La cosa es generar el contenido algorítmicamente en lugar de manualmente. La cosa es simple (y complicada a la vez). Necesitamos un generador de secuencias a partir de una semilla. Por ejemplo una implementación de la serie de Fibonacci o un generador de pseudo-aleatorios. La cosa es que, para una semilla determinada se genere siempre la misma secuencia de números cada vez que llamemos a la función.

Ahora podemos usar algoritmos que usen los datos generados por la función para ir construyendo el contenido. Cosas como llamar a la función y dependiendo de ciertos bits del resultado generar un bloque de un tamaño determinado, con esquinas cuadradas o en chaflán, con determinados elementos en su superficie, etc. Podemos ir llamando a la función cada vez que tengamos que decidir el tipo de contenido o su localización. Hacemos lo mismo para generar espacios entre bloques (o no), para elegir cómo los conectamos entre sí, etc.

Son todo reglas que operan en base a los datos que se van generando. Como son siempre los mismos para una semilla dada, pues siempre sale la misma nave. La mayor dificultad está en ajustar el generador para que el contenido sea correcto y suficientemente variado.

Las semillas de cada nivel se generan a partir de la inicial del nivel anterior. Así podemos ir a un nivel determinado sin más que llamar a una función las veces que haga falta y luego llamar al generador.

Esta misma técnica la usaba Elite (y se importó para 1337) para generar todo el universo del juego. Ahí sí que era complicado, porque el contenido era muy rico y complejo. Aquí es algo más simple, pero suficiente para dar dolor de cabeza al programador.

Aquí vemos un ejemplo de cómo el generador va construyendo la base de la nave. Fue un primer intento que implementé en Matlab para probar, pero está bien para ver cómo funciona la cosa (sólo una parte, porque el completo ocupaba mucho):

creacion2b.gif
Creación
creacion2b.gif (969.28 KiB) Visto 2608 veces


El generador implementado en el juego es ligeramente diferente y ocupa ahora mismo 2559 bytes. Tiene algún fallo menor, pero está casi completo al 100%. No está mal. Con menos de 3K tenemos para generar, en principio, infinitos niveles, aunque no puedo asegurar que la secuencia no se acabe repitiendo. Probablemente el juego se quede en un número de niveles máximo del orden de 100.

Además es bastante rápido, así que el usuario no nota ningún retardo entre niveles por su causa.

Dejamos aquí este tema. Si alguien quiere más detalles técnicos o aclaraciones en algún punto, que lo pida. Yo encantado.

En la próxima edición a ver si empezamos a hablar de los enemigos y la IA.


Volver a “Oric”

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado