#15944 - abilyk - Wed Feb 04, 2004 10:13 pm
I wanted to share the OAM entry sorting system I've designed. Perhaps some of you may find it useful, and if there are any holes in my logic, I'm sure someone will point it out.
First, let me explain the goals of this system. Inspired by Rafael Baptista's GBA Resource Management paper. I wanted specific control over sprite z-depth without a lot of bookkeeping. I wanted a sprite array in which each sprite entry will remain in the same position from creation until deletion.
Here is my Sprite struct and the global Sprite array:
Attributes 0-2 contain the same data you've come to expect from OAM entries. Obviously, these bytes are necessary to hold the sprite's display info.
Attribute 3, however, does not hold rotation data as you might expect. I have no use for sprite rotation, so I'm using these two bytes for the purposes of my sorting system. The first byte of Attribute 3 holds the sprite-priority (0-127) of the sprite. Not to be confused with the background-relative priority held in Attribute 2, the sprite-priority is an integer that specifies a sprite's priority in relation to other sprites that have the same background-relative priority. The system supports 128 levels of sprite priority, so the user can set a different priority for each sprite on the screen if desired. A sprite with a small sprite-priority value (such as 0) will be drawn above a sprite with a larger sprite-priority value (such as 1). If 2 sprites have the same sprite-priority value, there is no guarantee as to which will be drawn above the other.
The second byte of Attribute 3 holds the index (0-127) of the sprite's current position in the global sprite array. This index is necessary to maintain because while a sprite's position in the global sprite array will maintain constant, its position in OAM will vary. When a sprite changes position in OAM, the system must be able to refer back to its position in the global array, using this value, so it can update the position of the sprite's current index in OAM.
The OAM_index variable holds the index (0-127) of this sprite's current position in OAM.
The double-size variable in attribute 0 tells whether the sprite is active (has been added) or inactive (has been deleted or not yet added).
Now that the structure has been explained, let's go over how the system works. The user can add to or delete from the sprite array using functions such as this:
As an example, the user could use these functions to add/delete a number of sprites during a single frame. Once all sprites have been added to or deleted from the global array, the array must be sorted and the necessary data copied into OAM.
After calling this function, OAM should be fully sorted by priority. All sprites with background-relative priorities of 0 will come before sprites with background-relative priorities of 1, and so forth. Within each of the 4 groups of sprites with identical background-relative priorities, sprites will be sorted by sprite-priority. Sprites with small sprite-priority values will come before sprites with larger sprite-priorities.
So concludes my description of the sprite sorting system I've designed. I don't believe I left out any necessary details, but if I did, or there are any questions, comments, bug-fixes, etc., please let me know.
Thanks,
Andrew
First, let me explain the goals of this system. Inspired by Rafael Baptista's GBA Resource Management paper. I wanted specific control over sprite z-depth without a lot of bookkeeping. I wanted a sprite array in which each sprite entry will remain in the same position from creation until deletion.
Here is my Sprite struct and the global Sprite array:
Code: |
typedef Struct Sprite
{ u16 attribute0; u16 attribute1; u16 attribute2; u16 attribute3; u16 OAM_index; } Sprite sprite[128]; |
Attributes 0-2 contain the same data you've come to expect from OAM entries. Obviously, these bytes are necessary to hold the sprite's display info.
Attribute 3, however, does not hold rotation data as you might expect. I have no use for sprite rotation, so I'm using these two bytes for the purposes of my sorting system. The first byte of Attribute 3 holds the sprite-priority (0-127) of the sprite. Not to be confused with the background-relative priority held in Attribute 2, the sprite-priority is an integer that specifies a sprite's priority in relation to other sprites that have the same background-relative priority. The system supports 128 levels of sprite priority, so the user can set a different priority for each sprite on the screen if desired. A sprite with a small sprite-priority value (such as 0) will be drawn above a sprite with a larger sprite-priority value (such as 1). If 2 sprites have the same sprite-priority value, there is no guarantee as to which will be drawn above the other.
The second byte of Attribute 3 holds the index (0-127) of the sprite's current position in the global sprite array. This index is necessary to maintain because while a sprite's position in the global sprite array will maintain constant, its position in OAM will vary. When a sprite changes position in OAM, the system must be able to refer back to its position in the global array, using this value, so it can update the position of the sprite's current index in OAM.
The OAM_index variable holds the index (0-127) of this sprite's current position in OAM.
The double-size variable in attribute 0 tells whether the sprite is active (has been added) or inactive (has been deleted or not yet added).
Now that the structure has been explained, let's go over how the system works. The user can add to or delete from the sprite array using functions such as this:
Code: |
pseudocode:
// this function will activate the sprite and return the new sprite's index in the global sprite array u16 AddSprite(x_coord, y_coord, palette_type, ..., background_relative_priority, sprite_priority); // this function will deactivate the sprite void DeleteSprite(sprite_index); |
As an example, the user could use these functions to add/delete a number of sprites during a single frame. Once all sprites have been added to or deleted from the global array, the array must be sorted and the necessary data copied into OAM.
Code: |
pseudocode:
// In addition to the global sprite array and OAM, // the SortIntoOAM() function requires the use of // 4 arrays for sorting. These each need to be // 128 entry arrays with 8 bytes per entry, just like OAM typedef Struct OAM_Entry { u16 attribute0; u16 attribute1; u16 attribute2; u16 attribute3; } OAM_Entry bg_priority_0[128]; OAM_Entry bg_priority_1[128]; OAM_Entry bg_priority_2[128]; OAM_Entry bg_priority_3[128]; void SortIntoOAM() { for(each active sprite entry in global sprite array) { copy entry into the bg_priority array corresponding to the background-relative priority value; // for example, background_relative_priority = 0, entry is copied into bg_priority_0 array } Sort each bg_priority array by the sprite-priority values of each entry, in ascending order; Copy bg_priority_0 array into OAM starting at the beginning; Copy bg_priority_1 array into OAM starting immediately after where the previous copy ended; Copy bg_priority_2 array into OAM starting immediately after where the previous copy ended; Copy bg_priority_3 array into OAM starting immediately after where the previous copy ended; for(each active sprite entry in OAM) { use the entry's index of its position in the global sprite array to access the sprite's OAM_index variable; set OAM_index variable to the current OAM index; } } |
After calling this function, OAM should be fully sorted by priority. All sprites with background-relative priorities of 0 will come before sprites with background-relative priorities of 1, and so forth. Within each of the 4 groups of sprites with identical background-relative priorities, sprites will be sorted by sprite-priority. Sprites with small sprite-priority values will come before sprites with larger sprite-priorities.
So concludes my description of the sprite sorting system I've designed. I don't believe I left out any necessary details, but if I did, or there are any questions, comments, bug-fixes, etc., please let me know.
Thanks,
Andrew