We've just updated MediaWiki and its underlying software. If anything doesn't look or work quite right, please mention it to us. --RanAS

es/DynamicZandDyzenQuickGuide: Difference between revisions

From SnesLab
Jump to: navigation, search
 
(67 intermediate revisions by the same user not shown)
Line 5: Line 5:


= Instalación de Dynamic Z =
= Instalación de Dynamic Z =
== Pre-requisitos ==
Para que la aplicación "Dynamic Z Installer.exe" o "Dynamic Resource adder.exe" funcionen, debes tener instalado:
*dotNet Core 3.1[https://dotnet.microsoft.com/download]
== Lorom ==
== Lorom ==
<ol start="1">
<ol start="1">
Line 19: Line 23:
*'''Dynamic Sprite Support:''' Es necesario para utilizar Sprites Dinámicos convencionales. Requiere '''Graphics and Tilemap Change'''.
*'''Dynamic Sprite Support:''' Es necesario para utilizar Sprites Dinámicos convencionales. Requiere '''Graphics and Tilemap Change'''.
*'''Shared Dynamic Sprite Support:''' Es necesario para utilizar Sprites Dinámicos con memoria compartida. Requiere '''Dynamic Sprite Support'''.
*'''Shared Dynamic Sprite Support:''' Es necesario para utilizar Sprites Dinámicos con memoria compartida. Requiere '''Dynamic Sprite Support'''.
*'''Semi Dynamic Sprite Support:''' Es necesario para utilizas Sprites Semi-Dinámicos. Requiere '''Graphics and Tilemap Change'''.
*'''Semi Dynamic Sprite Support:''' Es necesario para utilizar Sprites Semi-Dinámicos. Requiere '''Graphics and Tilemap Change'''.
*'''Palette Change System:''' Es necesario para realizar cambios de paletas dinámicos.
*'''Palette Change System:''' Es necesario para realizar cambios de paletas dinámicos.
*'''HSV Support:''' Es necesario para realizar efectos de paletas de colores tales como cambiar el Hue, Saturación o Valor, tambien para mezclar un color base con los colores de la paleta. Requiere '''Palette Change System'''.
*'''HSL Support:''' Es necesario para realizar efectos de paletas de colores tales como cambiar el tinte, Saturación o brillo (Hue-Saturation or Lightness), tambien para mezclar un color base con los colores de la paleta. Requiere '''Palette Change System'''.
*'''Player Graphic Change System:''' Permite cambiar los gráficos del player tanto siguiendo el mismo esquema de graficos que el player original, como hacer tu propia rutina grafica para players de cualquier tamaño. Requiere '''Graphics and Tilemap Change'''.
*'''Player Graphic Change System:''' Permite cambiar los gráficos del player tanto siguiendo el mismo esquema de graficos que el player original, como hacer tu propia rutina grafica para players de cualquier tamaño. Requiere '''Graphics and Tilemap Change'''.
*'''Player Palette Change System:''' Permite cambiar la paleta de color del player (Paleta 8) dinámicamente. Requiere '''Palette Change System'''.
*'''Player Palette Change System:''' Permite cambiar la paleta de color del player (Paleta 8) dinámicamente. Requiere '''Palette Change System'''.
Line 72: Line 76:
*'''Semi Dynamic Sprite Support:''' Es necesario para utilizar Sprites Semi-Dinámicos. Requiere '''Graphics and Tilemap Change'''.
*'''Semi Dynamic Sprite Support:''' Es necesario para utilizar Sprites Semi-Dinámicos. Requiere '''Graphics and Tilemap Change'''.
*'''Palette Change System:''' Es necesario para realizar cambios de paletas dinámicos.
*'''Palette Change System:''' Es necesario para realizar cambios de paletas dinámicos.
*'''HSV Support:''' Es necesario para realizar efectos de paletas de colores tales como cambiar el Hue, Saturación o Valor, tambien para mezclar un color base con los colores de la paleta. Requiere '''Palette Change System'''.
*'''HSL Support:''' Es necesario para realizar efectos de paletas de colores tales como cambiar el tinte, Saturación o brillo (Hue-Saturation or Lightness), tambien para mezclar un color base con los colores de la paleta. Requiere '''Palette Change System'''.
*'''Player Graphic Change System:''' Permite cambiar los gráficos del player tanto siguiendo el mismo esquema de graficos que el player original, como hacer tu propia rutina grafica para players de cualquier tamaño. Requiere '''Graphics and Tilemap Change'''.
*'''Player Graphic Change System:''' Permite cambiar los gráficos del player tanto siguiendo el mismo esquema de graficos que el player original, como hacer tu propia rutina grafica para players de cualquier tamaño. Requiere '''Graphics and Tilemap Change'''.
*'''Player Palette Change System:''' Permite cambiar la paleta de color del player (Paleta 8) dinámicamente. Requiere '''Palette Change System'''.
*'''Player Palette Change System:''' Permite cambiar la paleta de color del player (Paleta 8) dinámicamente. Requiere '''Palette Change System'''.
Line 112: Line 116:
.Normal
.Normal
Klaptrap
Klaptrap
Klump
Gnawty
Krusha
SigmaX2
SMM2BulletBill
DKCBarrel
DKCFire
.Cluster
.Cluster
DKCContactEffect
DKCContactEffect
DKCExplosion
DKCVerticalSmoke
DKCHorizontalSmoke
.Extended
.Extended
KlumpGrenade
KlumpGrenade
Line 135: Line 129:
.Normal
.Normal
Klaptrap
Klaptrap
Klump
Gnawty
Krusha
SigmaX2
SMM2BulletBill
DKCBarrel
DKCFire
.Cluster
.Cluster
DKCContactEffect
DKCContactEffect
DKCExplosion
DKCVerticalSmoke
DKCHorizontalSmoke
.Extended
.Extended
KlumpGrenade
KlumpGrenade
Line 157: Line 141:


Alternativamente puede utilizarse en consola tambien con el comando:
Alternativamente puede utilizarse en consola tambien con el comando:
 
<pre>
DynamicResourceAdder <nombre del rom.smc> <lista de recursos.txt> <carpeta de pixi>
DynamicResourceAdder <nombre del rom.smc> <lista de recursos.txt> <carpeta de pixi>
</pre>
</li>
<li>
Luego de insertar los recursos dinámicos (como GFXs, paletas de colores o tilemaps), inserta los archivos que utilizan esos recursos (como los sprites, bloques, códigos uberasm, parches, o cualquier archivo que utilice los recursos dinámicos insertados con el tool).
</li>
</li>
</ol>
</ol>
'''Nota: cada vez que insertes recursos dinámicos debes insertar todos los archivos que utilicen esos recursos dinámicos, si no se realiza esto, los gráficos, paletas o tilemaps utilizados por esos archivos se podrían ver glitcheados.'''


Para utilizar este tool, el archivo que importa los recursos dinámicos, en la primera linea de código debe incluir todos los recursos que utiliza de la siguiente manera:
Para utilizar este tool, el archivo que importa los recursos dinámicos, en la primera linea de código debe incluir todos los recursos que utiliza de la siguiente manera:
Line 175: Line 165:
Luego cada uno de estos recursos puede ser referenciado utilizando los defines "!GFXYY" donde YY es el numero del recurso, por ejemplo:
Luego cada uno de estos recursos puede ser referenciado utilizando los defines "!GFXYY" donde YY es el numero del recurso, por ejemplo:


*!GFX00: Referenciaría al primer recurso.
*!GFX00: Referenciaría al primer recurso. En Klaptrap seria la dirección de memoria de el archivo Klaptrap.bin.
*!GFX01: Referenciaría al segundo recurso.
*!GFX01: Referenciaría al segundo recurso. En Klaptrap seria la dirección de memoria de el archivo KlaptrapPal1.bin.
*!GFX02: Referenciaría al tercer recurso. En Klaptrap seria la dirección de memoria de el archivo KlaptrapPal2.bin.
*!GFX03: Referenciaría al cuarto recurso. En Klaptrap seria la dirección de memoria de el archivo KlaptrapPal3.bin.
*!GFX04: Referenciaría al quinto recurso. En Klaptrap seria la dirección de memoria de el archivo KlaptrapPal4.bin.
*!GFX05: Referenciaría al sexto recurso. En Klaptrap seria la dirección de memoria de el archivo KlaptrapPal5.bin.


y asi sucesivamente.
y asi sucesivamente.
Line 201: Line 195:
Puedes utilizar cualquier programa de edición de imágenes para hacer los gráficos. Los gráficos deben cumplir las siguientes condiciones:
Puedes utilizar cualquier programa de edición de imágenes para hacer los gráficos. Los gráficos deben cumplir las siguientes condiciones:


*La cantidad total de colores utilizados por los gráficos es máximo 15 colores.
*La cantidad total de colores utilizados por los gráficos es máximo 15 colores. Entre estos 15 colores no se considera el color del Background.
*El fondo debe ser transparente (color 00000000).
*El fondo debe ser transparente (color #00000000).
*Considera que los colores en SNES utilizan formato de color BGR555, por lo tanto, se recomienda que los canales RGB de los colores terminen en 0 o en 8.
*Considera que los colores en SNES utilizan formato de color BGR555, por lo tanto, se recomienda que los canales RGB de los colores terminen en 0 o en 8, además que van desde 0 hasta 248.
*No hagas gráficos demasiado grandes, considera que tras la optimización de los gráficos, los tiles deben caber en un espacio de máximo 128x32 (128x48 si usas 50% more mode).
*No hagas gráficos demasiado grandes, considera que tras la optimización de los gráficos, los tiles deben caber en un espacio de máximo 128x32 (128x48 si usas 50% more mode).
*Los gráficos deben seguir un formato de Sprite Sheet donde cada frame utilice un tamaño del mismo tamaño y frames vacios.
*Los gráficos deben seguir un formato de Sprite Sheet donde cada frame debe utilizar la misma cantidad de espacio y no puede haber frames vacíos.


Ejemplo:
Ejemplo:
Line 221: Line 215:
[[File:Cuadrado Central.png|center|Central Square]]
[[File:Cuadrado Central.png|center|Central Square]]


Por lo tanto, debes ajustar la posición de los gráficos de manera que se coordine con el cuadrado mostrado en el CFG Editor.
Por lo tanto, debes ajustar la posición de los gráficos de manera que se coordine con el cuadrado mostrado en el CFG Editor. Ten en consideración que en el CFG Editor, los puntos rojos muestran el object clipping y el cuadrado verde oscuro muestra la posición del sprite.


= Dyzen para Sprites Dinámicos =
= Dyzen para Sprites Dinámicos =
== Pre-Requisitos ==
Para que Dyzen funcione, requiere:
*dotNet Framework 4.8 [https://dotnet.microsoft.com/download]
== Funcionamiento ==
Al abrir Dyzen te aparecerá el siguiente mensaje:
Al abrir Dyzen te aparecerá el siguiente mensaje:


Line 275: Line 273:


El resto de opciones se explican solas.
El resto de opciones se explican solas.
Luego de rippear los gráficos, podrás visualizar cada uno seleccionando en el apartado "Frames" en el menu de la derecha.
Luego en el apartado "Animations" puedes crear animaciones, para esto apretamos el boton "New". Para añadir frames a la animación los seleccionamos del menu de arriba a la derecha, si quieres seleccionar varios frames al mismo tiempo manten presionado la tecla "ctrl" y ve seleccionando los frames que deseas. Luego de seleccionar los frames, aprieta el boton "+" para añadirlos a la animación.
Para las animaciones hay 2 opciones:
*'''Only Once:''' La animación se reproduce 1 sola vez.
*'''Continuous:''' La animación se repite continuamente.
Luego podras ir al apartado de Interaction para crear las cajas de colisión. Si quieres que todos los frames usen las mismas cajas de colisión, presionas la opción "Share with all Frames", si deseas que cada frame tenga sus propias cajas de colisión presionas la opción "Don't Share Interaction".
Un Truco para hacer el trabajo más rápido en la parte de interacción es apretar la opción "Share with all Frames", crear las cajas de colisión para un frame y luego cambiar a la opción "Don't Share Interaction", luego de eso todos los frames tendran las cajas creadas mientras se utilizo la opción "Share with all Frames" pero podrás modificar las cajas para que se adapten a cada frame.
Una vez terminado el sprite con sus frames, animaciones y cajas de colisión presiona la opción "File->Extract To", selecciona una carpeta y aparecera esta ventana.
[[File:Dyzen Extract To.png|center|Dyzen Extract To]]
*'''Code:''' Es para que exporte el código del sprite.
**'''Flip X:''' Es para que el sprite pueda voltear horizontalmente.
**'''Flip Y:''' Es para que el sprite pueda voltear Verticalmente.
*'''SP1:''' Es para que exporte los gráficos en SP1.
*'''SP2:''' Es para que exporte los gráficos en SP2.
*'''SP3:''' Es para que exporte los gráficos en SP3.
*'''SP4:''' Es para que exporte los gráficos en SP4.
*'''Palette:''' Es para que exporte la paleta de colores.
Se recomienda seleccionar todas las opciones a excepción de Flip Y, en caso que tu sprite no necesite ser flippeado en X, tambien quitar la opción Flip X.
= Creando un CFG para el sprite =
Abrimos el CFG Editor de pixi:
[[File:CFG Editor 1.png|center|CFG Editor 1]]
Las relevante es:
*Cambiar la opción de Normal a Custom.
*En la opción "Extra Byte Count", pon al menos 1 (puede ser hasta 12, quizás a futuro si el LM lo admite puedes poner más) en Extra Bit Clear y Extra Bit Set.
*Si utilizas el sistema de interacción de Dyzen debes seleccionar las opciones:
**'''Process interaction with Mario Every Frame'''
**'''Don't use default interaction with Mario'''
*El Act Like suele ser 36, en muy pocos casos tiene otro valor, se recomienda en el 90% de los casos utilizar el valor 36.
Las demás opciones son a gusto de cada uno, solo ten en consideración poner el Object Clipping que mejor se ajuste a tu sprite y la opción "Don't disable clipping when starkilled" puede ser útil cuando quieres hacer una animación de muerte para tu sprite. Aqui por ejemplo el CFG de Klaptrap.
[[File:Klaptrap CFG.png|center|Klaptrap CFG]]


= Explicando el Sistema de animación =
= Explicando el Sistema de animación =
El sistema de animación tiene las siguientes variables:
<pre>
!FrameIndex = !SpriteMiscTable1
!AnimationTimer = !SpriteMiscTable7
!AnimationIndex = !SpriteMiscTable2
!AnimationFrameIndex = !SpriteMiscTable3
!LocalFlip = !SpriteMiscTable4
!GlobalFlip = !SpriteMiscTable5
!LastFrameIndex = !SpriteMiscTable6
</pre>
*'''!FrameIndex:''' Esta variable tiene el identificador del Frame que es mostrado en pantalla por el sprite por la rutina gráfica.
*'''!AnimationTimer:''' Esta variable almacena la cantidad de Frames (entendidos como Game Loops, cuadros de animación), en los que el cuadro de animación actual se mostrara en pantalla.
*'''!AnimationIndex:''' Esta variable almacena el identificador de la animación que es reproducida actualmente por el sprite.
*'''!AnimationFrameIndex:''' Indica que frame dentro de la animación actual esta siendo mostrado por el sprite, por ejemplo si el sprite tuviera una animación con los frames 1, 5 y 3 (en ese orden), !AnimationFrameIndex tendrá el valor 0 si esta en el valor 1 (primer valor), 1 si esta en el frame 5 (segundo valor) y 2 si esta en el frame 3 (tercer valor). De esta manera una animación puede tener repetido el mismo frame varias veces.
*'''!LocalFlip:''' Esta variable indica si la animación actual esta volteando el frame que esta reproducido actualmente, este valor no debe ser modificado fuera de la rutina de animación por el sistema de Dyzen.
*'''!GlobalFlip:''' Esta variable se utiliza para voltear al sprite tanto horizontal como verticalmente.
*'''!LastFrameIndex:''' Esta variable indica el valor que tuvo !FrameIndex al momento de enviar sus gráficos a la VRAM. Esta variable es modificada por la rutina dinámica del Dynamic Z.
Para cambiar el frame o la animación del sprite manualmente debes llamar la siguiente macro:
<pre>
%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
</pre>
Devuelve el flag Z activado si en el game loop actual se puede cambiar de frame y 0 sino. Ejemplo de uso:
<pre>
%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ .DontChangeFrame
LDA #$01
STA !FrameIndex,x
.DontChangeFrame
RTS
</pre>
Cambiaria el frame al con ID #$01.
Esta macro puede ser utilizada para sprites normales, clusters, extended y overworld de la siguiente manera:
*'''%CheckEvenOrOdd("DZ_DS_Loc_US_Normal"):''' Para Normal Sprites.
*'''%CheckEvenOrOdd("DZ_DS_Loc_US_Cluster"):''' Para Cluster Sprites.
*'''%CheckEvenOrOdd("DZ_DS_Loc_US_Extended"):''' Para Extended Sprites.
*'''%CheckEvenOrOdd("DZ_DS_Loc_US_OW"):''' Para Overworld Sprites.
Esto se realiza debido a que por default los sprites del Dynamic Z son sprites 30FPS, esta macro se utiliza para sincronizar las rutinas graficas de los sprites dinámicos y que no se muestren gráficos basura. En el caso de los sprites 60FPS, no es necesario utilizarla.
Dyzen además crea rutinas para cambiar entre las distintas animaciones. Estas rutinas se generara 1 por cada animación creada el dyzen y tienen como nombre ChangeAnimationFromStart_<nombre de animación>. pueden ser llamadas de la siguiente manera (suponiendo que tengo una animación para caminar):
<pre>
JSR ChangeAnimationFromStart_Walk
</pre>
Esto cambiaria la animación del sprite a la animación de caminar.
Debes mezclarlo con la macro que se comentaba antes, por lo tanto quedaria asi:
<pre>
%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ +
JSR ChangeAnimationFromStart_Walk
+
RTS
</pre>
== Tablas de la rutina de animación ==
Hay tablas que manejan el funcionamiento de cada una de las animaciones, estas animaciones son creadas por Dyzen sin embargo el usuario podría querer editarlas manualmente para añadir o modificar animaciones preexistentes, estas son:
*'''AnimationLenght:''' Almacena la cantidad de frames que posee cada animación. Cada valor es de 16 bits.
*'''AnimationLastTransition:''' Almacena el valor que debe tomar la variable "!AnimationFrameIndex", luego de que la animación llegue a su fin. De momento desde Dyzen permite 2 valores posibles, la cantidad de frames de la animación - 1 (en este caso seria una animación "Only Once") o 0 (en este caso seria una animación continua). Puedes modificar este valor para que la animación luego se repita desde cierto frame en adelante, de esa manera podrias hacer una animación que tiene una introducción y luego se repite la sección final. Cada valor es de 16 bits.
*'''AnimationIndexer:''' Almacena el índice desde donde inician los datos de cada animación en las distintas tablas. En caso que quieras añadir animaciones de forma manual, tendrías que considerar el índice de la ultima animación y sumarle la cantidad de frames que trae la ultima animación y debería darte el valor para tu nueva animación. Cada valor es de 16 bits.
*'''Frames:''' Almacena los indices de cada frame que contiene cada animación. Cada valor es de 8 bits. Para el 99% de los sprites un indice de 8 bits es más que suficiente, permitiendo hasta 255 frames, en caso que tu sprite utilice más de 255 frames, tendrias que modificar las rutinas para que funcionen con indices de 16 bits, en ese caso, esta tabla seria de 16 bits. La rutina de animación de player por default es de 16 bits, ya que, es más común que los players superen los 255 frames.
*'''Times:''' Almacena cuantos game loops (SNES Frame), deben transcurrir para que transicionar al siguiente frame. El valor minimo es 0 y el valor máximo es 255. En el caso de los sprites dinámicos de 30FPS (que son los que por default crea Dyzen), los valores de esta tabla deben ser pares, (0, 2, 4, etc.). Cada valor es de 8 bits.
Valores de referencia para un sprite normal o un sprite dinámico 60FPS:
{| class="wikitable"
|-
! Valor !! Framerate NTSC !! Framerate PAL
|-
| 0 || 60 FPS || 50 FPS
|-
| 1 || 30 FPS || 25 FPS
|-
| 2 || 20 FPS || 16.7 FPS
|-
| 3 || 15 FPS || 12.5 FPS
|}
Valores de referencia para un sprite dinámico 30FPS:
{| class="wikitable"
|-
! Valor !! Framerate NTSC !! Framerate PAL
|-
| 0 || 30 FPS || 25 FPS
|-
| 2 || 15 FPS || 12.5 FPS
|-
| 4 || 10 FPS || 8.3 FPS
|-
| 6 || 7.5 FPS || 6.25 FPS
|}
*'''Flips:''' Indica como debe voltearse un frame al ser reproducido por la animación, esta tabla puede no aparecer siempre, depende de si al momento de crear un sprite en Dyzen, alguna de las animaciones tuvo al menos 1 frame que haya sido flipeado, además los valores de la tabla se pueden utilizar solamente si se permitió en Dyzen el Flip Horizontal o Vertical. Cada valor puede tomar solo los valores 0, 1, 2 y 3. Cada valor es de 8 bits.
**'''$00:''' No Flipea.
**'''$01:''' Flipea Horizontalmente. Requiere haber puesto en Dyzen al momento de exportar la opción "Flip X".
**'''$02:''' Flipea Verticalmente. Requiere haber puesto en Dyzen al momento de exportar la opción "Flip Y".
**'''$03:''' Flipea Horizontal y Verticalmente. Requiere haber puesto en Dyzen al momento de exportar las opciones "Flip X" y "Flip Y".
= Sistema de interacción y cajas de colisión =
= Sistema de interacción y cajas de colisión =
El sistema de interacción funciona asignándole cajas de colisión a cada frame, cada caja de colisión tiene 2 acciones que se pueden ejecutar al momento de entrar en contacto, una acción ocurre cuando la caja entra en contacto y otra función ocurre cuando la caja no entra en contacto. La información de la interacción queda almacenada de la siguiente manera:
*'''<code>$00</code>:''' Posición X del limite izquierdo de la caja de colisión del objetivo. 16 bits.
*'''<code>$08</code>:''' Posición Y del limite superior la caja de colisión del objetivo. 16 bits.
*'''<code>$02</code>:''' Posición X de el limite derecho de la caja de colisión del objetivo. 16 bits.
*'''<code>$0C</code>:''' Posición Y del limite inferior de la caja de colisión del objetivo. 16 bits.
*'''<code>$4D</code>:''' Información de la colisión. En la acción que es ejecutada cuando la caja de colisión no hace contacto pueden ser utilizada para saber datos sobre la posición relativa entre la caja de colisión y el objetivo. Si hubo contacto entre la caja de colisión revisada y el objetivo tiene el valor <code>#$0F</code>, si no hubo colisión tiene los siguientes valores.
**'''<code>#$01</code>:''' No hubo contacto en ningún eje. La caja de colisión actual esta abajo del objetivo.
**'''<code>#$02</code>:''' No hubo contacto en ningún eje. La caja de colisión actual esta arriba del objetivo.
**'''<code>#$03</code>:''' Hay contacto pero solo en el eje vertical.
**'''<code>#$04</code>:''' No hubo contacto en ningún eje. La caja de colisión actual esta a la derecha del objetivo.
**'''<code>#$08</code>:''' No hubo contacto en ningún eje. La caja de colisión actual esta a la izquierda del objetivo.
**'''<code>#$0C</code>:''' Hay contacto pero solo en el eje horizontal.
**'''<code>#$0F</code>:''' Hay contacto.
*'''<code>$04</code>:''' Posición X del sprite. 16 bits.
*'''<code>$0A</code>:''' Posición Y del sprite. 16 bits.
*'''<code>$45</code>:''' Posición X del limite izquierdo de la caja de colisión que esta siendo revisada actualmente. 16 bits.
*'''<code>$47</code>:''' Posición Y del limite superior la caja de colisión que esta siendo revisada actualmente. 16 bits.
*'''<code>$49</code>:''' Posición X de el limite derecho de la caja de colisión que esta siendo revisada actualmente. 16 bits.
*'''<code>$4B</code>:''' Posición Y del limite inferior de la caja de colisión que esta siendo revisada actualmente. 16 bits.
Luego que se revisa la interacción de todas las cajas, se tiene reservada la dirección de RAM <code>$4F</code> que puede utilizarse para llamar una rutina basandose en que valor le pusieron a esa dirección, esto es optativo, pero en la practica se suele usar en muchas interacciones distintas.
== Tablas de la Rutina de Interacción ==
El sistema de interacción utiliza (por cada tipo de colisión), una tabla llamada '''HitboxTables''', esta es una tabla que almacena las referencias al resto de tablas del sistema. Por Ejemplo:
<pre>
HitboxTables:
dw HitboxAdder
dw FrameHitboxesIndexer
dw FrameHitBoxes
dw HitboxType
dw HitboxXOffset
dw HitboxYOffset
dw HitboxWidth
dw HitboxHeight
dw HitboxAction1
dw HitboxAction2
dw Actions
</pre>
Podriamos crear diferentes versiones de esta tabla dependiendo contra que objetivo se desea colisionar. Por ejemplo, podriamos tener una version para colisión con el player, otra por colisión contra un sprite o contra bolas de fuego, etc. Al hacer esto, casi todas las referencias permaneceran igual, pero cambiaria la referencia a la tabla Actions, por ejemplo si usaramos el sistema para interacción con el player y para interacción con sprites, necesitariamos 2 tablas como estas:
<pre>
HitboxTablesPlayer:
dw HitboxAdder
dw FrameHitboxesIndexer
dw FrameHitBoxes
dw HitboxType
dw HitboxXOffset
dw HitboxYOffset
dw HitboxWidth
dw HitboxHeight
dw HitboxAction1
dw HitboxAction2
dw ActionsPlayer
HitboxTablesSprites:
dw HitboxAdder
dw FrameHitboxesIndexer
dw FrameHitBoxes
dw HitboxType
dw HitboxXOffset
dw HitboxYOffset
dw HitboxWidth
dw HitboxHeight
dw HitboxAction1
dw HitboxAction2
dw ActionsSprites
</pre>
Por otro lado, tendrías que crear esas tablas de Actions, se hablara más en detalle de la tabla Actions en la parte de '''Creando acciones para las cajas de colisión'''.
Las demás tablas son:
*'''HitboxAdder:''' Almacena 4 valores, se utilizan estos valores para desplazar el indice en que se empieza a leer los datos de las demás tablas dependiendo de si el sprite esta volteado o no. De esta manera la caja de colisión se adapta a el volteo del sprite. Los valores son de 16 bits.
*'''FrameHitboxesIndexer:''' Por cada Frame, se almacena un valor, este valor es utilizado como indice para buscar que cajas de colisión tiene cada frame, en la tabla '''FrameHitBoxes'''.
*'''FrameHitBoxes:''' Cada linea de la tabla representa que cajas de colisión tiene cada frame, el valor <code>$FF</code> se utiliza para indicar el final de la linea. Por ejemplo, si un frame tuviera las cajas de colisión 2, 4 y 9, tendria la linea los valores <code>$02,$04,$09,$FF</code>.
*'''HitboxType:''' Indica de que tipo es la caja de colisión, de momento no se utiliza, pero a futuro, se supone que podras utilizar cajas circulares o cajas en forma de rayo. Los valores son de 16 bits.
*'''HitboxXOffset:''' Indica la posición relativa en X de la caja de colisión con el sprite. Los valores son de 16 bits.
*'''HitboxYOffset:''' Indica la posición relativa en Y de la caja de colisión con el sprite. Los valores son de 16 bits.
*'''HitboxWidth:''' Indica el ancho de la caja de colisión. Los valores son de 16 bits.
*'''HitboxHeight:''' Indica el alto de la caja de colisión. Los valores son de 16 bits.
*'''HitboxAction1:''' Indica que '''ContactActions''' usa cada caja de colisión. Los valores son de 16 bits.
*'''HitboxAction2:''' Indica que '''NoContactActions''' usa cada caja de colisión. Los valores son de 16 bits.
*'''Actions:''' Almacena referencias a las '''ContactActions''' y '''NoContactActions''' que pueden utilizar las cajas de colisión. Los valores son de 16 bits.
Todas estas tablas son creadas por Dyzen.
== Creando acciones para las cajas de colisión ==
La rutina de colisión se basa en 3 tipos de acciones:
*'''DefaultAction:''' Se ejecutara si al terminar la rutina de colisión al menos 1 de las cajas de colisión puso un valor distinto de 0 a la dirección de RAM <code>$4F</code>.
*'''ContactAction:''' Se ejecutara cada vez que alguna de las cajas de colisión haga contacto con el objetivo. Puede ser 1 acción por caja de colisión o 1 acción por conjunto de cajas.
*'''NoContactAction:''' Se ejecutara cada vez que alguna de las cajas de colisión '''NO''' haga contacto con el objetivo. Puede ser 1 acción por caja de colisión o 1 acción por conjunto de cajas.
Para las '''ContactAction''' y las '''NoContactAction''', se deben poner en la tabla '''Actions''', puedes poner todas las que quieras, ejemplo:
<pre>
Actions:
dw ContactAction
dw NoContactAction
ContactAction:
RTL
NoContactAction:
RTL
</pre>
Una vez puesta en la tabla "Action", cada caja de colisión la referencia en las tablas '''HitboxAction1''' (para las '''ContactActions''') y '''HitboxAction2''' (para las '''NoContactAction'''), el valor que debe ir en estas tablas es el indice de la acción en la tabla '''Action''' multiplicado por 2 y en 16 bits, usando el ejemplo anterior, si quisiera que la caja usara la acción "ContactAction" en la tabla "HitboxAction1", entonces usaria el valor <code>$0000</code>, pero si quisiera usar la acción "NoContactAction", usaria el valor <code>$0002</code>. Todo esto es hecho por Dyzen, pero se explica básicamente por si alguien quisiera editarlo manualmente.
El siguiente ejemplo mostrara como hacer una interacción en la que el player puede saltar sobre el enemigo, pero es dañado si lo toca por la lateral o por abajo. Tambien el enemigo morira si el player usa la estrella. Primero haremos las '''ContactAction''' y '''NoContactAction'''.
<pre>
Actions:
dw CheckBounce
dw CheckPlayerIsAbove
CheckBounce:
%DyzenCheckBounce()
RTL
CheckPlayerIsAbove:
%DyzenCheckPlayerIsAbove()
RTL
</pre>
En este caso tendremos 2 funciones:
*'''CheckBounce:''' Sera nuestra '''ContactAction''', esta función en este caso llama a una rutina de la librería de Dyzen que lo que hace es, almacenar los datos de la caja de colisión que colisiono siempre y cuando no haya una colisión previa (<code>$4F</code> tiene el valor <code>#$01</code>) o que la caja de colisión este más arriba que la ultima que hizo contacto, además si identifica que la colisión ocurrió dentro de los 8 pixeles más arriba de la caja de colisión del objetivo, entonces pone la variable <code>!SpritePlayerIsAbove,x</code> con el valor <code>#$01</code>. Tambien pone la variable <code>$4F</code> con el valor <code>#$01</code>. Esta información se guarda de la siguiente manera:
**'''<code>$6C</code>: Posición X del limite izquierdo de la caja de colisión. 16 bits.
**'''<code>$6A</code>: Posición Y del limite superior la caja de colisión. 16 bits.
**'''<code>$6E</code>: Posición X de el limite derecho de la caja de colisión. 16 bits.
**'''<code>$8D</code>: Posición Y del limite inferior de la caja de colisión. 16 bits.
*'''CheckPlayerIsAbove:''' Sera nuestra '''NoContactAction''', esta función en este caso llama a una rutina de la libreria de Dyzen lo que hace es en caso que no haya colisión, guarda la información de la caja de colisión, siempre y cuando sea la más cercana al player, en caso que 2 cajas esten a la misma distancia, prioriza la que ese más arriba. La información es guardada del mismo modo que en "CheckBounce". Esta rutina no se ejecuta si alguna caja procesada previamente hizo contacto y puso la dirección de RAM <code>$4F</code> en un valor distinto a 0.
Ahora crearemos la '''DefaultAction''' que sera ejecutada si en algún momento se ejecuto "CheckBounce", para esto haremos una rutina de la siguiente manera:
<pre>
DefaultAction:
LDA $1490|!addr ;if player is using the star
BEQ + ;kill the sprite
.PlayerHasStar
RTS
+
LDA !SpritePlayerIsAbove,x
BNE .PlayerIsAbove
.PlayerIsNotAbove
RTS
.PlayerIsAbove
RTS
</pre>
Podremos identificar 3 secciones distintas dentro:
*'''.PlayerHasStar:''' Se ejecuta cuando el player esta usando la estrella. Lo más común seria utilizar la rutina de pixi "%Star()" en esta sección para matar al sprite.
*'''.PlayerIsNotAbove:''' Se ejecuta cuando el player no esta sobre el sprite. Lo más común seria utilizar la rutina de dyzen "%DyzenDamagePlayer()" para dañar al player cuando ocurra esto, pero tambien podria dejarse en blanco en caso que quieras hacer una plataforma o tambien podrias llamar a la rutina de muerte del player (<code>JSL $00F606|!rom</code>).
*'''.PlayerIsAbove:''' Se ejecuta cuando el player esta sobre el sprite. Esta es la sección más flexible, normalmente podrías utilizarla para que el player rebote sobre el sprite y dañe al enemigo, también podrias revisar si el player esta haciendo spinjump y hacer una u otra cosa dependiendo de eso, también podrías hacer una plataforma solida. Hay múltiples opciones en esta sección de la rutina.
Por ejemplo, para un sprite que puedes rebotarle encima, que lo matas con la estrella y que te daña en otro caso:
<pre>
DefaultAction:
LDA $1490|!addr ;if player is using the star
BEQ + ;kill the sprite
.PlayerHasStar
%Star()
RTS
+
LDA !SpritePlayerIsAbove,x
BNE .PlayerIsAbove
.PlayerIsNotAbove
%DyzenDamagePlayer()
RTS
.PlayerIsAbove
REP #$20
LDA $6A ;Obtiene la posición de el limite superior de la caja de colisión
SEC ;
SBC $0C ;Le resta la posición de el limite inferior de la caja del player
CLC ;para obtener la cantidad de pixeles que debe mover al player para que este sobre el sprite.
ADC !PlayerY ;
STA !PlayerY ;Mueve al player al tope del sprite
SEP #$20
JSL $01AA33|!rom    ;Hace que el Player rebote
%DyzenPrepareContactEffect() ;Prepara las variables para poder mostrar el efecto de contacto
JSL $01AB99|!rom    ;Crea el efecto de contacto 
RTS
</pre>
Si quieres utilizar '''%DyzenCheckBounce()''' y '''%DyzenCheckPlayerIsAbove()''', en el sprite init, debes incluir las siguientes 2 lineas:
<pre>
LDA #$00
STA !SpritePlayerIsAbove,x
</pre>
Además antes de la linea <code>JSR InteractionMarioSprite</code> debes incluir la rutina <code>%DyzenPrepareBounce()</code> y despues de <code>JSR InteractionMarioSprite</code> incluir la linea <code>%DyzenDetectPlayerIsAbove()</code>, de la siguiente manera:
<pre>
%DyzenPrepareBounce()
JSR InteractMarioSprite
%DyzenDetectPlayerIsAbove()
</pre>
= Cargar paleta de colores dinámicamente =
= Cargar paleta de colores dinámicamente =
Puedes cargar paletas dinamicamente, para esto, debes utilizar una macro del Dynamic Z llamada '''TransferToCGRAM''', la cual de utiliza de la siguiente manera:
<pre>
%TransferToCGRAM(CGRAMOffset, SourceAddr, SourceBNK, Length)
</pre>
Parametros:
*'''CGRAMOffset:''' Es la posición en la paleta de colores desde donde se escribiran los colores que enviaremos, considera que cada paleta es de 16 colores, por lo tanto si tuviera por ejemplo el valor <code>#$1F</code>, seria el color 15 en la paleta 1, por tanto esta tendria un formato de estilo:
**'''PPPP CCCC:'''
***'''PPPP:''' Paleta de color.
***'''CCCC:''' Id del color dentro de la paleta.
*'''SourceAddr:''' Dirección de memoria donde esta almacenada la paleta que queremos enviar. Debe ser de 16 bits.
*'''SourceBNK:''' Banco de la dirección de memoria donde esta almacenada la paleta que queremos enviar. Debe ser de 16 bits, aunque el high byte debe ser 0.
*'''Length:''' Cantidad de bytes que se envían, ten en consideración que cada color utiliza 2 bytes, por lo tanto, por cada color que quieres enviar, debes sumar 2 bytes, por ejemplo, si quisiera enviar 16 colores, tendria que enviar 32 bytes, por lo tanto el valor seria <code>#$0020</code>. Debe ser de 16 bits.
Supongamos que utilizando el '''DRAdder''', añadimos una paleta de 32 colores usando el tercer recurso ('''!GFX02'''), si quisiera enviarla para que reemplace los colores de la paleta 4 y 5, podríamos hacerlo con la siguiente linea de código:
<pre>
    %TransferToCGRAM(#$0040, #!GFX02, #!GFX02>>16, #$0040)
</pre>
Por ejemplo si tuvieras la siguiente tabla que tuviera los siguientes colores:
<pre>
ColorTable:
    dw $0001,$07FF,$041F
</pre>
Y quisiera mover esos 3 colores partiendo desde el color 3 de la paleta 7, seria lo siguiente:
<pre>
    %TransferToCGRAM(#$0073, #ColorTable, #ColorTable>>16, #$0006)
</pre>
Recuerda, el formato de color del Super Nintendo es el siguiente:
*'''0BBBBB GGGGG RRRRR:'''
**'''BBBBB:''' Canal azul.
**'''GGGGG:''' Canal verde.
**'''RRRRR:''' Canal Rojo.
== Efectos con paletas de colores ==
== Efectos con paletas de colores ==
Dynamic Z incluye un sistema para hacer efectos de paletas de colores, los efectos que puedes hacer son:
*'''Cambiar el Tinte (H):''' El tinte de un color (Hue en ingles) es lo que hace que el color tenga cierta tonalidad, los valores de tinte en el sistema de Dynamic Z van desde 0 a 31 (<code>#$1F</code>), aca hay algunos valores de referencia:
**'''<code>#$00</code>:''' Rojo.
**'''<code>#$05</code>:''' Amarillo.
**'''<code>#$0B</code>:''' Verde.
**'''<code>#$10</code>:''' Cyan.
**'''<code>#$15</code>:''' Azul.
**'''<code>#$1B</code>:''' Morado.
*'''Cambiar la Saturación (S):''' La saturación es lo que hace que el color se vea más vivo o se vea más gris, los valores de saturación van desde 0 a 31 (<code>#$1F</code>). Con saturación 0, la paleta de colores quedaria en tonalidades de gris, mientras que con saturación <code>#$1F</code>, la paleta de colores quedaria con el color en su versión más cargada.
*'''Cambiar el Brillo (L):''' El brillo (Lightness en ingles) es lo que hace que el color sea más oscuro o más brillante, los valores de brillo van desde 0 a 31 (<code>#$1F</code>). Un brillo de 0, seria equivalente al color negro, mientras que un brillo de <code>#$1F</code> seria equivalente a la versión más brillante de ese color.
*'''Cambiar Canales RGB:''' Puedes modificar los canales RGB de forma independiente usando el sistema de Dynamic Z, esto te permite por ejemplo mezclar un color base con los colores de la paleta
Este sistema consta de 4 partes, obtener la paleta de colores, transformar la paleta a una base procesable por el sistema, aplicar el efecto de color deseado y enviar la paleta modificada para que reemplace los colores del juego.
=== Obtener la paleta de colores ===
En este paso tenemos 2 opciones, la primera es usar una paleta de colores que hayamos creado nosotros mismos, en este caso podriamos por ejemplo usar el sistema del DRAdder para llamar un recurso usando <code>!GFXYY</code>, como tambien podriamos hacer una tabla con los colores que queremos y usar la tabla para obtener los colores. La segunda opción es obtener los colores que ya estan cargados en el mismo nivel, para esto utilizaremos la siguiente macro:
<pre>
    %TransferToCGRAMBuffer(Offset, Length)
</pre>
Esta macro lo que hace es enviar los colores que estan cargados al nivel, a un buffer. Por default, esta macro envia la información a un buffer llamado <code>DZ_PPUMirrors_CGRAM_PaletteCopy</code>, este buffer es de 512 bytes y cada 2 bytes seria un color de la paleta. Los parametros de esta macro son los siguientes:
*'''Offset:''' Te indica desde que color empezar a copiar la paleta de colores. Por ejemplo, <code>$73</code> seria el color 3 de la paleta 7.
*'''Length:''' Te indica cuantos bytes se copiaran, cada color toma 2 bytes. Debe ser de 16 bits.
Ejemplo de uso:
<pre>
    %TransferToCGRAMBuffer($01, #$01FE)
</pre>
Esto copiaria todos los colores dese el color 1 de la paleta 0, hasta el color 15 de la paleta 15 en el buffer <code>DZ_PPUMirrors_CGRAM_PaletteCopy</code>.
Luego de llamar esta macro debes esperar 1 Game Loop para ver los resultados reflejados en el buffer, ya que, no se puede leer la CGRAM fuera del NMI handler.
Si utilizas la macro '''TransferToCGRAMBuffer''', se recomienda usarla 1 frame despues del init y tener alguna manera de no volver a ejecutarla despues, solo necesitas llamarla 1 vez.
=== Procesar la base de colores ===
EL objetivo de este paso es separar los canales de los colores y transformarlos a otra base. Para esto hay 2 macros principalmente:
*'''<code>%SetHSLBase(sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>:''' Transforma colores BGR555 a una base de 3 canales independientes H, S y L de 5 bits cada uno. Donde el canal H es el Hue, el S es la saturación y el canal L es lightness.
*'''<code>%SetRGBBase(sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>:''' Transforma colores BGR555 a una base de 3 canales independientes R, G y B de 5 bits cada uno. Donde el canal R es el Rojo, el G es la verde y el canal B es azul.
Estas macros tienen los siguientes parametros:
*'''sourceAddr:''' Indica la dirección de memoria donde se obtienen los colores. Es de 16 bits.
*'''sourceBNK:''' Indica banco de la dirección de memoria donde se obtienen los colores. Es de 8 bits.
*'''destinationAddr:''' Indica la dirección de memoria donde se guardara la base de colores. Es de 16 bits.
*'''destinationBNK:''' Indica banco la dirección de memoria donde se guardara la base de colores. Es de 8 bits.
*'''length:''' Indica la cantidad de colores que se transformaran. Es de 16 bits.
Además de estas versiones de las macro existe la versión resumida:
*'''<code>%SetHSLBaseDefault(offset,length)</code>
*'''<code>%SetRGBBaseDefault(offset,length)</code>
Esto usa como source a el buffer <code>DZ_PPUMirrors_CGRAM_PaletteCopy</code> y como destination el buffer <code>DZ_PPUMirrors_CGRAM_BasePalette</code>. En este caso el parametro '''offset''', indicaria desde que color de la paleta de colores se empieza a hacer la transformación y debe ser de 16 bits.
Tambien existe una tercera version que es para usarla con el DRAdder o con tablas que tengan la paleta de colores almacenada:
*'''<code>%SetHSLBaseDRAdder(BinFile,destinationAddr,destinationBNK,length)</code>'''
*'''<code>%SetRGBBaseDRAdder(BinFile,destinationAddr,destinationBNK,length)</code>'''
En este caso en el parámetro '''BinFile''' debes poner en el caso de que sea un archivo binario que se haya sido incluido a través de DRAdder, usarías los defines <code>!GFXYY</code>, mientras que si usas una tabla, pondrías en ese parámetro el nombre de la tabla.
Algunos ejemplos de uso:
Establecer una base HSL de 16 colores usando como source la dirección guardada en las scratch <code>$00-$02</code> y usando como destination la dirección guardada en las scratch <code>$03-$05</code>.
<pre>
    %SetHSLBase($00,$02,$03,$05,#$0010)
</pre>
Establecer una base RGB de 48 colores usando como source al buffer <code>DZ_PPUMirrors_CGRAM_PaletteCopy</code> y como destination el buffer <code>DZ_PPUMirrors_CGRAM_BasePalette</code>, iniciando desde el primer color de la paleta 5.
<pre>
    %SetRGBBaseDefault(#$0050,#$0030)
</pre>
Establecer una base HSL de 64 colores usando como source una paleta de colores guardada en el quinto recurso dinamico designado por el DRAdder y guardandolo en el buffer <code>DZ_PPUMirrors_CGRAM_BasePalette</code>, iniciando desde el tercer color de la paleta 3.
<pre>
    %SetHSLBaseDRAdder(!GFX04,#DZ_PPUMirrors_CGRAM_BasePalette+($33*3),#DZ_PPUMirrors_CGRAM_BasePalette>>16,#$0040)
</pre>
Establecer una base RGB de 3 colores desde una tabla local y guardandolo en la dirección de RAM especificada en las scratch <code>$00-$02</code>.
<pre>
ColorTable:
    dw $0001,$07FF,$041F
</pre>
<pre>
    %SetHSLBaseDRAdder(ColorTable,$00,$02,#$0003)
</pre>
'''Nota: El procesamiento de estas macros es lento y no es necesario llamarlo cada frame, se recomienda llamarlo solo 1 vez despues de haber obtenido la paleta, recuerda que si utilizas las macro <code>TransferToCGRAMBuffer</code>, debes esperar 1 game loop para llamar las macros <code>SetBase</code>.'''
=== Hacer efectos de paleta ===
Para realizar este paso, debes llamar las macros '''Mix''', estas macros lo que hacen es ponderar los canales RGB o HSL (dependiendo de la macro), con los valores que pongas como parametro. Para esto tienes 2 parametros por valor:
*'''ratio:''' Va desde 0 hasta 8, donde <code>#$00</code> significa que utilizara 100% el canal que venia en el color en la paleta y  <code>#$08</code> significa que utilizara 100% el canal que entregaste como parametro.
*'''value:''' Va desde 0 hasta 31 (<code>#$1F</code>), indica el valor que le estarias entregando como parametro a ese canal especifico.
Las macros son las siguientes:
*<code>%MixHSL(ratio1,ratio2,ratio3,value1,value2,value3,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixRGB(ratio1,ratio2,ratio3,value1,value2,value3,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixHS(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixHL(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixSL(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixRB(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixRG(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixGB(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixH(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixS(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixL(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixR(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixG(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixB(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)</code>
*<code>%MixHSLDefault(ratio1,ratio2,ratio3,value1,value2,value3,offset,length)</code>
*<code>%MixRGBDefault(ratio1,ratio2,ratio3,value1,value2,value3,offset,length)</code>
*<code>%MixHSDefault(ratio1,ratio2,value1,value2,offset,length)</code>
*<code>%MixHLDefault(ratio1,ratio2,value1,value2,offset,length)</code>
*<code>%MixSLDefault(ratio1,ratio2,value1,value2,offset,length)</code>
*<code>%MixRBDefault(ratio1,ratio2,value1,value2,offset,length)</code>
*<code>%MixRGDefault(ratio1,ratio2,value1,value2,offset,length)</code>
*<code>%MixGBDefault(ratio1,ratio2,value1,value2,offset,length)</code>
*<code>%MixHDefault(ratio1,value1,offset,length)</code>
*<code>%MixSDefault(ratio1,value1,offset,length)</code>
*<code>%MixLDefault(ratio1,value1,offset,length)</code>
*<code>%MixRDefault(ratio1,value1,offset,length)</code>
*<code>%MixGDefault(ratio1,value1,offset,length)</code>
*<code>%MixBDefault(ratio1,value1,offset,length)</code>
Podemos notar 2 tipos de macros, las que tienen la palabra '''Default''' y las que no. Las que no tienen la palabra default, debes especificar la dirección desde donde vienen los datos ('''sourceAddr''' y '''sourceBNK''') y la dirección a la que llegan esos datos ('''destinationAddr''') y ('''destinationBNK'''), '''addr''' se refiere a la dirección y '''BNK''' se refiere al banco de memoria. Por otra parte en los que dicen '''Default''', use utiliza como '''source''' el buffer <code>DZ_PPUMirrors_CGRAM_BasePalette</code> y como destination el buffer <code>DZ_PPUMirrors_CGRAM_PaletteWriteMirror</code> y se utiliza el parámetro '''offset''' para indicar desde que color del buffer empezar a leer (considerar que los buffers son de 256 colores). Por ultimo, el parametro '''length''' indica la cantidad de colores que se procesaran.
Ejemplo de uso:
Reemplazar el canal H de todos los colores en la paleta 0, por el valor <code>#$1F</code>.
<pre>
    %MixHDefault(#$08,#$1F,#$0000,#$0010)</code>
</pre>
'''Nota: Estas macros dependiendo de cuantos colores se procesen, pueden ser algo lentas y causar slowdown, usar con discreción.'''
=== Enviar Paleta Procesada ===
Esto ya deberiamos saber como realizarlo si leiste el capitulo de '''Cargar paleta de colores dinámicamente'''. Solo debes tener cuidado de como usar eso de forma correcta.
=== Ejemplo ===
Aqui un ejemplo del sistema, en este caso tenemos un sprite que lo unico que hara es ir alternando el hue de toda la paleta 2.
<pre>
!Started = !SpriteMiscTable1    ;Lo utilizaremos para evitar llamar la rutina de cargar paleta y de SetBase más de una vez
!H = !SpriteMiscTable2          ;Sera el valor de Hue que usaremos
print "INIT ",pc
    LDA #$00        ;Carga 0 en el Canal Hue y en la variable !Started,x
    STA !Started,x
    STA !H,x
RTL
print "MAIN ",pc
    PHB
    PHK
    PLB
    JSR SpriteCode
    PLB
RTL
!Offset = $0020        ;Color desde donde inicia el procesamiento
SpriteCode:
    LDA !Started,x
    BNE +              ;Si Started es 0, entonces cargamos la paleta desde la
                      ;paleta que esta cargada en el nivel. Y ponemos Started en 1
    INC !Started,x
    %TransferToCGRAMBuffer($01, #$01FE)
    LDX !SpriteIndex
RTS
+
    CMP #$01
    BNE +              ;Si Started es 1, entonces hacemos SetBase para cargar una base HSL.
                      ;Además ponemos Started en 2 para evitar hacer SetBase 2 veces.
    INC !Started,x
    %SetHSLBase("#DZ_PPUMirrors_CGRAM_PaletteCopy+(!Offset*2)","#DZ_PPUMirrors_CGRAM_PaletteCopy>>16","#DZ_PPUMirrors_CGRAM_BasePalette+(!Offset*3)","#DZ_PPUMirrors_CGRAM_BasePalette>>16",#$0010)
    LDX !SpriteIndex
+
    ;Cambiamos el canal H de los colores en la paleta 2
    %MixH("#$08","!H,x","#DZ_PPUMirrors_CGRAM_BasePalette+(!Offset*3)","#DZ_PPUMirrors_CGRAM_BasePalette>>16","#DZ_PPUMirrors_CGRAM_PaletteWriteMirror+(!Offset*2)","#DZ_PPUMirrors_CGRAM_PaletteWriteMirror>>16",#$0010)
    ;Enviamos la paleta para que sea usada en el nivel.
    %TransferToCGRAM(#!Offset, "#DZ_PPUMirrors_CGRAM_PaletteWriteMirror+(!Offset*2)","#DZ_PPUMirrors_CGRAM_PaletteWriteMirror>>16", #$0020)
    LDX !SpriteIndex
    ;Actualizamos el valor del Hue para que vaya alternando.
    LDA !H,x
    INC A
    STA !H,x
    CMP #$20
    BCS +
RTS
+
    STZ !H,x
RTS
</pre>
Este ejemplo se ve asi:
[[File:Hue Change.gif|center|Hue Change]]
'''Nota: La razón por la que no se llama la macro <code>TransferToCGRAMBuffer</code> en el Sprite Init, es porque si se pone ahí, no funciona correctamente si el sprite es situado al inicio del nivel, ya que, la paleta aun no estaría correctamente cargada en el nivel.'''
= Cargar Gráficos Manualmente =
= Cargar Gráficos Manualmente =
TransferToCGRAM(CGRAMOffset, TableAddr, TableBNK, Lenght)
LDA DZ_Timer
AND #$01
BNE +
    LDA DZ_Timer
CMP !SafeFrame
BNE +
RTS
= Librería de Dynamic Z =
= Librería de Dynamic Z =
= Librería de Dyzen =
= Librería de Dyzen =

Latest revision as of 14:40, 30 July 2021

English Português Español 日本語

En esta guia se explicara paso a paso como crear un Sprite Dinámico desde 0 utilizando Dynamic Z (V3.75 o superior) y Dyzen (Versión para Sprites Dinámicos).

Instalación de Dynamic Z

Pre-requisitos

Para que la aplicación "Dynamic Z Installer.exe" o "Dynamic Resource adder.exe" funcionen, debes tener instalado:

  • dotNet Core 3.1[1]

Lorom

  1. Iniciando desde un ROM limpio, expandirlo a al menos 2MB en lunar magic y hacer cualquier modificación en el lunar magic.
  2. Abrir el programa "Dynamic Z Installer.exe".
  3. Seleccionar las funcionalidades que deseas que se añadan a tu ROM.
    Dynamic Z Installer 1
    • Graphics and Tilemap Change: Es utilizado para cualquier recurso que requiera cambiar gráficos o tilemaps de manera dinámica.
    • Dynamic Sprite Support: Es necesario para utilizar Sprites Dinámicos convencionales. Requiere Graphics and Tilemap Change.
    • Shared Dynamic Sprite Support: Es necesario para utilizar Sprites Dinámicos con memoria compartida. Requiere Dynamic Sprite Support.
    • Semi Dynamic Sprite Support: Es necesario para utilizar Sprites Semi-Dinámicos. Requiere Graphics and Tilemap Change.
    • Palette Change System: Es necesario para realizar cambios de paletas dinámicos.
    • HSL Support: Es necesario para realizar efectos de paletas de colores tales como cambiar el tinte, Saturación o brillo (Hue-Saturation or Lightness), tambien para mezclar un color base con los colores de la paleta. Requiere Palette Change System.
    • Player Graphic Change System: Permite cambiar los gráficos del player tanto siguiendo el mismo esquema de graficos que el player original, como hacer tu propia rutina grafica para players de cualquier tamaño. Requiere Graphics and Tilemap Change.
    • Player Palette Change System: Permite cambiar la paleta de color del player (Paleta 8) dinámicamente. Requiere Palette Change System.

    Nota: Tanto el Player Graphic Change System como Player Palette Change System (Player Features), tienen severos problemas de compatibilidad con recursos que hacen cambios en el player tales como LX5's custom power ups, 32x32 Player Tilemap o 8x8 Tile DMAer. Si usas recursos similares a estos, se recomienda no seleccionar estas funcionalidades.

    Luego de seleccionar las funcionalidades deseadas, apretar el boton "Next". Los features que estan seleccionados por default, no tienen problemas de compatibilidad.

  4. Selecciona tu ROM, Si usas Pixi tambien selecciona donde esta instalado pixi y si usas uberasm tool tambien selecciona donde esta instalado uberasm tool.
    Dynamic Z Installer 2

    Luego presiona el boton "Install".

    Si estas utilizando una Beta de Dynamic Z V3.75, luego de que termine la instalación, vuelve a apretar el boton "Install" e instálalo denuevo por segunda vez. Esto es un bug de la Beta de Dynamic Z V3.75, en la versión finalizada o versiones más nuevas esto no es necesario.

Nota: Luego de instalar Dynamic Z. Por favor, dejar una copia de Asar en la misma carpeta donde se encuentra el ROM.

SA-1

  1. Iniciando desde un ROM limpio, expandirlo a al menos 2MB en Lunar Magic. No hacer ninguna otra modificación en LM.
  2. Abrir el archivo "sa1.asm" y en la linea:
    !DSX		= 1
    

    Cambiarlo por:

    !DSX		= 0
    
  3. Instalar SA-1 utilizando Asar.
  4. Abrir Lunar Magic y hacer cualquier cambio en el ROM.
  5. Abrir el programa "Dynamic Z Installer.exe".
  6. Seleccionar las funcionalidades que deseas que se añadan a tu ROM.
    Dynamic Z Installer 1
    • Graphics and Tilemap Change: Es utilizado para cualquier recurso que requiera cambiar gráficos o tilemaps de manera dinámica.
    • Dynamic Sprite Support: Es necesario para utilizar Sprites Dinámicos convencionales. Requiere Graphics and Tilemap Change.
    • Shared Dynamic Sprite Support: Es necesario para utilizar Sprites Dinámicos con memoria compartida. Requiere Dynamic Sprite Support.
    • Semi Dynamic Sprite Support: Es necesario para utilizar Sprites Semi-Dinámicos. Requiere Graphics and Tilemap Change.
    • Palette Change System: Es necesario para realizar cambios de paletas dinámicos.
    • HSL Support: Es necesario para realizar efectos de paletas de colores tales como cambiar el tinte, Saturación o brillo (Hue-Saturation or Lightness), tambien para mezclar un color base con los colores de la paleta. Requiere Palette Change System.
    • Player Graphic Change System: Permite cambiar los gráficos del player tanto siguiendo el mismo esquema de graficos que el player original, como hacer tu propia rutina grafica para players de cualquier tamaño. Requiere Graphics and Tilemap Change.
    • Player Palette Change System: Permite cambiar la paleta de color del player (Paleta 8) dinámicamente. Requiere Palette Change System.

    Nota: Tanto el Player Graphic Change System como Player Palette Change System (Player Features), tienen severos problemas de compatibilidad con recursos que hacen cambios en el player tales como LX5's custom power ups, 32x32 Player Tilemap o 8x8 Tile DMAer. Si usas recursos similares a estos, se recomienda no seleccionar estas funcionalidades.

    Luego de seleccionar las funcionalidades deseadas, apretar el boton "Next". Los features que estan seleccionados por default, no tienen problemas de compatibilidad.

  7. Selecciona tu ROM, Si usas Pixi tambien selecciona donde esta instalado pixi y si usas uberasm tool tambien selecciona donde esta instalado uberasm tool.
    Dynamic Z Installer 2

    Luego presiona el boton "Install".

    Si estas utilizando una Beta de Dynamic Z V3.75, luego de que termine la instalación, vuelve a apretar el boton "Install" e instálalo denuevo por segunda vez. Esto es un bug de la Beta de Dynamic Z V3.75, en la versión finalizada o versiones más nuevas esto no es necesario.

Nota: Luego de instalar Dynamic Z. Por favor, dejar una copia de Asar en la misma carpeta donde se encuentra el ROM.

Insertar Gráficos y Recursos Dinámicos

Luego de haber instalado Dynamic Z, encontraras en la carpeta del ROM, un tool llamado "Dynamic Resource Adder.exe" (DRAdder), este tool es utilizado para insertar recursos dinámicos en el ROM. Si bien su uso no es obligatorio (ya que, un sprite dinámico podria decidir utilizar otra manera de insertar estos recursos dentro del ROM), si es recomendable, ya que, de esta manera se lleva un mejor control sobre estos recursos y además se evita el uso de parches extras, además que los recursos se intentan insertar en los banks mayores a $40 de ser posible y que es bastante sencillo importar estos recursos.

  1. Agrega los recursos dinámicos en la carpeta "Dynamic Resources", estos recursos pueden ser tanto graficos, paletas de colores (en formato ".bin") o tilemaps.
  2. Abre el archivo "ResourceList.txt", veras algo como lo siguiente:
    PIXI:
    .Normal
    .Cluster
    .Extended
    OTHER:
    
  3. Para el caso de los sprites, agrega en los labels correspondientes el nombre del archivo ".asm" del Sprite. Por ejemplo:
    PIXI:
    .Normal
    Klaptrap
    .Cluster
    DKCContactEffect
    .Extended
    KlumpGrenade
    OTHER:
    
  4. Para los recursos que no son sprites, como por ejemplo un codigo uberasm, un parche o un bloque, puedes utilizar el label "OTHER:" añadiendo el path del archivo. Por ejemplo:
    PIXI:
    .Normal
    Klaptrap
    .Cluster
    DKCContactEffect
    .Extended
    KlumpGrenade
    OTHER:
    .\Patches\Player Revolution\Players\NSMBPlayer.asm
    
  5. Abre el programa "Dynamic Resource Adder.exe" (DRAdder), escribe el path de lo que pida la aplicación, si pide la carpeta de pixi arrastras la carpeta de pixi, si pide el ROM arrastras el ROM, etc. Alternativamente puede utilizarse en consola tambien con el comando:
    DynamicResourceAdder <nombre del rom.smc> <lista de recursos.txt> <carpeta de pixi>
    
  6. Luego de insertar los recursos dinámicos (como GFXs, paletas de colores o tilemaps), inserta los archivos que utilizan esos recursos (como los sprites, bloques, códigos uberasm, parches, o cualquier archivo que utilice los recursos dinámicos insertados con el tool).

Nota: cada vez que insertes recursos dinámicos debes insertar todos los archivos que utilicen esos recursos dinámicos, si no se realiza esto, los gráficos, paletas o tilemaps utilizados por esos archivos se podrían ver glitcheados.

Para utilizar este tool, el archivo que importa los recursos dinámicos, en la primera linea de código debe incluir todos los recursos que utiliza de la siguiente manera:

;@<Recurso 1>,<Recurso 2>,<Recurso 3>, ...

Por ejemplo, Klaptrap utiliza 5 paletas distintas y los graficos, si ves la primera linea de este sprite, es:

;@Klaptrap.bin,KlaptrapPal1.bin,KlaptrapPal2.bin,KlaptrapPal3.bin,KlaptrapPal4.bin,KlaptrapPal5.bin

Luego cada uno de estos recursos puede ser referenciado utilizando los defines "!GFXYY" donde YY es el numero del recurso, por ejemplo:

  • !GFX00: Referenciaría al primer recurso. En Klaptrap seria la dirección de memoria de el archivo Klaptrap.bin.
  • !GFX01: Referenciaría al segundo recurso. En Klaptrap seria la dirección de memoria de el archivo KlaptrapPal1.bin.
  • !GFX02: Referenciaría al tercer recurso. En Klaptrap seria la dirección de memoria de el archivo KlaptrapPal2.bin.
  • !GFX03: Referenciaría al cuarto recurso. En Klaptrap seria la dirección de memoria de el archivo KlaptrapPal3.bin.
  • !GFX04: Referenciaría al quinto recurso. En Klaptrap seria la dirección de memoria de el archivo KlaptrapPal4.bin.
  • !GFX05: Referenciaría al sexto recurso. En Klaptrap seria la dirección de memoria de el archivo KlaptrapPal5.bin.

y asi sucesivamente.

Si quisiera por ejemplo obtener el BNK del primer recurso utilizaria:

#!GFX00>>16

Para el High Byte seria:

#!GFX00>>8

Y para el Low Byte seria:

#!GFX00

¿Cómo hacer los gráficos para un Sprite dinámico?

Puedes utilizar cualquier programa de edición de imágenes para hacer los gráficos. Los gráficos deben cumplir las siguientes condiciones:

  • La cantidad total de colores utilizados por los gráficos es máximo 15 colores. Entre estos 15 colores no se considera el color del Background.
  • El fondo debe ser transparente (color #00000000).
  • Considera que los colores en SNES utilizan formato de color BGR555, por lo tanto, se recomienda que los canales RGB de los colores terminen en 0 o en 8, además que van desde 0 hasta 248.
  • No hagas gráficos demasiado grandes, considera que tras la optimización de los gráficos, los tiles deben caber en un espacio de máximo 128x32 (128x48 si usas 50% more mode).
  • Los gráficos deben seguir un formato de Sprite Sheet donde cada frame debe utilizar la misma cantidad de espacio y no puede haber frames vacíos.

Ejemplo:

DKC Contact Effect

Programas como aseprites te permiten trabajar cada frame de manera independiente y luego exportarlo como un sprite sheet con la función "File->Export Sprite Sheet".

¿Cómo centrar el sprite para que calce con el object clipping deseado?

En varios editores de imágenes, puedes crear grillas para guiarte, en aseprites por ejemplo, las grillas son de 16x16 por default. El primer truco es hacer que el canvas del sprite sea de tamaño 16*nx16*n con n = un número impar, por ejemplo, 16x16, 48x48, 80x80, 112x112, etc.

Luego el cuadrado Central del canvas, sera equivalente al cuadrado de color más oscuro mostrado en el CFG Editor.

Central Square

Por lo tanto, debes ajustar la posición de los gráficos de manera que se coordine con el cuadrado mostrado en el CFG Editor. Ten en consideración que en el CFG Editor, los puntos rojos muestran el object clipping y el cuadrado verde oscuro muestra la posición del sprite.

Dyzen para Sprites Dinámicos

Pre-Requisitos

Para que Dyzen funcione, requiere:

  • dotNet Framework 4.8 [2]

Funcionamiento

Al abrir Dyzen te aparecerá el siguiente mensaje:

Dyzen New Project

Ya que, haremos un sprite dinámico, presionaremos el botón "Dynamic Sprite".

Luego de esto podremos ver 3 secciones principales:

  • Frames: En esta sección podrás hacer cada cuadro de animación del sprite.
  • Animations: En esta sección podrás hacer cada una de las animaciones del sprite.
  • Interaction: En esta sección podrás hacer las cajas de colisión del sprite.

Partiendo desde la sección de Frames, encontraras un botón que dice "Load Sprite Sheet". Al presionarlo te saldrá esta ventana:

Dyzen Sprite Sheet Ripper 1

En esta pantalla primero pondremos el tamaño del canvas que fue utilizado al crear el frame. Luego apretaremos el botón "Load Sprite Sheet", seleccionaremos los gráficos del sprite y luego seleccionaremos una paleta de colores y luego apretaremos el botón "Load From Sprite" y presionaremos el botón "OK" y saldrá esta ventana:

Dyzen Sprite Sheet Ripper 2

Aquí saldrán las siguientes opciones:

  • Max # of 8x8 tiles: La cantidad máxima de tiles de 8x8 que puede tener un frame. Mientras mayor sea el número, más lento es el algoritmo, mientras menor sea el número es más rápido.
  • Max # of Extra Childrens: Esta variable mientras mayor sea, más lento es el algoritmo y mientras menor sea, más rapido es.
  • Heuristic Weight: Esta variable mientras menor sea, más lento sera el algoritmo y mientras mayor sea, más rapido sera.

Mientras más lenta sea la configuración del algoritmo es más preciso y da una respuesta más optima, por el otro lado mientras más rapida es la configuración entonces da una solución menos optima. Configuración recomendada para Sprites muy pequeños:

  • Max # of 8x8 tiles: No Limit.
  • Max # of Extra Childrens: 16.
  • Heuristic Weight: 1

Configuración recomendada para Sprites medianos o normales:

  • Max # of 8x8 tiles: 4.
  • Max # of Extra Childrens: 2.
  • Heuristic Weight: 1

Configuración recomendada para Sprites medianos-grandes:

  • Max # of 8x8 tiles: 4.
  • Max # of Extra Childrens: 0~1.
  • Heuristic Weight: 1

Configuración recomendada para Sprites grandes:

  • Max # of 8x8 tiles: 0.
  • Max # of Extra Childrens: 0.
  • Heuristic Weight: 1

El resto de opciones se explican solas.

Luego de rippear los gráficos, podrás visualizar cada uno seleccionando en el apartado "Frames" en el menu de la derecha.

Luego en el apartado "Animations" puedes crear animaciones, para esto apretamos el boton "New". Para añadir frames a la animación los seleccionamos del menu de arriba a la derecha, si quieres seleccionar varios frames al mismo tiempo manten presionado la tecla "ctrl" y ve seleccionando los frames que deseas. Luego de seleccionar los frames, aprieta el boton "+" para añadirlos a la animación.

Para las animaciones hay 2 opciones:

  • Only Once: La animación se reproduce 1 sola vez.
  • Continuous: La animación se repite continuamente.

Luego podras ir al apartado de Interaction para crear las cajas de colisión. Si quieres que todos los frames usen las mismas cajas de colisión, presionas la opción "Share with all Frames", si deseas que cada frame tenga sus propias cajas de colisión presionas la opción "Don't Share Interaction".

Un Truco para hacer el trabajo más rápido en la parte de interacción es apretar la opción "Share with all Frames", crear las cajas de colisión para un frame y luego cambiar a la opción "Don't Share Interaction", luego de eso todos los frames tendran las cajas creadas mientras se utilizo la opción "Share with all Frames" pero podrás modificar las cajas para que se adapten a cada frame.

Una vez terminado el sprite con sus frames, animaciones y cajas de colisión presiona la opción "File->Extract To", selecciona una carpeta y aparecera esta ventana.

Dyzen Extract To
  • Code: Es para que exporte el código del sprite.
    • Flip X: Es para que el sprite pueda voltear horizontalmente.
    • Flip Y: Es para que el sprite pueda voltear Verticalmente.
  • SP1: Es para que exporte los gráficos en SP1.
  • SP2: Es para que exporte los gráficos en SP2.
  • SP3: Es para que exporte los gráficos en SP3.
  • SP4: Es para que exporte los gráficos en SP4.
  • Palette: Es para que exporte la paleta de colores.

Se recomienda seleccionar todas las opciones a excepción de Flip Y, en caso que tu sprite no necesite ser flippeado en X, tambien quitar la opción Flip X.

Creando un CFG para el sprite

Abrimos el CFG Editor de pixi:

CFG Editor 1

Las relevante es:

  • Cambiar la opción de Normal a Custom.
  • En la opción "Extra Byte Count", pon al menos 1 (puede ser hasta 12, quizás a futuro si el LM lo admite puedes poner más) en Extra Bit Clear y Extra Bit Set.
  • Si utilizas el sistema de interacción de Dyzen debes seleccionar las opciones:
    • Process interaction with Mario Every Frame
    • Don't use default interaction with Mario
  • El Act Like suele ser 36, en muy pocos casos tiene otro valor, se recomienda en el 90% de los casos utilizar el valor 36.

Las demás opciones son a gusto de cada uno, solo ten en consideración poner el Object Clipping que mejor se ajuste a tu sprite y la opción "Don't disable clipping when starkilled" puede ser útil cuando quieres hacer una animación de muerte para tu sprite. Aqui por ejemplo el CFG de Klaptrap.

Klaptrap CFG

Explicando el Sistema de animación

El sistema de animación tiene las siguientes variables:

!FrameIndex = !SpriteMiscTable1
!AnimationTimer = !SpriteMiscTable7
!AnimationIndex = !SpriteMiscTable2
!AnimationFrameIndex = !SpriteMiscTable3
!LocalFlip = !SpriteMiscTable4
!GlobalFlip = !SpriteMiscTable5
!LastFrameIndex = !SpriteMiscTable6
  • !FrameIndex: Esta variable tiene el identificador del Frame que es mostrado en pantalla por el sprite por la rutina gráfica.
  • !AnimationTimer: Esta variable almacena la cantidad de Frames (entendidos como Game Loops, cuadros de animación), en los que el cuadro de animación actual se mostrara en pantalla.
  • !AnimationIndex: Esta variable almacena el identificador de la animación que es reproducida actualmente por el sprite.
  • !AnimationFrameIndex: Indica que frame dentro de la animación actual esta siendo mostrado por el sprite, por ejemplo si el sprite tuviera una animación con los frames 1, 5 y 3 (en ese orden), !AnimationFrameIndex tendrá el valor 0 si esta en el valor 1 (primer valor), 1 si esta en el frame 5 (segundo valor) y 2 si esta en el frame 3 (tercer valor). De esta manera una animación puede tener repetido el mismo frame varias veces.
  • !LocalFlip: Esta variable indica si la animación actual esta volteando el frame que esta reproducido actualmente, este valor no debe ser modificado fuera de la rutina de animación por el sistema de Dyzen.
  • !GlobalFlip: Esta variable se utiliza para voltear al sprite tanto horizontal como verticalmente.
  • !LastFrameIndex: Esta variable indica el valor que tuvo !FrameIndex al momento de enviar sus gráficos a la VRAM. Esta variable es modificada por la rutina dinámica del Dynamic Z.

Para cambiar el frame o la animación del sprite manualmente debes llamar la siguiente macro:

%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")

Devuelve el flag Z activado si en el game loop actual se puede cambiar de frame y 0 sino. Ejemplo de uso:

%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ .DontChangeFrame
LDA #$01
STA !FrameIndex,x
.DontChangeFrame
RTS

Cambiaria el frame al con ID #$01.

Esta macro puede ser utilizada para sprites normales, clusters, extended y overworld de la siguiente manera:

  • %CheckEvenOrOdd("DZ_DS_Loc_US_Normal"): Para Normal Sprites.
  • %CheckEvenOrOdd("DZ_DS_Loc_US_Cluster"): Para Cluster Sprites.
  • %CheckEvenOrOdd("DZ_DS_Loc_US_Extended"): Para Extended Sprites.
  • %CheckEvenOrOdd("DZ_DS_Loc_US_OW"): Para Overworld Sprites.

Esto se realiza debido a que por default los sprites del Dynamic Z son sprites 30FPS, esta macro se utiliza para sincronizar las rutinas graficas de los sprites dinámicos y que no se muestren gráficos basura. En el caso de los sprites 60FPS, no es necesario utilizarla.

Dyzen además crea rutinas para cambiar entre las distintas animaciones. Estas rutinas se generara 1 por cada animación creada el dyzen y tienen como nombre ChangeAnimationFromStart_<nombre de animación>. pueden ser llamadas de la siguiente manera (suponiendo que tengo una animación para caminar):

JSR ChangeAnimationFromStart_Walk

Esto cambiaria la animación del sprite a la animación de caminar.

Debes mezclarlo con la macro que se comentaba antes, por lo tanto quedaria asi:

%CheckEvenOrOdd("DZ_DS_Loc_US_Normal")
BEQ +
JSR ChangeAnimationFromStart_Walk
+
RTS

Tablas de la rutina de animación

Hay tablas que manejan el funcionamiento de cada una de las animaciones, estas animaciones son creadas por Dyzen sin embargo el usuario podría querer editarlas manualmente para añadir o modificar animaciones preexistentes, estas son:

  • AnimationLenght: Almacena la cantidad de frames que posee cada animación. Cada valor es de 16 bits.
  • AnimationLastTransition: Almacena el valor que debe tomar la variable "!AnimationFrameIndex", luego de que la animación llegue a su fin. De momento desde Dyzen permite 2 valores posibles, la cantidad de frames de la animación - 1 (en este caso seria una animación "Only Once") o 0 (en este caso seria una animación continua). Puedes modificar este valor para que la animación luego se repita desde cierto frame en adelante, de esa manera podrias hacer una animación que tiene una introducción y luego se repite la sección final. Cada valor es de 16 bits.
  • AnimationIndexer: Almacena el índice desde donde inician los datos de cada animación en las distintas tablas. En caso que quieras añadir animaciones de forma manual, tendrías que considerar el índice de la ultima animación y sumarle la cantidad de frames que trae la ultima animación y debería darte el valor para tu nueva animación. Cada valor es de 16 bits.
  • Frames: Almacena los indices de cada frame que contiene cada animación. Cada valor es de 8 bits. Para el 99% de los sprites un indice de 8 bits es más que suficiente, permitiendo hasta 255 frames, en caso que tu sprite utilice más de 255 frames, tendrias que modificar las rutinas para que funcionen con indices de 16 bits, en ese caso, esta tabla seria de 16 bits. La rutina de animación de player por default es de 16 bits, ya que, es más común que los players superen los 255 frames.
  • Times: Almacena cuantos game loops (SNES Frame), deben transcurrir para que transicionar al siguiente frame. El valor minimo es 0 y el valor máximo es 255. En el caso de los sprites dinámicos de 30FPS (que son los que por default crea Dyzen), los valores de esta tabla deben ser pares, (0, 2, 4, etc.). Cada valor es de 8 bits.

Valores de referencia para un sprite normal o un sprite dinámico 60FPS:

Valor Framerate NTSC Framerate PAL
0 60 FPS 50 FPS
1 30 FPS 25 FPS
2 20 FPS 16.7 FPS
3 15 FPS 12.5 FPS

Valores de referencia para un sprite dinámico 30FPS:

Valor Framerate NTSC Framerate PAL
0 30 FPS 25 FPS
2 15 FPS 12.5 FPS
4 10 FPS 8.3 FPS
6 7.5 FPS 6.25 FPS
  • Flips: Indica como debe voltearse un frame al ser reproducido por la animación, esta tabla puede no aparecer siempre, depende de si al momento de crear un sprite en Dyzen, alguna de las animaciones tuvo al menos 1 frame que haya sido flipeado, además los valores de la tabla se pueden utilizar solamente si se permitió en Dyzen el Flip Horizontal o Vertical. Cada valor puede tomar solo los valores 0, 1, 2 y 3. Cada valor es de 8 bits.
    • $00: No Flipea.
    • $01: Flipea Horizontalmente. Requiere haber puesto en Dyzen al momento de exportar la opción "Flip X".
    • $02: Flipea Verticalmente. Requiere haber puesto en Dyzen al momento de exportar la opción "Flip Y".
    • $03: Flipea Horizontal y Verticalmente. Requiere haber puesto en Dyzen al momento de exportar las opciones "Flip X" y "Flip Y".

Sistema de interacción y cajas de colisión

El sistema de interacción funciona asignándole cajas de colisión a cada frame, cada caja de colisión tiene 2 acciones que se pueden ejecutar al momento de entrar en contacto, una acción ocurre cuando la caja entra en contacto y otra función ocurre cuando la caja no entra en contacto. La información de la interacción queda almacenada de la siguiente manera:

  • $00: Posición X del limite izquierdo de la caja de colisión del objetivo. 16 bits.
  • $08: Posición Y del limite superior la caja de colisión del objetivo. 16 bits.
  • $02: Posición X de el limite derecho de la caja de colisión del objetivo. 16 bits.
  • $0C: Posición Y del limite inferior de la caja de colisión del objetivo. 16 bits.
  • $4D: Información de la colisión. En la acción que es ejecutada cuando la caja de colisión no hace contacto pueden ser utilizada para saber datos sobre la posición relativa entre la caja de colisión y el objetivo. Si hubo contacto entre la caja de colisión revisada y el objetivo tiene el valor #$0F, si no hubo colisión tiene los siguientes valores.
    • #$01: No hubo contacto en ningún eje. La caja de colisión actual esta abajo del objetivo.
    • #$02: No hubo contacto en ningún eje. La caja de colisión actual esta arriba del objetivo.
    • #$03: Hay contacto pero solo en el eje vertical.
    • #$04: No hubo contacto en ningún eje. La caja de colisión actual esta a la derecha del objetivo.
    • #$08: No hubo contacto en ningún eje. La caja de colisión actual esta a la izquierda del objetivo.
    • #$0C: Hay contacto pero solo en el eje horizontal.
    • #$0F: Hay contacto.
  • $04: Posición X del sprite. 16 bits.
  • $0A: Posición Y del sprite. 16 bits.
  • $45: Posición X del limite izquierdo de la caja de colisión que esta siendo revisada actualmente. 16 bits.
  • $47: Posición Y del limite superior la caja de colisión que esta siendo revisada actualmente. 16 bits.
  • $49: Posición X de el limite derecho de la caja de colisión que esta siendo revisada actualmente. 16 bits.
  • $4B: Posición Y del limite inferior de la caja de colisión que esta siendo revisada actualmente. 16 bits.

Luego que se revisa la interacción de todas las cajas, se tiene reservada la dirección de RAM $4F que puede utilizarse para llamar una rutina basandose en que valor le pusieron a esa dirección, esto es optativo, pero en la practica se suele usar en muchas interacciones distintas.

Tablas de la Rutina de Interacción

El sistema de interacción utiliza (por cada tipo de colisión), una tabla llamada HitboxTables, esta es una tabla que almacena las referencias al resto de tablas del sistema. Por Ejemplo:

HitboxTables:
	dw HitboxAdder
	dw FrameHitboxesIndexer
	dw FrameHitBoxes
	dw HitboxType
	dw HitboxXOffset
	dw HitboxYOffset
	dw HitboxWidth
	dw HitboxHeight
	dw HitboxAction1
	dw HitboxAction2
	dw Actions

Podriamos crear diferentes versiones de esta tabla dependiendo contra que objetivo se desea colisionar. Por ejemplo, podriamos tener una version para colisión con el player, otra por colisión contra un sprite o contra bolas de fuego, etc. Al hacer esto, casi todas las referencias permaneceran igual, pero cambiaria la referencia a la tabla Actions, por ejemplo si usaramos el sistema para interacción con el player y para interacción con sprites, necesitariamos 2 tablas como estas:

HitboxTablesPlayer:
	dw HitboxAdder
	dw FrameHitboxesIndexer
	dw FrameHitBoxes
	dw HitboxType
	dw HitboxXOffset
	dw HitboxYOffset
	dw HitboxWidth
	dw HitboxHeight
	dw HitboxAction1
	dw HitboxAction2
	dw ActionsPlayer

HitboxTablesSprites:
	dw HitboxAdder
	dw FrameHitboxesIndexer
	dw FrameHitBoxes
	dw HitboxType
	dw HitboxXOffset
	dw HitboxYOffset
	dw HitboxWidth
	dw HitboxHeight
	dw HitboxAction1
	dw HitboxAction2
	dw ActionsSprites

Por otro lado, tendrías que crear esas tablas de Actions, se hablara más en detalle de la tabla Actions en la parte de Creando acciones para las cajas de colisión.

Las demás tablas son:

  • HitboxAdder: Almacena 4 valores, se utilizan estos valores para desplazar el indice en que se empieza a leer los datos de las demás tablas dependiendo de si el sprite esta volteado o no. De esta manera la caja de colisión se adapta a el volteo del sprite. Los valores son de 16 bits.
  • FrameHitboxesIndexer: Por cada Frame, se almacena un valor, este valor es utilizado como indice para buscar que cajas de colisión tiene cada frame, en la tabla FrameHitBoxes.
  • FrameHitBoxes: Cada linea de la tabla representa que cajas de colisión tiene cada frame, el valor $FF se utiliza para indicar el final de la linea. Por ejemplo, si un frame tuviera las cajas de colisión 2, 4 y 9, tendria la linea los valores $02,$04,$09,$FF.
  • HitboxType: Indica de que tipo es la caja de colisión, de momento no se utiliza, pero a futuro, se supone que podras utilizar cajas circulares o cajas en forma de rayo. Los valores son de 16 bits.
  • HitboxXOffset: Indica la posición relativa en X de la caja de colisión con el sprite. Los valores son de 16 bits.
  • HitboxYOffset: Indica la posición relativa en Y de la caja de colisión con el sprite. Los valores son de 16 bits.
  • HitboxWidth: Indica el ancho de la caja de colisión. Los valores son de 16 bits.
  • HitboxHeight: Indica el alto de la caja de colisión. Los valores son de 16 bits.
  • HitboxAction1: Indica que ContactActions usa cada caja de colisión. Los valores son de 16 bits.
  • HitboxAction2: Indica que NoContactActions usa cada caja de colisión. Los valores son de 16 bits.
  • Actions: Almacena referencias a las ContactActions y NoContactActions que pueden utilizar las cajas de colisión. Los valores son de 16 bits.

Todas estas tablas son creadas por Dyzen.

Creando acciones para las cajas de colisión

La rutina de colisión se basa en 3 tipos de acciones:

  • DefaultAction: Se ejecutara si al terminar la rutina de colisión al menos 1 de las cajas de colisión puso un valor distinto de 0 a la dirección de RAM $4F.
  • ContactAction: Se ejecutara cada vez que alguna de las cajas de colisión haga contacto con el objetivo. Puede ser 1 acción por caja de colisión o 1 acción por conjunto de cajas.
  • NoContactAction: Se ejecutara cada vez que alguna de las cajas de colisión NO haga contacto con el objetivo. Puede ser 1 acción por caja de colisión o 1 acción por conjunto de cajas.

Para las ContactAction y las NoContactAction, se deben poner en la tabla Actions, puedes poner todas las que quieras, ejemplo:


Actions:
dw ContactAction
dw NoContactAction

ContactAction:
RTL

NoContactAction:
RTL

Una vez puesta en la tabla "Action", cada caja de colisión la referencia en las tablas HitboxAction1 (para las ContactActions) y HitboxAction2 (para las NoContactAction), el valor que debe ir en estas tablas es el indice de la acción en la tabla Action multiplicado por 2 y en 16 bits, usando el ejemplo anterior, si quisiera que la caja usara la acción "ContactAction" en la tabla "HitboxAction1", entonces usaria el valor $0000, pero si quisiera usar la acción "NoContactAction", usaria el valor $0002. Todo esto es hecho por Dyzen, pero se explica básicamente por si alguien quisiera editarlo manualmente.

El siguiente ejemplo mostrara como hacer una interacción en la que el player puede saltar sobre el enemigo, pero es dañado si lo toca por la lateral o por abajo. Tambien el enemigo morira si el player usa la estrella. Primero haremos las ContactAction y NoContactAction.

Actions:
dw CheckBounce
dw CheckPlayerIsAbove

CheckBounce:
	%DyzenCheckBounce()
RTL

CheckPlayerIsAbove:
	%DyzenCheckPlayerIsAbove()
RTL

En este caso tendremos 2 funciones:

  • CheckBounce: Sera nuestra ContactAction, esta función en este caso llama a una rutina de la librería de Dyzen que lo que hace es, almacenar los datos de la caja de colisión que colisiono siempre y cuando no haya una colisión previa ($4F tiene el valor #$01) o que la caja de colisión este más arriba que la ultima que hizo contacto, además si identifica que la colisión ocurrió dentro de los 8 pixeles más arriba de la caja de colisión del objetivo, entonces pone la variable !SpritePlayerIsAbove,x con el valor #$01. Tambien pone la variable $4F con el valor #$01. Esta información se guarda de la siguiente manera:
    • $6C: Posición X del limite izquierdo de la caja de colisión. 16 bits.
    • $6A: Posición Y del limite superior la caja de colisión. 16 bits.
    • $6E: Posición X de el limite derecho de la caja de colisión. 16 bits.
    • $8D: Posición Y del limite inferior de la caja de colisión. 16 bits.
  • CheckPlayerIsAbove: Sera nuestra NoContactAction, esta función en este caso llama a una rutina de la libreria de Dyzen lo que hace es en caso que no haya colisión, guarda la información de la caja de colisión, siempre y cuando sea la más cercana al player, en caso que 2 cajas esten a la misma distancia, prioriza la que ese más arriba. La información es guardada del mismo modo que en "CheckBounce". Esta rutina no se ejecuta si alguna caja procesada previamente hizo contacto y puso la dirección de RAM $4F en un valor distinto a 0.

Ahora crearemos la DefaultAction que sera ejecutada si en algún momento se ejecuto "CheckBounce", para esto haremos una rutina de la siguiente manera:

DefaultAction:
	LDA $1490|!addr	;if player is using the star
	BEQ +			;kill the sprite
.PlayerHasStar
RTS
+

	LDA !SpritePlayerIsAbove,x
	BNE .PlayerIsAbove
.PlayerIsNotAbove
RTS
.PlayerIsAbove

RTS

Podremos identificar 3 secciones distintas dentro:

  • .PlayerHasStar: Se ejecuta cuando el player esta usando la estrella. Lo más común seria utilizar la rutina de pixi "%Star()" en esta sección para matar al sprite.
  • .PlayerIsNotAbove: Se ejecuta cuando el player no esta sobre el sprite. Lo más común seria utilizar la rutina de dyzen "%DyzenDamagePlayer()" para dañar al player cuando ocurra esto, pero tambien podria dejarse en blanco en caso que quieras hacer una plataforma o tambien podrias llamar a la rutina de muerte del player (JSL $00F606|!rom).
  • .PlayerIsAbove: Se ejecuta cuando el player esta sobre el sprite. Esta es la sección más flexible, normalmente podrías utilizarla para que el player rebote sobre el sprite y dañe al enemigo, también podrias revisar si el player esta haciendo spinjump y hacer una u otra cosa dependiendo de eso, también podrías hacer una plataforma solida. Hay múltiples opciones en esta sección de la rutina.

Por ejemplo, para un sprite que puedes rebotarle encima, que lo matas con la estrella y que te daña en otro caso:

DefaultAction:
	LDA $1490|!addr	;if player is using the star
	BEQ +			;kill the sprite
.PlayerHasStar
	%Star()
RTS
+

	LDA !SpritePlayerIsAbove,x
	BNE .PlayerIsAbove
.PlayerIsNotAbove
	%DyzenDamagePlayer()
RTS
.PlayerIsAbove
	
	REP #$20
	LDA $6A		;Obtiene la posición de el limite superior de la caja de colisión
	SEC		;
	SBC $0C		;Le resta la posición de el limite inferior de la caja del player
	CLC		;para obtener la cantidad de pixeles que debe mover al player para que este sobre el sprite.
	ADC !PlayerY	;
	STA !PlayerY	;Mueve al player al tope del sprite
	SEP #$20

	JSL $01AA33|!rom    		;Hace que el Player rebote

	%DyzenPrepareContactEffect()	;Prepara las variables para poder mostrar el efecto de contacto
	JSL $01AB99|!rom    		;Crea el efecto de contacto  
RTS

Si quieres utilizar %DyzenCheckBounce() y %DyzenCheckPlayerIsAbove(), en el sprite init, debes incluir las siguientes 2 lineas:

	LDA #$00
	STA !SpritePlayerIsAbove,x

Además antes de la linea JSR InteractionMarioSprite debes incluir la rutina %DyzenPrepareBounce() y despues de JSR InteractionMarioSprite incluir la linea %DyzenDetectPlayerIsAbove(), de la siguiente manera:

	%DyzenPrepareBounce()

	JSR InteractMarioSprite

	%DyzenDetectPlayerIsAbove()

Cargar paleta de colores dinámicamente

Puedes cargar paletas dinamicamente, para esto, debes utilizar una macro del Dynamic Z llamada TransferToCGRAM, la cual de utiliza de la siguiente manera:

%TransferToCGRAM(CGRAMOffset, SourceAddr, SourceBNK, Length)

Parametros:

  • CGRAMOffset: Es la posición en la paleta de colores desde donde se escribiran los colores que enviaremos, considera que cada paleta es de 16 colores, por lo tanto si tuviera por ejemplo el valor #$1F, seria el color 15 en la paleta 1, por tanto esta tendria un formato de estilo:
    • PPPP CCCC:
      • PPPP: Paleta de color.
      • CCCC: Id del color dentro de la paleta.
  • SourceAddr: Dirección de memoria donde esta almacenada la paleta que queremos enviar. Debe ser de 16 bits.
  • SourceBNK: Banco de la dirección de memoria donde esta almacenada la paleta que queremos enviar. Debe ser de 16 bits, aunque el high byte debe ser 0.
  • Length: Cantidad de bytes que se envían, ten en consideración que cada color utiliza 2 bytes, por lo tanto, por cada color que quieres enviar, debes sumar 2 bytes, por ejemplo, si quisiera enviar 16 colores, tendria que enviar 32 bytes, por lo tanto el valor seria #$0020. Debe ser de 16 bits.

Supongamos que utilizando el DRAdder, añadimos una paleta de 32 colores usando el tercer recurso (!GFX02), si quisiera enviarla para que reemplace los colores de la paleta 4 y 5, podríamos hacerlo con la siguiente linea de código:

    %TransferToCGRAM(#$0040, #!GFX02, #!GFX02>>16, #$0040)

Por ejemplo si tuvieras la siguiente tabla que tuviera los siguientes colores:

ColorTable:
    dw $0001,$07FF,$041F

Y quisiera mover esos 3 colores partiendo desde el color 3 de la paleta 7, seria lo siguiente:

    %TransferToCGRAM(#$0073, #ColorTable, #ColorTable>>16, #$0006)

Recuerda, el formato de color del Super Nintendo es el siguiente:

  • 0BBBBB GGGGG RRRRR:
    • BBBBB: Canal azul.
    • GGGGG: Canal verde.
    • RRRRR: Canal Rojo.

Efectos con paletas de colores

Dynamic Z incluye un sistema para hacer efectos de paletas de colores, los efectos que puedes hacer son:

  • Cambiar el Tinte (H): El tinte de un color (Hue en ingles) es lo que hace que el color tenga cierta tonalidad, los valores de tinte en el sistema de Dynamic Z van desde 0 a 31 (#$1F), aca hay algunos valores de referencia:
    • #$00: Rojo.
    • #$05: Amarillo.
    • #$0B: Verde.
    • #$10: Cyan.
    • #$15: Azul.
    • #$1B: Morado.
  • Cambiar la Saturación (S): La saturación es lo que hace que el color se vea más vivo o se vea más gris, los valores de saturación van desde 0 a 31 (#$1F). Con saturación 0, la paleta de colores quedaria en tonalidades de gris, mientras que con saturación #$1F, la paleta de colores quedaria con el color en su versión más cargada.
  • Cambiar el Brillo (L): El brillo (Lightness en ingles) es lo que hace que el color sea más oscuro o más brillante, los valores de brillo van desde 0 a 31 (#$1F). Un brillo de 0, seria equivalente al color negro, mientras que un brillo de #$1F seria equivalente a la versión más brillante de ese color.
  • Cambiar Canales RGB: Puedes modificar los canales RGB de forma independiente usando el sistema de Dynamic Z, esto te permite por ejemplo mezclar un color base con los colores de la paleta

Este sistema consta de 4 partes, obtener la paleta de colores, transformar la paleta a una base procesable por el sistema, aplicar el efecto de color deseado y enviar la paleta modificada para que reemplace los colores del juego.

Obtener la paleta de colores

En este paso tenemos 2 opciones, la primera es usar una paleta de colores que hayamos creado nosotros mismos, en este caso podriamos por ejemplo usar el sistema del DRAdder para llamar un recurso usando !GFXYY, como tambien podriamos hacer una tabla con los colores que queremos y usar la tabla para obtener los colores. La segunda opción es obtener los colores que ya estan cargados en el mismo nivel, para esto utilizaremos la siguiente macro:

    %TransferToCGRAMBuffer(Offset, Length)

Esta macro lo que hace es enviar los colores que estan cargados al nivel, a un buffer. Por default, esta macro envia la información a un buffer llamado DZ_PPUMirrors_CGRAM_PaletteCopy, este buffer es de 512 bytes y cada 2 bytes seria un color de la paleta. Los parametros de esta macro son los siguientes:

  • Offset: Te indica desde que color empezar a copiar la paleta de colores. Por ejemplo, $73 seria el color 3 de la paleta 7.
  • Length: Te indica cuantos bytes se copiaran, cada color toma 2 bytes. Debe ser de 16 bits.

Ejemplo de uso:

    %TransferToCGRAMBuffer($01, #$01FE)

Esto copiaria todos los colores dese el color 1 de la paleta 0, hasta el color 15 de la paleta 15 en el buffer DZ_PPUMirrors_CGRAM_PaletteCopy.

Luego de llamar esta macro debes esperar 1 Game Loop para ver los resultados reflejados en el buffer, ya que, no se puede leer la CGRAM fuera del NMI handler.

Si utilizas la macro TransferToCGRAMBuffer, se recomienda usarla 1 frame despues del init y tener alguna manera de no volver a ejecutarla despues, solo necesitas llamarla 1 vez.

Procesar la base de colores

EL objetivo de este paso es separar los canales de los colores y transformarlos a otra base. Para esto hay 2 macros principalmente:

  • %SetHSLBase(sourceAddr,sourceBNK,destinationAddr,destinationBNK,length): Transforma colores BGR555 a una base de 3 canales independientes H, S y L de 5 bits cada uno. Donde el canal H es el Hue, el S es la saturación y el canal L es lightness.
  • %SetRGBBase(sourceAddr,sourceBNK,destinationAddr,destinationBNK,length): Transforma colores BGR555 a una base de 3 canales independientes R, G y B de 5 bits cada uno. Donde el canal R es el Rojo, el G es la verde y el canal B es azul.

Estas macros tienen los siguientes parametros:

  • sourceAddr: Indica la dirección de memoria donde se obtienen los colores. Es de 16 bits.
  • sourceBNK: Indica banco de la dirección de memoria donde se obtienen los colores. Es de 8 bits.
  • destinationAddr: Indica la dirección de memoria donde se guardara la base de colores. Es de 16 bits.
  • destinationBNK: Indica banco la dirección de memoria donde se guardara la base de colores. Es de 8 bits.
  • length: Indica la cantidad de colores que se transformaran. Es de 16 bits.

Además de estas versiones de las macro existe la versión resumida:

  • %SetHSLBaseDefault(offset,length)
  • %SetRGBBaseDefault(offset,length)

Esto usa como source a el buffer DZ_PPUMirrors_CGRAM_PaletteCopy y como destination el buffer DZ_PPUMirrors_CGRAM_BasePalette. En este caso el parametro offset, indicaria desde que color de la paleta de colores se empieza a hacer la transformación y debe ser de 16 bits.

Tambien existe una tercera version que es para usarla con el DRAdder o con tablas que tengan la paleta de colores almacenada:

  • %SetHSLBaseDRAdder(BinFile,destinationAddr,destinationBNK,length)
  • %SetRGBBaseDRAdder(BinFile,destinationAddr,destinationBNK,length)

En este caso en el parámetro BinFile debes poner en el caso de que sea un archivo binario que se haya sido incluido a través de DRAdder, usarías los defines !GFXYY, mientras que si usas una tabla, pondrías en ese parámetro el nombre de la tabla.

Algunos ejemplos de uso:

Establecer una base HSL de 16 colores usando como source la dirección guardada en las scratch $00-$02 y usando como destination la dirección guardada en las scratch $03-$05.

    %SetHSLBase($00,$02,$03,$05,#$0010)


Establecer una base RGB de 48 colores usando como source al buffer DZ_PPUMirrors_CGRAM_PaletteCopy y como destination el buffer DZ_PPUMirrors_CGRAM_BasePalette, iniciando desde el primer color de la paleta 5.

    %SetRGBBaseDefault(#$0050,#$0030)

Establecer una base HSL de 64 colores usando como source una paleta de colores guardada en el quinto recurso dinamico designado por el DRAdder y guardandolo en el buffer DZ_PPUMirrors_CGRAM_BasePalette, iniciando desde el tercer color de la paleta 3.

    %SetHSLBaseDRAdder(!GFX04,#DZ_PPUMirrors_CGRAM_BasePalette+($33*3),#DZ_PPUMirrors_CGRAM_BasePalette>>16,#$0040)

Establecer una base RGB de 3 colores desde una tabla local y guardandolo en la dirección de RAM especificada en las scratch $00-$02.

ColorTable:
    dw $0001,$07FF,$041F
    %SetHSLBaseDRAdder(ColorTable,$00,$02,#$0003)

Nota: El procesamiento de estas macros es lento y no es necesario llamarlo cada frame, se recomienda llamarlo solo 1 vez despues de haber obtenido la paleta, recuerda que si utilizas las macro TransferToCGRAMBuffer, debes esperar 1 game loop para llamar las macros SetBase.

Hacer efectos de paleta

Para realizar este paso, debes llamar las macros Mix, estas macros lo que hacen es ponderar los canales RGB o HSL (dependiendo de la macro), con los valores que pongas como parametro. Para esto tienes 2 parametros por valor:

  • ratio: Va desde 0 hasta 8, donde #$00 significa que utilizara 100% el canal que venia en el color en la paleta y #$08 significa que utilizara 100% el canal que entregaste como parametro.
  • value: Va desde 0 hasta 31 (#$1F), indica el valor que le estarias entregando como parametro a ese canal especifico.

Las macros son las siguientes:

  • %MixHSL(ratio1,ratio2,ratio3,value1,value2,value3,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixRGB(ratio1,ratio2,ratio3,value1,value2,value3,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixHS(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixHL(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixSL(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixRB(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixRG(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixGB(ratio1,ratio2,value1,value2,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixH(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixS(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixL(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixR(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixG(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixB(ratio1,value1,sourceAddr,sourceBNK,destinationAddr,destinationBNK,length)
  • %MixHSLDefault(ratio1,ratio2,ratio3,value1,value2,value3,offset,length)
  • %MixRGBDefault(ratio1,ratio2,ratio3,value1,value2,value3,offset,length)
  • %MixHSDefault(ratio1,ratio2,value1,value2,offset,length)
  • %MixHLDefault(ratio1,ratio2,value1,value2,offset,length)
  • %MixSLDefault(ratio1,ratio2,value1,value2,offset,length)
  • %MixRBDefault(ratio1,ratio2,value1,value2,offset,length)
  • %MixRGDefault(ratio1,ratio2,value1,value2,offset,length)
  • %MixGBDefault(ratio1,ratio2,value1,value2,offset,length)
  • %MixHDefault(ratio1,value1,offset,length)
  • %MixSDefault(ratio1,value1,offset,length)
  • %MixLDefault(ratio1,value1,offset,length)
  • %MixRDefault(ratio1,value1,offset,length)
  • %MixGDefault(ratio1,value1,offset,length)
  • %MixBDefault(ratio1,value1,offset,length)

Podemos notar 2 tipos de macros, las que tienen la palabra Default y las que no. Las que no tienen la palabra default, debes especificar la dirección desde donde vienen los datos (sourceAddr y sourceBNK) y la dirección a la que llegan esos datos (destinationAddr) y (destinationBNK), addr se refiere a la dirección y BNK se refiere al banco de memoria. Por otra parte en los que dicen Default, use utiliza como source el buffer DZ_PPUMirrors_CGRAM_BasePalette y como destination el buffer DZ_PPUMirrors_CGRAM_PaletteWriteMirror y se utiliza el parámetro offset para indicar desde que color del buffer empezar a leer (considerar que los buffers son de 256 colores). Por ultimo, el parametro length indica la cantidad de colores que se procesaran.

Ejemplo de uso:

Reemplazar el canal H de todos los colores en la paleta 0, por el valor #$1F.

    %MixHDefault(#$08,#$1F,#$0000,#$0010)</code>

Nota: Estas macros dependiendo de cuantos colores se procesen, pueden ser algo lentas y causar slowdown, usar con discreción.

Enviar Paleta Procesada

Esto ya deberiamos saber como realizarlo si leiste el capitulo de Cargar paleta de colores dinámicamente. Solo debes tener cuidado de como usar eso de forma correcta.

Ejemplo

Aqui un ejemplo del sistema, en este caso tenemos un sprite que lo unico que hara es ir alternando el hue de toda la paleta 2.

!Started = !SpriteMiscTable1    ;Lo utilizaremos para evitar llamar la rutina de cargar paleta y de SetBase más de una vez
!H = !SpriteMiscTable2          ;Sera el valor de Hue que usaremos

print "INIT ",pc

    LDA #$00        ;Carga 0 en el Canal Hue y en la variable !Started,x
    STA !Started,x
    STA !H,x

RTL
print "MAIN ",pc
    PHB
    PHK
    PLB
    JSR SpriteCode
    PLB
RTL

!Offset = $0020        ;Color desde donde inicia el procesamiento

SpriteCode:

    LDA !Started,x
    BNE +              ;Si Started es 0, entonces cargamos la paleta desde la
                       ;paleta que esta cargada en el nivel. Y ponemos Started en 1
    INC !Started,x

    %TransferToCGRAMBuffer($01, #$01FE)
    LDX !SpriteIndex
RTS
+
    CMP #$01
    BNE +              ;Si Started es 1, entonces hacemos SetBase para cargar una base HSL.
                       ;Además ponemos Started en 2 para evitar hacer SetBase 2 veces.
    INC !Started,x


    %SetHSLBase("#DZ_PPUMirrors_CGRAM_PaletteCopy+(!Offset*2)","#DZ_PPUMirrors_CGRAM_PaletteCopy>>16","#DZ_PPUMirrors_CGRAM_BasePalette+(!Offset*3)","#DZ_PPUMirrors_CGRAM_BasePalette>>16",#$0010)

    LDX !SpriteIndex 

+
    ;Cambiamos el canal H de los colores en la paleta 2
    %MixH("#$08","!H,x","#DZ_PPUMirrors_CGRAM_BasePalette+(!Offset*3)","#DZ_PPUMirrors_CGRAM_BasePalette>>16","#DZ_PPUMirrors_CGRAM_PaletteWriteMirror+(!Offset*2)","#DZ_PPUMirrors_CGRAM_PaletteWriteMirror>>16",#$0010)

    ;Enviamos la paleta para que sea usada en el nivel.
    %TransferToCGRAM(#!Offset, "#DZ_PPUMirrors_CGRAM_PaletteWriteMirror+(!Offset*2)","#DZ_PPUMirrors_CGRAM_PaletteWriteMirror>>16", #$0020)

    LDX !SpriteIndex 

    ;Actualizamos el valor del Hue para que vaya alternando.
    LDA !H,x
    INC A
    STA !H,x
    CMP #$20
    BCS +
RTS
+
    STZ !H,x
RTS

Este ejemplo se ve asi:

Hue Change

Nota: La razón por la que no se llama la macro TransferToCGRAMBuffer en el Sprite Init, es porque si se pone ahí, no funciona correctamente si el sprite es situado al inicio del nivel, ya que, la paleta aun no estaría correctamente cargada en el nivel.

Cargar Gráficos Manualmente

TransferToCGRAM(CGRAMOffset, TableAddr, TableBNK, Lenght)

LDA DZ_Timer AND #$01 BNE +

   LDA DZ_Timer

CMP !SafeFrame BNE + RTS

Librería de Dynamic Z

Librería de Dyzen