Dynamic Xtreme sprite creation guide
| English | Português | Español | 日本語 |
This article explains how to create dynamic sprites using Dynamic Xtreme and PIXI.
The guide covers the complete process: preparing graphics, defining poses, including the necessary files in the sprite, and using the system to load and draw its poses on screen.
For questions about a call's registers, parameters, or return values, see the ASM API. The relevant sections of this guide link directly to the corresponding calls.
Requirements
To follow this guide, you need:
- PIXI installed and configured to insert sprites into your ROM.
- Dynamic Xtreme installed and configured. If you have not done this yet, see the Quick Start guide.
Files used by a dynamic sprite
In addition to the files normally used by PIXI, a dynamic sprite needs:
- Graphics file (*.bin): the graphics for the sprite's poses.
- DynamicInfo (*.dynamicinfo): specifies which graphics the sprite uses and how they are divided into poses.
- DrawInfo (*.drawinfo): the information needed to draw the poses.
- ASM file (*.asm): the sprite's code, which uses the resources above.
One possible arrangement is to keep the Dynamic Xtreme files with the PIXI resource itself. For example, a normal sprite could use this structure:
PIXI/
└── sprites/
└── MySprite/
├── MySprite.asm
├── MySprite.json
└── DynamicXtreme/
├── MySprite.bin
├── MySprite.dynamicinfo
└── MySprite.drawinfo
Keep the ASM and configuration file in the sprite's main folder. Store the graphics, DynamicInfo, and DrawInfo in the DynamicXtreme subfolder.
In this arrangement, the sprite's main ASM must include DynamicInfo and DrawInfo with these directives:
;#incdyn "DynamicXtreme/MySprite.dynamicinfo" ;#incdri "DynamicXtreme/MySprite.drawinfo"
Paths are relative to the ASM containing the directives. For a cluster sprite, extended sprite, or another type supported by PIXI, the whole resource folder can be arranged the same way inside the corresponding PIXI folder.
You can also use Dynamic Xtreme's traditional folders. Put *.dynamicinfo in DynamicInfos, *.drawinfo in DrawInfos, and *.bin graphics in DynamicResources. Dynamic Xtreme discovers files stored this way directly, so the sprite does not need ;#incdyn or ;#incdri directives.
The first option makes it easier to distribute the sprite with all its resources in one folder; the second retains Dynamic Xtreme's traditional organization. For each format's structure and rules, see Dynamic Xtreme files and formats.
During installation, Dynamic Xtreme generates the defines used by the sprite code and saves them in DXDefines.asm. This table summarizes the names used in the guide:
| Example define | Source | Purpose |
|---|---|---|
!RenderBoxXDistanceOutOfScreenMySprite
|
Horizontal bounds of the poses in MySprite.drawinfo.
|
Configure the horizontal RenderBox distance in INIT. |
!RenderBoxYDistanceOutOfScreenMySprite
|
Vertical bounds of the poses in MySprite.drawinfo.
|
Configure the vertical RenderBox distance in INIT. |
!MySpritePaletteTableOffset
|
The Palettes list in MySprite.dynamicinfo.
|
Tell palette assignment which table to use. |
!DynamicPoseIDMySprite000
|
The first pose in MySprite.bin, declared under PosesGraphics in DynamicInfo.
|
Request that pose's graphics through !Upload. The 000 suffix identifies the graphics file's first pose; 003 identifies its fourth.
|
!PoseIDMySprite_Frame0
|
The Frame0 pose declared in MySprite.drawinfo.
|
Select the pose drawn by !DXDraw.
|
!PaletteEffectIDMyEffect0
|
The first effect in MyEffect.paleffect.
|
Select the palette effect to apply. |
Names are normalized by removing separators and capitalizing each word's first letter: for example, my_sprite becomes MySprite. Always check the exact generated names in DXDefines.asm; the guide also repeats their origin beside each use.
Sprite structure
A dynamic sprite retains the usual PIXI sprite structure, but must prepare Dynamic Xtreme's variables and organize its main routine according to whether it is within drawing distance of the screen.
The structure used in this guide has these parts:
- INIT: initializes the sprite variables and Dynamic Xtreme state.
- Short routine: runs the logic that must continue even when the sprite is outside drawing distance.
- Long routine: handles work needed only within drawing distance, such as requesting and drawing the pose.
Include the sprite type defines
Before the sprite's code, include the defines file for its resource type:
NormalSpriteDefines.asmfor normal sprites.ClusterSpriteDefines.asmfor cluster sprites.ExtendedSpriteDefines.asmfor extended sprites.
For example, if a normal sprite's ASM is in a subfolder of sprites, the include can be written as:
incsrc "../NormalSpriteDefines.asm"
The path is always relative to the sprite ASM, so adjust it to the resource's folder structure. This file supplies table and routine aliases for the sprite type. Names such as !PoseIndex, !RenderBox, and !Upload therefore select the correct implementation for a normal, cluster, or extended sprite.
INIT
For normal sprites, INIT must set the RenderBox distances, assign initial values, and invalidate any previous state left in the same PIXI slot.
The following normal sprite example starts at pose 0 and obtains its version and initial flip from the PIXI configuration:
print "INIT ",pc
LDA.b #!RenderBoxXDistanceOutOfScreenMySprite
STA !RenderXDistanceOutOfScreen,x
LDA.b #!RenderBoxYDistanceOutOfScreenMySprite
STA !RenderYDistanceOutOfScreen,x
LDA !ExtraBits,x
AND #$04
LSR
LSR
STA !GlobalFlip,x
LDA !ExtraByte1,x
AND #$0F
STA !Version,x
LDA #$00
STA !PoseIndex,x
STA !LocalFlip,x
STA !LastFlip,x
LDA #$FF
STA !Palette,x
STA !LastPoseIndex,x
STA !LastPoseHashIndex,x
STA !LastVersion,x
RTL
Each variable in the example has a specific role:
!RenderXDistanceOutOfScreenand!RenderYDistanceOutOfScreenstore the distances used by!RenderBoxto decide whether the sprite is still close enough to the screen to draw.!RenderBoxXDistanceOutOfScreenMySpriteand!RenderBoxYDistanceOutOfScreenMySpriteare generated from DrawInfo and available inDXDefines.asm. The resource name is normalized by removing separators such as spaces and underscores and capitalizing each word's first letter. For example,my_spriteandmy spritebecomeMySprite.!PoseIndexselects the desired dynamic pose within DynamicInfo. Zero selects the resource's first pose. An animation routine can assign another value before requesting graphics.!Versionidentifies the sprite's current variant. A version can change its palette, graphics, or behavior. Normal sprites commonly take it from the low four bits of!ExtraByte1,x, allowing up to 16 versions to be selected in Lunar Magic.!GlobalFlipis the sprite's overall flip. Normal sprites can obtain it from the extra bit: 0 keeps the original orientation and 1 applies the initial flip.!LocalFlipadds a flip for the current pose. Dynamic Xtreme combines both and stores the effective drawing flip in!LastFlip.!Palettestores the currently assigned palette. It starts at$FFbecause no valid palette has been assigned yet.!LastPoseIndexstores the last pose Dynamic Xtreme successfully obtained, and!LastPoseHashIndexstores its VRAM cache slot. Both start at$FFbecause no pose is available yet.!LastVersionstores the version previously processed by palette management. Starting it at$FFensures the first version is processed even when!Versionstarts at 0.
If a sprite uses several DrawInfo files, the distances must cover all its graphics. Check each DrawInfo's generated values in DXDefines.asm and use the largest horizontal and vertical distances. For example, if the head and body use separate DrawInfo files, using only the head's distances may stop drawing the sprite while part of its body is still on screen.
Values marked $FF represent invalid states. Besides indicating that no palette or pose is available yet, they prevent reuse of data left by a sprite that previously occupied the same PIXI slot.
Initializing cluster and extended sprites
Cluster and extended sprites have no dedicated INIT routine. To initialize them once, reserve one of their tables, such as !ClusterMiscTable1 or !ExtendedMiscTable1, and give it a name such as !Started:
!Started = !ClusterMiscTable1
When creating the sprite, set !Started to 0 using the new cluster or extended sprite's index. The slot may have been used before and could still hold another sprite's value.
At the start of SpriteCode, before !RenderBox, check this variable:
SpriteCode:
LDA !Started,x
BNE +
JSR Start
RTS
+
!RenderBox
; Rest of the sprite code
Start contains the initialization normally done in a normal sprite's INIT. At the end, it sets !Started to 1 so it does not run again on later frames:
Start:
; Initialize the sprite here
LDA #$01
STA !Started,x
RTS
Because Start is called with JSR, it must end with RTS. The code creating the cluster or extended sprite must also prepare inherited data, such as !PaletteOption, !PaletteAssignmentOption, or !GlobalFlip.
PaletteOption
!PaletteOption controls the requested palette and whether it receives the global palette effect. Its format is:
EPPP----
Edetermines whether the palette receives the global effect: 0 excludes it and 1 enables it.PPPrepresents a palette from 8 to F:000selects palette 8 and111selects palette F.- The low four bits are not part of
PaletteOption.
With automatic assignment, PPP = 000 searches all eight palettes for a free slot; a nonzero 0XX value limits the search to palettes 8–B, and 1XX limits it to C–F. With manual assignment, PPP directly selects the palette.
For normal sprites, !PaletteOption directly uses !ExtraByte1,x. The remaining four bits commonly select the sprite version, giving the whole byte this format:
EPPPVVVV
EPPPcontains the palette options described above.VVVVselects one of 16 sprite versions, from 0 to 15.
!PaletteOption directly uses the high four bits, while INIT copies the low four bits to !Version. You do not need to copy PaletteOption into another table.
PaletteAssignmentOption
!PaletteAssignmentOption determines how the sprite's palette is assigned:
--AA----
AA = 00: automatic assignment. Dynamic Xtreme finds a slot allowed byPaletteOptionand loads the palette dynamically.AA = 01: manual assignment.PaletteOptionspecifies the exact slot, and Dynamic Xtreme loads the palette dynamically.AA = 10: manual assignment without upload. The sprite uses the palette already in the level; Dynamic Xtreme does not load a new one.
For normal sprites, !PaletteAssignmentOption directly uses !ExtraProp2,x. Set the mode through bits 5 and 4 of Extra Property Byte 2; it does not need to be copied into another table in INIT.
For cluster and extended sprites, set !PaletteOption and !PaletteAssignmentOption manually when they are created. Normally, copy the creating sprite's assignment mode. When copying PaletteOption, clear the low four bits because they contain a normal sprite's version, not palette options.
If the spawned sprite uses the same palette as its parent, copy its PaletteOption after clearing the low four bits. If it uses a different palette and the parent uses automatic assignment, keep the same search range. For manual assignment, with or without upload, add $10 to the masked value to select the next palette.
In the last case, the parent cannot use palette F because the spawned sprite needs the next palette. Configure the parent with a palette from 8 to E.
Besides these variables, INIT can initialize any tables needed for the sprite's behavior.
SpriteCode
See RenderBox in the API for its inputs and result.
SpriteCode starts with !RenderBox. It returns carry set when the sprite is within drawing distance and clear when it is outside. This selects the short or long routine.
A basic structure is:
SpriteCode:
!RenderBox
BCS .LongRoutine
.ShortRoutine
LDA #$00
%SubOffScreen()
; Put your sprite's logic here
RTS
.LongRoutine
JSR PaletteManagement
BCC ..Return
JSR PoseManagement
BCC ..Return
JSR Draw
; Put your sprite's logic here
..Return
RTS
Short routine
The short routine runs when the sprite is outside the area checked by !RenderBox. Run only the core logic needed to maintain its behavior, without requesting dynamic graphics, processing its palette, or drawing it. The exact contents depend on the sprite and may include movement, state, timers, interactions, or animation.
For normal sprites, calling %SubOffScreen() here is recommended. It takes a value from 0 to 7 in A to select the offscreen removal setting; the example uses the usual value, 0. This lets PIXI remove the sprite when it gets far enough offscreen, without running this check in the more expensive drawing path.
Long routine
The long routine runs when the sprite is within drawing distance. It handles the complete process: palette management, requesting the dynamic pose or updating its last-use record if it is already in VRAM to protect it from replacement, drawing the sprite, and finally running the same core logic as the short routine.
There is no need to call %SubOffScreen() again in the long routine. !RenderBox has already determined that the sprite is within drawing distance, so the check would be redundant.
PaletteManagement and PoseManagement return carry set when a usable resource exists. If either returns carry clear, the example branches to ..Return and ends the routine. The sprite is not drawn and the subsequent logic is not run during that frame.
The following sections develop the palette, pose, and drawing routines. Their names are just one way to organize the code and can be changed to suit the sprite.
Palette management
Load a dynamic palette
See PaletteAssignment in the API for details.
To use palette assignment in a normal sprite, configure at least one Extra Byte in its *.cfg or *.json:
- Extra Byte 1: supplies
!PaletteOption; in this guide's example, its low four bits select!Version. - Extra Property Byte 2: sets
!PaletteAssignmentOptionthrough bits 5 and 4:00for automatic assignment,01for manual assignment, and10for manual assignment without upload. These bits correspond to$00,$10, and$20; the remaining bits can serve other sprite options.
Load a dynamic palette through !PaletteAssignment. Simply load the resource's generated palette table offset into 16-bit A:
PaletteManagement:
REP #$20
LDA #!MySpritePaletteTableOffset
!PaletteAssignment
RTS
!MySpritePaletteTableOffset is generated from the palettes declared in DynamicInfo and appears in DXDefines.asm. The DynamicInfo name is normalized by removing spaces, underscores, and other separators and capitalizing each word's first letter. For example, my_sprite.dynamicinfo produces MySprite.
!PaletteAssignment uses !Version to select a table entry: version 0 uses the first palette, version 1 the second, and so on. It also uses !PaletteOption and !PaletteAssignmentOption to decide where and how to assign it.
The routine returns carry set when a usable palette exists and clear when one could not yet be obtained. SpriteCode therefore checks carry before loading poses and drawing. Call !PaletteAssignment every frame of the long routine to keep the assignment while the sprite uses it.
Select a palette without changing the sprite version
A sprite's palette can change without changing its version or behavior. For example, it can switch between its normal palette and a second palette during an animation or temporary state. Keeping both palettes as separate resources also lets other sprites reuse the second one.
Because !PaletteAssignment selects its entry using !Version, temporarily save the sprite's logical version, set the desired palette index, and restore the original version afterward:
PaletteManagement:
LDA !Version,x
PHA
LDA #$02
STA !Version,x
REP #$20
LDA #!MySpritePaletteTableOffset
!PaletteAssignment
PLA
STA !Version,x
RTS
Here, !PaletteAssignment uses table entry 2, but the sprite's behavior keeps its original version. This separates palette selection from the character's logical variant. PLA and STA do not change carry, so PaletteManagement still returns the correct !PaletteAssignment result.
!MySpritePaletteTableOffset is generated from DynamicInfo and stored in DXDefines.asm. The same name normalization applies: remove separators and capitalize each word's first letter, so my_sprite becomes MySprite.
Concatenate a local effect with the global effect
See the API entries for PaletteAssignmentNoUpload, ConcatenateRGBPaletteEffectToGlobal, and ConcatenateHSLPaletteEffectToGlobal.
When a palette needs its own effect combined with the global palette effect, use !PaletteAssignmentNoUpload followed by !ConcatenateRGBPaletteEffectToGlobal or !ConcatenateHSLPaletteEffectToGlobal:
PaletteManagement:
REP #$20
LDA #!MySpritePaletteTableOffset
!PaletteAssignmentNoUpload
BCC .Return
PHY
LDA !Palette,x
TAX
LDA $14
AND #$06
TAY
REP #$20
LDA PaletteEffects,y
PLY
!ConcatenateRGBPaletteEffectToGlobal
.Return
RTS
PaletteEffects:
dw !PaletteEffectIDMyEffect0
dw !PaletteEffectIDMyEffect1
dw !PaletteEffectIDMyEffect2
dw !PaletteEffectIDMyEffect3
!PaletteAssignmentNoUpload assigns the palette and prepares its base address without performing the final upload. It also leaves information in Y that tells the concatenation routine whether the effect must be rebuilt.
This example uses $14, Super Mario World's main frame counter, to select one of four effects. Since Y is temporarily needed to read PaletteEffects, it is first saved with PHY and restored with PLY. Pass that restored value unchanged to the concatenation routine.
Before concatenation, X must contain !Palette, which already represents the index required by the routine. A must contain the 16-bit local effect ID. Here, Y indexes a table to load the ID, but the ID is ultimately passed in A.
!MySpritePaletteTableOffset comes from DynamicInfo and is stored in DXDefines.asm. The !PaletteEffectIDMyEffectX defines are generated from a *.paleffect during installation and stored in the same file. In both cases, names are normalized by removing spaces, underscores, and other separators and capitalizing each word's first letter. For example, my_effect.paleffect produces MyEffect; X is the effect index within the collection.
Use !ConcatenateRGBPaletteEffectToGlobal for RGB effects such as PaletteTransitionRGB, and !ConcatenateHSLPaletteEffectToGlobal for HSL effects such as MixH. To combine local and global effects, enable bit E of PaletteOption. The routine handles any conversions needed when the two effects use different color spaces. See Effect types for each effect's color space.
Concatenation returns carry set when a usable result exists, including a previous result kept as a fallback, and clear when that palette has no valid result yet. Preserve this carry so SpriteCode can decide whether to continue drawing.
Dynamic pose management
Request dynamic poses with !Upload or !UploadWithoutPoseOffset. These routines search the VRAM cache and request a load if the pose is not available. Call them every frame of the long routine to keep the sprite's pose in use.
Upload
See Upload in the API for details.
!Upload takes the resource's base dynamic ID in 16-bit A and adds !PoseIndex:
PoseManagement:
REP #$20
LDA #!DynamicPoseIDMySprite000
!Upload
RTS
Here, !PoseIndex = 0 requests the first pose, 1 the next, and so on. This lets the animation routine change only !PoseIndex while PoseManagement keeps the same base ID.
!DynamicPoseIDMySprite000 is generated from DynamicInfo during installation and appears in DXDefines.asm. Names are normalized by removing spaces, underscores, and other separators and capitalizing each word's first letter. For example, my_sprite.dynamicinfo produces MySprite.
!Upload returns carry set when a usable pose exists. This can be the pose requested this frame or the sprite's last valid pose, used as a fallback while the new one cannot yet be loaded. Carry is clear only when the sprite has no usable pose.
When a request succeeds, !LastPoseIndex stores the available pose and !LastPoseHashIndex its VRAM cache entry. Drawing must therefore use the last available pose's state rather than assume !PoseIndex has been loaded.
UploadWithoutPoseOffset
See UploadWithoutPoseOffset in the API for details.
!UploadWithoutPoseOffset also takes a dynamic ID in 16-bit A but uses it directly, without adding !PoseIndex:
PoseManagement:
REP #$20
LDA #!DynamicPoseIDMySprite003
!UploadWithoutPoseOffset
RTS
Use this variant when your code has already selected the exact dynamic ID, for example through a table or condition. Carry has the same meaning as in !Upload: set when a usable pose exists, clear when none is available.
!DynamicPoseIDMySprite003 is also generated from DynamicInfo and stored in DXDefines.asm. The same CamelCase-style normalization removes separators and capitalizes each word's first letter.
Currently, the normal and cluster sprite defines publish !UploadWithoutPoseOffset. Extended sprites must use !Upload with !PoseIndex until their defines file publishes this alias.
!DynamicPoseID... defines belong to dynamic loading. Do not confuse them with DrawInfo's !PoseID... defines, which are used to draw the pose.
Drawing poses on screen
Once PoseManagement obtains a usable pose, draw it with !DXDraw or !DXDrawWithOffset. Both use the sprite position, flip, palette, and dynamic pose state kept by Dynamic Xtreme.
Before either call, prepare:
- The drawing pose ID in 16-bit A.
- Additional properties in
!PropParam, using theYXCC----format. - The highest allowed tile priority in
!MaxTilePriorityParam.
!MaxTilePriorityParam accepts:
$00: maximum priority.$01: high priority.$02: the sprite type's own priority.$03: lowest priority.
DXDraw
See DXDraw in the API for details.
!DXDraw draws the pose using the sprite's normal position as its origin. A basic drawing routine is:
Draw:
LDA #$20
STA !PropParam
LDA #$02
STA !MaxTilePriorityParam
LDA !LastPoseIndex,x
REP #$21
AND #$00FF
ADC #!PoseIDMySprite_Frame0
!DXDraw
RTS
!LastPoseIndex is added to the first drawing ID to select the pose actually available. Use !LastPoseIndex, not !PoseIndex: while a new pose is unavailable, Dynamic Xtreme may keep the previous pose as a fallback.
This example assumes DrawInfo poses follow the same order as DynamicInfo poses. If they differ, convert !LastPoseIndex to the matching drawing ID, for example using a table.
!PoseIDMySprite_Frame0 is generated from DrawInfo during installation and appears in DXDefines.asm. Generated names remove spaces, underscores, and other separators and capitalize each word's first letter. For example, my_sprite produces MySprite.
!PoseID... identifies a drawing pose declared in DrawInfo. Do not confuse it with DynamicInfo's !DynamicPoseID..., used by !Upload to request dynamic graphics.
!DXDraw combines !PropParam with !LastFlip, the assigned palette, and the pose's own properties. It also uses !LastPoseHashIndex to locate graphics in the VRAM cache. If this is $FF, nothing is drawn.
DXDrawWithOffset
See DXDrawWithOffset in the API for details.
!DXDrawWithOffset works the same way but shifts the drawing relative to the sprite. Supply signed 8-bit offsets in !XOffsetParam and !YOffsetParam:
Draw:
LDA #$08
STA !XOffsetParam
LDA #$F8
STA !YOffsetParam
LDA #$20
STA !PropParam
LDA #$02
STA !MaxTilePriorityParam
LDA !LastPoseIndex,x
REP #$21
AND #$00FF
ADC #!PoseIDMySprite_Frame0
!DXDrawWithOffset
RTS
This example shifts the drawing 8 pixels right and 8 pixels up. Positive X moves right and negative X left; positive Y moves down and negative Y up.
The offset changes only where the pose is drawn, not the sprite's actual position, hitbox, or interactions. It is useful for aligning graphics, adding visual movement, or drawing several parts of an entity from one logical position.
As above, !PoseIDMySprite_Frame0 is generated from DrawInfo and stored in DXDefines.asm. Dynamic Xtreme's name normalization removes separators such as spaces and underscores and capitalizes each word's first letter.
DrawStatic
See DrawStatic in the API for details.
Dynamic Xtreme can also draw static poses. Declare them with .IsStatic in DrawInfo and draw them with %DrawStatic(). Their graphics are already in VRAM, so they do not need !Upload, !LastPoseIndex, or !LastPoseHashIndex.
When a static pose uses the default tile (0) for all components, drawing takes the tile number from !Tile, which represents scratch RAM $05. This lets the same pose, such as a one-tile pose, draw different graphics. Prepare this value before calling %DrawStatic():
LDA #$80 ; Tile number to draw
STA !Tile ; Scratch RAM $05
Prepare this value for every drawing call that needs it. Setting it only in INIT is insufficient because other routines share the scratch RAM.
For dynamic poses, !DXDraw and !DXDrawWithOffset obtain the tile from the graphics' VRAM cache location, so !Tile does not need to be set manually. If a static DrawInfo declares nondefault tiles, drawing uses those values directly.
This example draws a static pose at a normal sprite's position, without flipping and using palette A:
Draw:
LDA #$20
STA !PropParam
LDA #$02
STA !MaxTilePriorityParam
REP #$21
LDA #!PoseIDMyStaticSprite_Frame0
%DrawStatic(0, 1, #$00, #$04, "!SpriteXLow,x", "!SpriteXHigh,x", "!SpriteYLow,x", "!SpriteYHigh,x")
The first parameter selects additional offsets: 0 draws at the supplied position; 1 also applies !XOffsetParam and !YOffsetParam. The remaining parameters provide the drawing context, flip, palette, and 16-bit X/Y positions. Quote indexed positions so the comma in ,x is not treated as a macro argument separator.
%DrawStatic() ends the routine with RTS, so this example does not add another RTS after the macro.
Here, #$00 disables flipping and #$04 selects palette A. These arguments can also use tables or variables when the sprite needs to change orientation or palette.
!PoseIDMyStaticSprite_Frame0 is generated from DrawInfo during installation and appears in DXDefines.asm. Its name uses PascalCase: remove spaces, underscores, hyphens, and other separators and capitalize each word's first letter.
For more on declaring static poses, see Dynamic Xtreme files and formats.
Install and test the sprite
Whenever a resource processed by Dynamic Xtreme changes, reinstall Dynamic Xtreme first and then reinsert the sprites with PIXI. This includes *.dynamicinfo, *.drawinfo, *.bin graphics, *.paleffect effects, and any other dynamic resource used by the sprite.
Order matters: Dynamic Xtreme must process the resources and regenerate their data and defines before PIXI assembles the sprite with the updated information.
If you add, remove, or modify ;#incdyn, ;#incdri, or ;#incpale resource includes in the ASM, reinstall Dynamic Xtreme first and then PIXI.
If you only modify the sprite's *.asm code without changing those includes, you do not need to reinstall Dynamic Xtreme. Run PIXI again.
For the complete resource installation, configuration, and reinstallation process, see the Dynamic Xtreme Quick Start guide.
After each installation, test the sprite in-game and check its poses, palettes, flipping, and offsets throughout its states and animations.
Common mistakes
Using an unnormalized name in a generated define
Names inside Dynamic Xtreme's generated defines must follow the tool's normalization: remove spaces, underscores, and hyphens and capitalize each word's first letter, using PascalCase.
For example, a pose named my_sprite-pose uses MySpritePose in a define such as:
!DynamicPoseIDMySpritePose
Using the original separators, different capitalization, or confusing !DynamicPoseID... with !PoseID... prevents Asar from finding the symbol. When unsure, check the exact generated name in DXDefines.asm.
Incorrectly initializing sprite variables
Initialize Dynamic Xtreme's variables to valid values whenever a sprite is created. With !PaletteAssignment, !Palette, !LastPoseIndex, !LastPoseHashIndex, and !LastVersion must start at $FF; !PoseIndex, !LocalFlip, and !LastFlip normally start at 0. Also configure the !RenderBox distances.
Incorrect initialization may reuse a palette, pose, or data left by another sprite in the same slot. Cluster and extended sprites must initialize through the simulated startup routine explained above.
Not reserving an Extra Byte for the palette
A normal sprite that loads palettes needs at least one Extra Byte in its *.cfg or *.json. Dynamic Xtreme uses Extra Byte 1 as !PaletteOption and commonly uses its low four bits for !Version.
Without any declared Extra Bytes, !ExtraByte1,x will not contain the configuration needed to select the sprite's palette and version correctly.
Not restoring a fixed palette in the short routine
A sprite can use a fixed palette already loaded in the level instead of calling !PaletteAssignment. Even then, assign the desired drawing palette manually to !Palette,x.
Values increase by two and select sprite palettes 8–F:
$00: palette 8.$02: palette 9.$04: palette A.$06: palette B.$08: palette C.$0A: palette D.$0C: palette E.$0E: palette F.
Assign the value in INIT and in the short routine. When !RenderBox selects the short routine, it invalidates !Palette,x, so restore the fixed palette there:
; Inside INIT
LDA #$04 ; Palette A
STA !Palette,x
; Inside SpriteCode
.ShortRoutine
LDA #$04 ; Palette A
STA !Palette,x
LDA #$00
%SubOffScreen()
; Put your sprite's logic here
RTS
This manual assignment is only for sprites using fixed palettes without !PaletteAssignment. Sprites with dynamic palette loading must retain the assignment flow explained in the palette management section.