The wiki has been upgraded to MediaWiki 1.43. The wiki's theme is still being adapted to work with the new version, and may appear broken for the time being. --RanAS

Dynamic Xtreme sprite creation guide

From SnesLab
Jump to: navigation, search
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.asm for normal sprites.
  • ClusterSpriteDefines.asm for cluster sprites.
  • ExtendedSpriteDefines.asm for 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:

  • !RenderXDistanceOutOfScreen and !RenderYDistanceOutOfScreen store the distances used by !RenderBox to decide whether the sprite is still close enough to the screen to draw. !RenderBoxXDistanceOutOfScreenMySprite and !RenderBoxYDistanceOutOfScreenMySprite are generated from DrawInfo and available in DXDefines.asm. The resource name is normalized by removing separators such as spaces and underscores and capitalizing each word's first letter. For example, my_sprite and my sprite become MySprite.
  • !PoseIndex selects the desired dynamic pose within DynamicInfo. Zero selects the resource's first pose. An animation routine can assign another value before requesting graphics.
  • !Version identifies 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.
  • !GlobalFlip is 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. !LocalFlip adds a flip for the current pose. Dynamic Xtreme combines both and stores the effective drawing flip in !LastFlip.
  • !Palette stores the currently assigned palette. It starts at $FF because no valid palette has been assigned yet.
  • !LastPoseIndex stores the last pose Dynamic Xtreme successfully obtained, and !LastPoseHashIndex stores its VRAM cache slot. Both start at $FF because no pose is available yet.
  • !LastVersion stores the version previously processed by palette management. Starting it at $FF ensures the first version is processed even when !Version starts 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----
  • E determines whether the palette receives the global effect: 0 excludes it and 1 enables it.
  • PPP represents a palette from 8 to F: 000 selects palette 8 and 111 selects 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
  • EPPP contains the palette options described above.
  • VVVV selects 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 by PaletteOption and loads the palette dynamically.
  • AA = 01: manual assignment. PaletteOption specifies 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 !PaletteAssignmentOption through bits 5 and 4: 00 for automatic assignment, 01 for manual assignment, and 10 for 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 the YXCC---- 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.