es/Dynamic Xtreme UberASM guide
| English | Português | Español | 日本語 |
En este artículo se explica cómo utilizar Dynamic Xtreme desde UberASM para configurar los recursos de un nivel y crear cambios de gráficos y colores durante el juego. Se incluyen ejemplos para reservar paletas, elegir el espacio disponible para sprites dinámicos, crear animaciones y modificar los gráficos del jugador.
Requisitos
- Tener UberASMTool instalado y configurado para insertar códigos en tu ROM.
- Tener Dynamic Xtreme instalado con las funciones necesarias para el ejemplo que quieras utilizar. Consulta la guía de inicio rápido para su instalación.
Para consultar los parámetros de una llamada o el uso de una variable, utiliza la referencia de la API ASM.
Estas son las funcionalidades necesarias para cada ejemplo:
| Ejemplo | Funcionalidades que debes habilitar |
|---|---|
| Reservar paletas para otros recursos | PalettesChange.
|
| Elegir el espacio de VRAM para sprites dinámicos | GraphicsChange y DynamicPoses. Para dibujar esas poses con Dynamic Xtreme también necesitas DrawingSystem.
|
| Configurar la cantidad de datos que se transfieren por frame | GraphicsChange para las cargas gráficas o PalettesChange para las cargas de paletas; ambas si utilizas los dos tipos de carga.
|
| Crear animaciones de gráficos | GraphicsChange y PalettesChange, porque el ejemplo carga gráficos y una paleta. Añade PalettesEffects si cambias !PaletteGlobalEffect a 1 para utilizar el efecto global.
|
| Cambiar paletas durante el juego | PalettesChange y, con !PaletteGlobalEffect = 1 como en el ejemplo, PalettesEffects. Para cargar sin efecto global, utiliza !PaletteGlobalEffect = 0; en ese caso basta con PalettesChange.
|
| Cambiar los gráficos del jugador | PlayerFeatures, junto con sus dependencias GraphicsChange y PalettesChange.
|
| Aplicar un efecto global de paleta, fijo, cíclico o de transición | PalettesChange y PalettesEffects.
|
La animación de gráficos de esta guía copia datos a un destino fijo de VRAM: no necesita DynamicPoses ni DrawingSystem. Para conocer las rutinas y macros que habilita cada funcionalidad, consulta la tabla de funcionalidades de la API.
Preparar los ejemplos
Cada ejemplo es independiente. Guarda el código de nivel en la carpeta correspondiente de UberASMTool y añádelo a su lista. Si reúnes varios ejemplos en un archivo, combina sus bloques load, init y main para no repetir las mismas etiquetas.
loadprepara datos durante la carga del nivel.initprepara las opciones al entrar al nivel. El ejemplo de reserva de paletas utiliza este bloque.mainejecuta el código durante el juego, una vez por frame.
Los defines y macros de Dynamic Xtreme deben estar disponibles en UberASMTool mediante la integración realizada durante la instalación. Si utilizas gráficos, paletas o efectos adicionales, decláralos en el ASM mediante los includes correspondientes:
;#incdyn "DynamicXtreme/MyResources.dynamicinfo" ;#incpale "DynamicXtreme/MyEffect.paleffect"
Las rutas son relativas al ASM. Incluye únicamente los archivos que utiliza tu ejemplo. También puedes usar las carpetas tradicionales de Dynamic Xtreme, como se explica en Archivos y formatos.
Después de añadir o modificar estos recursos, instala primero Dynamic Xtreme y después UberASMTool. Si solo cambias la lógica del ASM, basta con volver a ejecutar UberASMTool.
Los nombres RAM_LIBRE_... de los ejemplos son marcadores: reemplázalos por direcciones de RAM libre antes de insertar el código. No son defines de Dynamic Xtreme. Reserva la cantidad de bytes indicada y utiliza direcciones distintas para variables que deban coexistir. Comprueba que no las utilicen el juego, Dynamic Xtreme, Lunar Magic u otros recursos; que una dirección esté libre en SMW original no garantiza que siga libre en tu ROM. Puedes buscar direcciones en el mapa de RAM de SMW Central filtrado por espacios vacíos.
Si utilizas una dirección corta de RAM de SMW que no pertenece a la Direct Page, añade |!addr para aplicar el remapeo correspondiente a LoROM o SA-1. Por ejemplo, al elegir la dirección del temporizador:
!AnimationTimer = RAM_LIBRE_ANIM_TIMER|!addr
Sustituye RAM_LIBRE_ANIM_TIMER por la dirección elegida y conserva |!addr. Las direcciones de Direct Page se acceden mediante la base D del contexto de ejecución y no utilizan este remapeo. Si eliges una dirección completa de 24 bits, comprueba su banco y que las instrucciones del ejemplo puedan acceder a ella; |!addr está pensado aquí para las direcciones cortas fuera de Direct Page.
Reservar paletas para otros recursos
Si una animación, un HUD u otro recurso necesita una paleta fija, conviene reservarla para que Dynamic Xtreme no la elija al asignar colores a los sprites dinámicos.
Para reservarla, escribe $FFFE en su entrada de DX_Dynamic_Palettes_ID. Cada entrada ocupa 2 bytes. Por ejemplo, la paleta 9 utiliza el desplazamiento $12: 9 × 2.
Consulta el significado de esta tabla en Paletas y efecto global de la API.
init:
REP #$20
LDA #$FFFE
STA.l DX_Dynamic_Palettes_ID+$12 ; Reservar la paleta 9
SEP #$20
RTL
Para reservar varias, escribe el mismo valor en sus entradas:
| Paleta | Desplazamiento |
|---|---|
| 8 | $10
|
| 9 | $12
|
| A | $14
|
| B | $16
|
| C | $18
|
| D | $1A
|
| E | $1C
|
| F | $1E
|
La reserva impide que la asignación automática reclame esa paleta; no carga colores por sí misma. Un recurso configurado para utilizarla manualmente todavía puede cambiar sus colores, por lo que no debes asignársela también a otro recurso que necesite una paleta diferente.
Elegir el espacio de VRAM para sprites dinámicos
Los sprites dinámicos necesitan una parte de VRAM donde cargar sus poses. Elige un espacio que no estés utilizando para gráficos fijos del nivel.
Puedes consultar el mapa de VRAM de SMW en SNESLab para ver qué regiones corresponden a SP1, SP2, SP3 y SP4.
Por ejemplo, para permitir que utilicen SP4:
La rutina DynamicPoseSpaceConfig recibe una de las configuraciones predefinidas.
init:
LDA #!DynamicSpaceConfigSP4
JSL !DynamicPoseSpaceConfig
RTL
Si necesitas más espacio y el nivel lo permite, puedes elegir !DynamicSpaceConfigSP34 para SP3 y SP4, o !DynamicSpaceConfigSP234 para SP2, SP3 y SP4. Hay más opciones, incluidas medias páginas y configuraciones para compartir espacio con el jugador o DSX.
Los defines disponibles están en DXDefines.asm y en la tabla de configuraciones de la API. Configura el espacio antes de que el nivel empiece a cargar poses; cambiarlo no elimina automáticamente las poses que ya estaban en VRAM.
Crear una configuración propia
Si necesitas combinar espacios de otra manera, utiliza DefineDynamicSpaceConfig. Este ejemplo deja disponible la primera mitad de SP3 y todo SP4, y restringe el resto:
init:
; SP1 y SP2 restringidos.
%DefineDynamicSpaceConfig($00, $3F, $80)
; Primera mitad de SP3 disponible.
%DefineDynamicSpaceConfig($40, $4F, $00)
; Segunda mitad de SP3 restringida.
%DefineDynamicSpaceConfig($50, $5F, $80)
; SP4 disponible.
%DefineDynamicSpaceConfig($60, $7F, $00)
RTL
Los dos primeros parámetros son el primer y el último bloque de 16×16, incluidos ambos. El tercero utiliza $00 para permitir poses dinámicas o $80 para restringir el espacio. Los IDs avanzan de izquierda a derecha y de arriba abajo, con 8 bloques por fila. La documentación de la macro incluye la tabla de numeración completa.
Este ejemplo sustituye la llamada a la configuración predefinida: no ejecutes las dos configuraciones una después de otra. Ajusta los rangos para cubrir los bloques $00–$7F sin superponerlos y restringe las zonas que necesiten conservar otros gráficos del nivel. Requiere GraphicsChange y DynamicPoses, igual que el ejemplo anterior.
Configurar la cantidad de datos que se transfieren por frame
DX_Dynamic_MaxDataPerFrame indica cuántos bytes pueden programar las cargas normales de Dynamic Xtreme durante un frame. Lo comparten las poses dinámicas, las animaciones y las demás transferencias realizadas con sus macros.
Consulta estas variables en Contadores y cantidad de datos por frame de la API.
El valor predeterminado es $0800, equivalente a 2048 bytes. Este ejemplo lo aumenta a $0A00, equivalente a 2560 bytes:
init:
REP #$20
LDA #$0A00
STA.l DX_Dynamic_MaxDataPerFrame
SEP #$20
RTL
Subirlo permite intentar más cargas durante el mismo frame, pero no aumenta el tiempo que tiene SNES para copiarlas. Si la cantidad total es excesiva, pueden aparecer problemas visuales. Ajusta el valor según el nivel y comprueba el resultado con sus sprites y animaciones funcionando juntos.
Las macros normales devuelven el carry desactivado si una carga no cabe en el límite. En ese caso, conserva lo que ya se muestra y vuelve a intentarlo después. Las variantes Forced se saltan la comprobación y también suman sus bytes al total.
Crear animaciones de gráficos
Este ejemplo crea una animación de gráficos: una tabla indica dónde empieza cada imagen, un temporizador controla cuándo cambiarla y la imagen anterior se mantiene si no se acepta la transferencia.
Utiliza TransferToVRAM para los cambios de gráficos, su variante Forced para la carga inicial y TransferPaletteResource para los colores. Consulta esos enlaces para sus parámetros, registros y resultados.
Declara los archivos del ejemplo en DynamicXtreme/SMMArrow.dynamicinfo:
Resources:
SMMArrow.bin
Palettes:
SMMArrowPal.bin
;#incdyn "DynamicXtreme/SMMArrow.dynamicinfo"
; Aquí pon RAM libre: un byte distinto para cada variable.
!CurrentPoseIndex = RAM_LIBRE_ANIM_POSE
!AnimationTimer = RAM_LIBRE_ANIM_TIMER
!PaletteLoaded = RAM_LIBRE_ANIM_PALETA
; Frames de espera antes de intentar el siguiente cambio.
!TimePerPose = $03
; Bytes de cada imagen: un tile de 8x8 en 4bpp ocupa $0020.
!DataSend = $0200
; Cantidad de imágenes utilizadas.
!NumberOfPoses = $05
; Origen: recurso instalado. También puedes usar !AN2Buffer2.
!Source = !ResourceSMMArrow
; Dirección de destino en VRAM.
!Destination = !BG2Line0
; Paleta instalada y primer color de destino.
!Palette = "SMMArrowPal"
!PaletteDestination = $71
; 0 = sin efecto global; 1 = recibe el efecto global.
!PaletteGlobalEffect = 0
init:
STZ !CurrentPoseIndex
LDA #!TimePerPose
STA !AnimationTimer
STZ !PaletteLoaded
%ForcedTransferToVRAM(#!Destination, #!Source, #((!Source>>16)&$FF), #!DataSend)
%TransferPaletteResource(!Palette, #!PaletteDestination, !PaletteGlobalEffect)
BCC .Return
LDA #$01
STA !PaletteLoaded
.Return
RTL
main:
; Reintentar la paleta si no se aceptó durante init.
LDA !PaletteLoaded
BNE .Animate
%TransferPaletteResource(!Palette, #!PaletteDestination, !PaletteGlobalEffect)
BCS .PaletteReady
RTL
.PaletteReady
LDA #$01
STA !PaletteLoaded
.Animate
LDA !AnimationTimer
BEQ .NextPose
DEC !AnimationTimer
RTL
.NextPose
LDA !CurrentPoseIndex
INC A
CMP #!NumberOfPoses
BCC .SelectPose
LDA #$00
.SelectPose
STA $05
ASL
TAX
REP #$20
LDA.l PosesTable,x
CLC
ADC.w #!Source
STA $00
SEP #$20
%TransferToVRAM(#!Destination, $00, #((!Source>>16)&$FF), #!DataSend)
BCC .Return
LDA $05
STA !CurrentPoseIndex
LDA #!TimePerPose
STA !AnimationTimer
.Return
RTL
PosesTable:
dw $0000
dw !DataSend*$01
dw !DataSend*$02
dw !DataSend*$03
dw !DataSend*$04
Qué puedes cambiar
!TimePerPose: tiempo de espera. Con$03, el cambio se intenta cada cuatro frames: tres para descontar el temporizador y uno para realizar el cambio.!DataSend: bytes de cada imagen.$0200equivale a 16 tiles de 8×8 en 4bpp.!NumberOfPosesyPosesTable: cantidad de imágenes y sus desplazamientos dentro del origen. El ejemplo utiliza las primeras cinco imágenes deSMMArrow.bin. Si añades más, amplía también la tabla.!Source: dirección de los gráficos. El ejemplo lee directamente!ResourceSMMArrow; para animar gráficos ya preparados por Lunar Magic puedes usar!AN2Buffer2.!Destination: zona de VRAM que se actualizará. Coloca en el nivel tiles que utilicen esa zona y comprueba que otro recurso no la sobrescriba.!Palette,!PaletteDestinationy!PaletteGlobalEffect: paleta, primer color de destino y participación en el efecto global.
Reemplaza los tres marcadores de RAM del principio por direcciones disponibles durante todo el nivel: cada variable necesita un byte propio. No son scratch temporales. !PaletteLoaded permite reintentar una carga de paleta rechazada sin repetirla después de que haya sido aceptada.
!ResourceSMMArrow, !PaletteSMMArrowPal y !PaletteSMMArrowPalSize se generan al instalar el DynamicInfo y quedan en DXDefines.asm. Sus nombres se normalizan en PascalCase eliminando separadores; en este caso se conservan las mayúsculas de SMMArrow y SMMArrowPal.
Todas las imágenes utilizadas deben quedar en el mismo banco del origen. La carga inicial de gráficos utiliza Forced; los siguientes cambios respetan el límite de datos por frame. Si una carga se rechaza, el código conserva la imagen anterior y vuelve a intentarla en el siguiente frame, sin avanzar el índice ni reiniciar el temporizador.
El temporizador avanza cada vez que se ejecuta main. Si quieres detener la animación durante la pausa o cuando se congela la acción, puedes comprobar $13D4|!addr y $9D antes de descontarlo.
Cambiar paletas durante el juego
Las macros TransferPalette permiten cargar colores y mantener actualizadas las copias que utilizan SMW y Dynamic Xtreme. También se encargan de preparar los efectos de paleta, por lo que no necesitas copiar buffers ni borrar indicadores a mano.
Este ejemplo utiliza TransferPaletteResource; consulta su entrada en la API para los parámetros y el significado del carry.
El ejemplo utiliza SMMArrowPal.bin, un archivo de 15 colores ($001E bytes), y cambia los colores 1-F de la paleta 7. El color 0 queda sin modificar.
En DynamicXtreme/SMMArrow.dynamicinfo, declara los gráficos como recurso general y la paleta en Palettes:
Resources:
SMMArrow.bin
Palettes:
SMMArrowPal.bin
En el ASM del nivel:
;#incdyn "DynamicXtreme/SMMArrow.dynamicinfo"
; Aquí pon una RAM libre de un byte.
!PaletteChangePending = RAM_LIBRE_CAMBIO_PALETA
!Palette = "SMMArrowPal"
!PaletteDestination = $71
!PaletteGlobalEffect = 1
init:
LDA #$01
STA !PaletteChangePending
; Continuar con el primer intento de carga.
main:
LDA !PaletteChangePending
BNE .TryTransfer
RTL
.TryTransfer
%TransferPaletteResource(!Palette, #!PaletteDestination, !PaletteGlobalEffect)
BCC .Return
; Confirmar el cambio solo si la llamada fue aceptada.
STZ !PaletteChangePending
.Return
RTL
Reemplaza RAM_LIBRE_CAMBIO_PALETA por una dirección de RAM libre de un byte, disponible durante todo el nivel. !PaletteChangePending se pone en 1 al solicitar el cambio y solo vuelve a 0 cuando la macro devuelve el carry activado. Si la llamada falla, el cambio queda pendiente y se reintenta en el siguiente frame. Una vez aceptado, no se repite la carga.
!Palette selecciona el recurso y !PaletteDestination el primer color de destino. !PaletteGlobalEffect, utilizado como último parámetro de la macro, elige cómo se carga:
1: prepara los colores originales para que reciban el efecto global antes de mostrarse. También deja la paleta preparada si todavía no has seleccionado un efecto.0: carga los colores sin efecto global y excluye esa paleta del efecto. Además actualiza los buffers de SMW, necesarios para evitar problemas de colores durante la animación de victoria del goal.
Para cargar la misma paleta sin efecto global, cambia únicamente este define; conserva la comprobación del carry:
!PaletteGlobalEffect = 0
Si los efectos están deshabilitados en la instalación, ambas opciones realizan una carga directa. Cuando están habilitados y utilizas 1, el sistema carga el resultado ya procesado; no se muestra primero la paleta sin efecto durante un frame.
SMMArrowPal utiliza los defines !PaletteSMMArrowPal y !PaletteSMMArrowPalSize, generados desde Palettes del DynamicInfo y publicados en DXDefines.asm. El nombre se normaliza en PascalCase eliminando separadores y poniendo en mayúscula la inicial de cada palabra. En este caso se conserva SMMArrowPal tal como está escrito.
Para solicitar de nuevo el cambio durante el juego, pon !PaletteChangePending en 1 cuando se cumpla la condición que elijas. No lo hagas cada frame si la paleta no cambia. En una carga directa, el carry activado indica que se programó la transferencia; con efecto global, indica que los colores originales quedaron preparados, no que el resultado ya esté en pantalla.
La opción de efecto se aplica a toda la paleta de 16 colores. Si cargas colores para recibir un efecto global, prepara todos sus colores visibles, como los 15 de este ejemplo; no cambies solo unos pocos esperando conservar una base que todavía no has preparado.
Si eliges una paleta de sprites 8-F para un uso fijo, resérvala primero como se explica al comienzo del artículo. Estas macros no realizan asignación automática. Para cargar desde una dirección, un puntero o una tabla, consulta Cargar colores con TransferPalette en la API.
Cambiar los gráficos del jugador
Este ejemplo alterna entre el jugador original y Geno al pulsar arriba. Sus defines permiten elegir los gráficos y las cuatro paletas: Mario, Luigi, Mario con fuego y Luigi con fuego. También vuelve a seleccionar la paleta cuando cambia el estado del jugador.
Necesita PlayerFeatures habilitado. Los gráficos personalizados deben utilizar la misma organización de poses que los originales; cambiar el origen no adapta un spritesheet de otro formato.
El código utiliza las variables del jugador y las macros DivW, TransferPaletteFromPointer y TransferPaletteFromAddress. Sus entradas de la API explican los parámetros, registros y resultados de cada llamada.
En DynamicXtreme/Geno.dynamicinfo, declara:
Resources:
Geno.bin
Palettes:
GenoPal.bin
GenoPalPlayer2.bin
FireGenoPal.bin
FireGenoPalPlayer2.bin
;#incdyn "DynamicXtreme/Geno.dynamicinfo"
; Aquí pon una RAM libre de dos bytes consecutivos.
!Last0D82 = RAM_LIBRE_JUGADOR_2_BYTES
; Gráficos del jugador personalizado.
!PlayerGFXResource = !ResourceGeno
; Paletas: jugador 1, jugador 2 y sus variantes con fuego.
!PlayerGFXPalette = !PaletteGenoPal
!PlayerGFXPalettePlayer2 = !PaletteGenoPalPlayer2
!PlayerGFXFirePalette = !PaletteFireGenoPal
!PlayerGFXFirePalettePlayer2 = !PaletteFireGenoPalPlayer2
; Botón de cambio: $08 = arriba.
!ChangeButton = $08
init:
REP #$20
LDA $0D82|!addr
STA !Last0D82
SEP #$20
main:
LDA !ButtonDown_BYETUDLR
AND #!ChangeButton
BNE .Change
REP #$20
LDA $0D82|!addr
CMP !Last0D82
BNE .Reload
SEP #$20
RTL
.Change
LDA.l DX_Dynamic_Player_GFX_BNK
CMP.b #!GFX32Buffer>>16
BEQ .ChooseGeno
JMP .Mario
.ChooseGeno
JMP .Geno
.Reload
SEP #$20
LDA.l DX_Dynamic_Player_GFX_BNK
CMP.b #!GFX32Buffer>>16
BNE .Geno
JMP .Mario
.Geno
; Obtener 0, 1, 2 o 3 según jugador y estado de fuego.
REP #$20
LDA $0D82|!addr
SEC
SBC #!PlayerBasePalette
STA $00
SEP #$20
%DivW($01, $00, #$14)
; Cada dirección de GenoPals ocupa tres bytes.
LDA !DivisionResult
ASL
CLC
ADC !DivisionResult
TAX
; Conservar el origen elegido y preparar los colores 2-5.
REP #$20
LDA.l GenoPals,x
STA $03
CLC
ADC #$0002
STA $00
SEP #$20
LDA.l GenoPals+2,x
STA $02
STA $05
%TransferPaletteFromPointer($00, #$82, $0008, 0)
BCS .ApplyGeno
RTL
.ApplyGeno
REP #$20
LDA.w #!PlayerGFXResource
STA.l DX_Dynamic_Player_GFX_Addr
LDA.w #(!PlayerGFXResource>>16)&$00FF
STA.l DX_Dynamic_Player_GFX_BNK
; La rutina del jugador carga los colores 6-F.
LDA $03
CLC
ADC #$000A
STA.l DX_Dynamic_Player_Palette_Addr
SEP #$20
LDA $05
STA.l DX_Dynamic_Player_Palette_BNK
JMP .ConfirmSelection
.Mario
%TransferPaletteFromAddress(!PlayerPaletteColor2To5, #$82, $0008, 0)
BCS .ApplyMario
RTL
.ApplyMario
REP #$20
LDA.w #!GFX32Buffer
STA.l DX_Dynamic_Player_GFX_Addr
LDA $0D82|!addr
STA.l DX_Dynamic_Player_Palette_Addr
LDA.w #(!GFX32Buffer>>16)&$00FF
STA.l DX_Dynamic_Player_GFX_BNK
LDA #$0000
STA.l DX_Dynamic_Player_Palette_BNK
SEP #$20
.ConfirmSelection
REP #$20
LDA $0D82|!addr
STA !Last0D82
SEP #$20
RTL
GenoPals:
dl !PlayerGFXPalette
dl !PlayerGFXPalettePlayer2
dl !PlayerGFXFirePalette
dl !PlayerGFXFirePalettePlayer2
Qué puedes cambiar
!PlayerGFXResource: gráficos del personaje personalizado.- Los cuatro defines
!PlayerGFX...Palette...: direcciones de sus paletas, en el orden de la tablaGenoPals. !ChangeButton: botón que alterna entre los personajes. Utiliza los bits del controlBYETUDLR;$08corresponde a arriba.!Last0D82: reemplazaRAM_LIBRE_JUGADOR_2_BYTESpor una dirección de RAM libre con dos bytes consecutivos disponibles. Guarda la selección de paleta del juego; ninguno de esos bytes debe estar ocupado por otro código.
!ResourceGeno y los defines !PaletteGenoPal, !PaletteGenoPalPlayer2, !PaletteFireGenoPal y !PaletteFireGenoPalPlayer2 se generan desde el DynamicInfo y quedan en DXDefines.asm. Los nombres usan PascalCase eliminando espacios, guiones y guiones bajos, y conservando las mayúsculas ya presentes.
Cada archivo de paleta contiene los colores 1-F. Se omite su primer color para copiar los colores 2-5; los últimos diez comienzan en +$0A y los utiliza la rutina del jugador.
Las cargas de los colores 2-5 utilizan TransferPalette, que también actualiza los buffers de SMW. El ejemplo no confirma el cambio de gráficos si esa transferencia es rechazada. Un cambio pendiente por el estado del jugador se vuelve a intentar mientras no coincida con !Last0D82; si se rechaza un cambio solicitado con el botón, vuelve a pulsarlo.
El código distingue al jugador original por el banco de !GFX32Buffer. Si lo adaptas para alternar entre varios personajes personalizados en ROM, necesitarás otra forma de recordar cuál está seleccionado.
Este ejemplo carga la paleta del jugador sin efecto global. No basta con cambiar a 1 el parámetro de una carga de cuatro colores para añadirle un efecto: en ese caso hay que preparar todos los colores visibles de la paleta 8 y respetar su carga especial.
Aplicar un efecto global de paleta
Los siguientes ejemplos permiten aplicar un efecto fijo, recorrer efectos en un ciclo o realizar una transición que se detiene al terminar. Puedes elegir cada paleta por separado e incluir el color fijo. Los códigos de ciclo y transición también permiten configurar la velocidad con !Delay.
Los tres configuran las variables del efecto global y las de color fijo y primer color de la paleta. Consulta sus entradas en la API para el significado de cada variable.
En los archivos *.paleffect, EffectType selecciona el tipo de efecto. Los ratios utilizan valores de 0 a 127 y los valores Channel, de 0 a 31.
Elegir las paletas afectadas
En cada código, cambia !P0 a !PF a 1 para incluir la paleta correspondiente o a 0 para excluirla. !EnableFixedColor controla el color fijo y !EnableColor00 permite incluir también el color 0,0.
Las máscaras !EnableBG, !EnableSP y !EnableColors se calculan a partir de esos ajustes. No necesitas modificarlas a mano. En la API actual, el bit bajo corresponde a la primera paleta de cada grupo:
| Valor del bit | Paleta de fondo | Paleta de sprites |
|---|---|---|
$01 |
0 | 8 |
$02 |
1 | 9 |
$04 |
2 | A |
$08 |
3 | B |
$10 |
4 | C |
$20 |
5 | D |
$40 |
6 | E |
$80 |
7 | F |
El color fijo y el color 0,0 utilizan DX_PPU_ColorEffect_Enable: bit $01 y bit $02, respectivamente.
En sprites con asignación dinámica, configura también el bit E de PaletteOption. Si cargas paletas mediante TransferPalette, su último parámetro determina si esas paletas participan en el efecto global. Evita que otro código vuelva a escribir máscaras distintas sin tener en cuenta esa configuración.
Aplicar un efecto fijo
Este ejemplo aplica el efecto que indiques en !PaletteEffetID. La configuración mostrada habilita todas las paletas y el color fijo, y selecciona el último efecto de DecreaseSaturation.
Guarda el siguiente contenido en DynamicXtreme/DecreaseSaturation.paleffect:
{
"Effects": [
{"Channel2": 0, "Ratio2": 4, "EffectType": 11},
{"Channel2": 0, "Ratio2": 8, "EffectType": 11},
{"Channel2": 0, "Ratio2": 12, "EffectType": 11},
{"Channel2": 0, "Ratio2": 16, "EffectType": 11},
{"Channel2": 0, "Ratio2": 20, "EffectType": 11},
{"Channel2": 0, "Ratio2": 25, "EffectType": 11},
{"Channel2": 0, "Ratio2": 29, "EffectType": 11},
{"Channel2": 0, "Ratio2": 33, "EffectType": 11},
{"Channel2": 0, "Ratio2": 37, "EffectType": 11},
{"Channel2": 0, "Ratio2": 41, "EffectType": 11},
{"Channel2": 0, "Ratio2": 45, "EffectType": 11},
{"Channel2": 0, "Ratio2": 49, "EffectType": 11},
{"Channel2": 0, "Ratio2": 53, "EffectType": 11},
{"Channel2": 0, "Ratio2": 57, "EffectType": 11},
{"Channel2": 0, "Ratio2": 61, "EffectType": 11},
{"Channel2": 0, "Ratio2": 66, "EffectType": 11},
{"Channel2": 0, "Ratio2": 70, "EffectType": 11},
{"Channel2": 0, "Ratio2": 74, "EffectType": 11},
{"Channel2": 0, "Ratio2": 78, "EffectType": 11},
{"Channel2": 0, "Ratio2": 82, "EffectType": 11},
{"Channel2": 0, "Ratio2": 86, "EffectType": 11},
{"Channel2": 0, "Ratio2": 90, "EffectType": 11},
{"Channel2": 0, "Ratio2": 94, "EffectType": 11},
{"Channel2": 0, "Ratio2": 98, "EffectType": 11},
{"Channel2": 0, "Ratio2": 102, "EffectType": 11},
{"Channel2": 0, "Ratio2": 107, "EffectType": 11},
{"Channel2": 0, "Ratio2": 111, "EffectType": 11},
{"Channel2": 0, "Ratio2": 115, "EffectType": 11},
{"Channel2": 0, "Ratio2": 119, "EffectType": 11},
{"Channel2": 0, "Ratio2": 123, "EffectType": 11},
{"Channel2": 0, "Ratio2": 127, "EffectType": 11},
{"Channel2": 0, "Ratio2": 127, "EffectType": 11}
]
}
El archivo define 32 efectos. EffectType = 11 corresponde a MixS; Channel2 = 0 indica que se reduce la saturación y Ratio2 controla cuánto. El último efecto utiliza 127, que aplica el cambio completo.
El código ASM es:
;#incpale "DynamicXtreme/DecreaseSaturation.paleffect"
; 1 = incluir la paleta en el efecto; 0 = excluirla.
!P0 = 1
!P1 = 1
!P2 = 1
!P3 = 1
!P4 = 1
!P5 = 1
!P6 = 1
!P7 = 1
!P8 = 1
!P9 = 1
!PA = 1
!PB = 1
!PC = 1
!PD = 1
!PE = 1
!PF = 1
!EnableFixedColor = 1
!EnableColor00 = 0
; Efecto que se aplica al entrar al nivel.
!PaletteEffetID = !PaletteEffectIDDecreaseSaturation31
; Máscaras calculadas: no hace falta modificarlas.
!EnableBG = !P0|(!P1<<1)|(!P2<<2)|(!P3<<3)|(!P4<<4)|(!P5<<5)|(!P6<<6)|(!P7<<7)
!EnableSP = !P8|(!P9<<1)|(!PA<<2)|(!PB<<3)|(!PC<<4)|(!PD<<5)|(!PE<<6)|(!PF<<7)
!EnableColors = !EnableFixedColor|(!EnableColor00<<1)
init:
LDA #!EnableBG
STA.l DX_Dynamic_Palettes_GlobalBGEnable
LDA #!EnableSP
STA.l DX_Dynamic_Palettes_GlobalSPEnable
LDA #!EnableColors
STA.l DX_PPU_ColorEffect_Enable
REP #$20
LDA #!PaletteEffetID
STA.l DX_Dynamic_Palettes_GlobalEffectID
SEP #$20
RTL
Qué puedes cambiar
!P0a!PF: paletas afectadas.!EnableFixedColory!EnableColor00: participación de esos colores.!PaletteEffetID: efecto seleccionado. Puedes utilizar cualquier ID generado desde tu archivo de efectos, no solo el último de este ejemplo.
Los defines !PaletteEffectIDDecreaseSaturation0 a !PaletteEffectIDDecreaseSaturation31 se generan desde el *.paleffect y quedan en DXDefines.asm. El nombre usa PascalCase eliminando espacios, guiones y guiones bajos, y conservando las mayúsculas ya presentes.
La selección se realiza en init; no hace falta repetirla cada frame. Para quitar el efecto, escribe 0 en el ID global y deja las paletas habilitadas para que el sistema restaure sus colores originales:
REP #$20
LDA #$0000
STA.l DX_Dynamic_Palettes_GlobalEffectID
SEP #$20
Hacer que el efecto se repita en un ciclo
Este ejemplo recorre una secuencia de efectos y vuelve al primero. Puedes configurar la velocidad, el primer y último ID y las paletas afectadas. La configuración mostrada incluye solo la paleta 0 y el color fijo.
Guarda el siguiente contenido en DynamicXtreme/Hue.paleffect:
{
"Effects": [
{"Channel1": 1, "Ratio1": 127, "EffectType": 9},
{"Channel1": 2, "Ratio1": 127, "EffectType": 9},
{"Channel1": 3, "Ratio1": 127, "EffectType": 9},
{"Channel1": 4, "Ratio1": 127, "EffectType": 9},
{"Channel1": 5, "Ratio1": 127, "EffectType": 9},
{"Channel1": 6, "Ratio1": 127, "EffectType": 9},
{"Channel1": 7, "Ratio1": 127, "EffectType": 9},
{"Channel1": 8, "Ratio1": 127, "EffectType": 9},
{"Channel1": 9, "Ratio1": 127, "EffectType": 9},
{"Channel1": 10, "Ratio1": 127, "EffectType": 9},
{"Channel1": 11, "Ratio1": 127, "EffectType": 9},
{"Channel1": 12, "Ratio1": 127, "EffectType": 9},
{"Channel1": 13, "Ratio1": 127, "EffectType": 9},
{"Channel1": 14, "Ratio1": 127, "EffectType": 9},
{"Channel1": 15, "Ratio1": 127, "EffectType": 9},
{"Channel1": 16, "Ratio1": 127, "EffectType": 9},
{"Channel1": 17, "Ratio1": 127, "EffectType": 9},
{"Channel1": 18, "Ratio1": 127, "EffectType": 9},
{"Channel1": 19, "Ratio1": 127, "EffectType": 9},
{"Channel1": 20, "Ratio1": 127, "EffectType": 9},
{"Channel1": 21, "Ratio1": 127, "EffectType": 9},
{"Channel1": 22, "Ratio1": 127, "EffectType": 9},
{"Channel1": 23, "Ratio1": 127, "EffectType": 9},
{"Channel1": 24, "Ratio1": 127, "EffectType": 9},
{"Channel1": 25, "Ratio1": 127, "EffectType": 9},
{"Channel1": 26, "Ratio1": 127, "EffectType": 9},
{"Channel1": 27, "Ratio1": 127, "EffectType": 9},
{"Channel1": 28, "Ratio1": 127, "EffectType": 9},
{"Channel1": 29, "Ratio1": 127, "EffectType": 9},
{"Channel1": 30, "Ratio1": 127, "EffectType": 9},
{"Channel1": 31, "Ratio1": 127, "EffectType": 9},
{"Channel1": 31, "Ratio1": 127, "EffectType": 9}
]
}
El archivo define 32 efectos de tono. EffectType = 9 corresponde a MixH y Ratio1 = 127 aplica por completo el tono seleccionado.
;#incpale "DynamicXtreme/Hue.paleffect"
; 1 = incluir la paleta en el efecto; 0 = excluirla.
!P0 = 1
!P1 = 0
!P2 = 0
!P3 = 0
!P4 = 0
!P5 = 0
!P6 = 0
!P7 = 0
!P8 = 0
!P9 = 0
!PA = 0
!PB = 0
!PC = 0
!PD = 0
!PE = 0
!PF = 0
!EnableFixedColor = 1
!EnableColor00 = 0
; Valores permitidos: $00, $01, $03, $07, $0F, $1F, $3F, $7F y $FF.
!Delay = $03
!StartingPaletteEffetID = !PaletteEffectIDHue0
!EndPaletteEffetID = !PaletteEffectIDHue31
; Máscaras calculadas: no hace falta modificarlas.
!EnableBG = !P0|(!P1<<1)|(!P2<<2)|(!P3<<3)|(!P4<<4)|(!P5<<5)|(!P6<<6)|(!P7<<7)
!EnableSP = !P8|(!P9<<1)|(!PA<<2)|(!PB<<3)|(!PC<<4)|(!PD<<5)|(!PE<<6)|(!PF<<7)
!EnableColors = !EnableFixedColor|(!EnableColor00<<1)
init:
LDA #!EnableBG
STA.l DX_Dynamic_Palettes_GlobalBGEnable
LDA #!EnableSP
STA.l DX_Dynamic_Palettes_GlobalSPEnable
LDA #!EnableColors
STA.l DX_PPU_ColorEffect_Enable
REP #$20
LDA #!StartingPaletteEffetID
STA.l DX_Dynamic_Palettes_GlobalEffectID
SEP #$20
RTL
main:
LDA $14
AND #!Delay
BNE .Return
REP #$20
LDA.l DX_Dynamic_Palettes_GlobalEffectID
CMP #!EndPaletteEffetID
BCS .Restart
INC A
BRA .Store
.Restart
LDA #!StartingPaletteEffetID
.Store
STA.l DX_Dynamic_Palettes_GlobalEffectID
SEP #$20
.Return
RTL
Qué puedes cambiar
!P0a!PF,!EnableFixedColory!EnableColor00: colores afectados.!Delay: separación entre cambios.$00cambia cada frame,$01cada dos,$03cada cuatro y$07cada ocho. Utiliza únicamente los valores indicados en el comentario.!StartingPaletteEffetIDy!EndPaletteEffetID: primer y último efecto del ciclo. Deben pertenecer a una secuencia de IDs consecutivos, con el inicio menor o igual que el final.
El ciclo muestra el último ID antes de volver al primero. Utiliza $14 para marcar el momento de los cambios; por ello, el primer intervalo depende del valor que tenga ese contador al entrar al nivel. Si quieres detenerlo al congelarse la acción, comprueba también $9D y $13D4|!addr al comienzo de main.
Los defines !PaletteEffectIDHue0 a !PaletteEffectIDHue31 se generan desde el *.paleffect y quedan en DXDefines.asm. El nombre se normaliza en PascalCase eliminando separadores; en este caso conserva Hue.
Hacer una transición que se detiene al terminar
Este ejemplo avanza desde el efecto inicial hasta el final y mantiene ese último resultado. Puedes configurar las paletas afectadas, el color fijo, la velocidad y el rango de IDs.
Guarda el siguiente contenido en DynamicXtreme/Night.paleffect:
{
"Effects": [
{"Channel1": 0, "Channel2": 0, "Ratio1": 0, "Ratio2": 0, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 4, "Ratio2": 4, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 8, "Ratio2": 8, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 12, "Ratio2": 12, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 16, "Ratio2": 16, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 20, "Ratio2": 20, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 25, "Ratio2": 25, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 29, "Ratio2": 29, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 33, "Ratio2": 33, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 37, "Ratio2": 37, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 41, "Ratio2": 41, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 45, "Ratio2": 45, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 49, "Ratio2": 49, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 53, "Ratio2": 53, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 57, "Ratio2": 57, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 61, "Ratio2": 61, "EffectType": 2},
{"Channel1": 0, "Channel2": 0, "Ratio1": 66, "Ratio2": 66, "EffectType": 2}
]
}
La transición tiene 17 pasos. EffectType = 2 corresponde a MixRG: reduce el rojo y el verde sin modificar el azul. Los ratios aumentan de 0 a 66 para oscurecer los colores gradualmente sin eliminar por completo el rojo y el verde.
;#incpale "DynamicXtreme/Night.paleffect"
; 1 = incluir la paleta en el efecto; 0 = excluirla.
!P0 = 1
!P1 = 1
!P2 = 1
!P3 = 1
!P4 = 1
!P5 = 1
!P6 = 1
!P7 = 1
!P8 = 1
!P9 = 1
!PA = 1
!PB = 1
!PC = 1
!PD = 1
!PE = 1
!PF = 1
!EnableFixedColor = 1
!EnableColor00 = 0
; Valores permitidos: $00, $01, $03, $07, $0F, $1F, $3F, $7F y $FF.
!Delay = $03
!StartingPaletteEffetID = !PaletteEffectIDNight0
!EndPaletteEffetID = !PaletteEffectIDNight16
; Máscaras calculadas: no hace falta modificarlas.
!EnableBG = !P0|(!P1<<1)|(!P2<<2)|(!P3<<3)|(!P4<<4)|(!P5<<5)|(!P6<<6)|(!P7<<7)
!EnableSP = !P8|(!P9<<1)|(!PA<<2)|(!PB<<3)|(!PC<<4)|(!PD<<5)|(!PE<<6)|(!PF<<7)
!EnableColors = !EnableFixedColor|(!EnableColor00<<1)
init:
LDA #!EnableBG
STA.l DX_Dynamic_Palettes_GlobalBGEnable
LDA #!EnableSP
STA.l DX_Dynamic_Palettes_GlobalSPEnable
LDA #!EnableColors
STA.l DX_PPU_ColorEffect_Enable
REP #$20
LDA #!StartingPaletteEffetID
STA.l DX_Dynamic_Palettes_GlobalEffectID
SEP #$20
RTL
main:
LDA $14
AND #!Delay
BNE .Return
REP #$20
LDA.l DX_Dynamic_Palettes_GlobalEffectID
CMP #!EndPaletteEffetID
BCS .Finished
INC A
STA.l DX_Dynamic_Palettes_GlobalEffectID
.Finished
SEP #$20
.Return
RTL
Qué puedes cambiar
!P0a!PF,!EnableFixedColory!EnableColor00: colores afectados.!Delay: tiempo entre cambios, con los mismos valores permitidos en el ejemplo de ciclo.!StartingPaletteEffetIDy!EndPaletteEffetID: primer y último efecto. Deben ser IDs consecutivos en orden ascendente.
Los defines !PaletteEffectIDNight0 a !PaletteEffectIDNight16 se generan desde Night.paleffect y quedan en DXDefines.asm. Los nombres usan PascalCase eliminando separadores; aquí se conserva Night.
Este ejemplo cambia entre efectos de mezcla. Para una transición hacia otra paleta completa, utiliza PalTransitionRGB o PalTransitionHSL, explicados en Archivos y formatos.