gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Beginners > Sprites garbled, every other line displayed.

#127793 - Huitzilopoctli - Sat May 05, 2007 1:43 am

Hello. I've been experimenting with trying to display sprites, and not enjoying it. The most frustrating part being that I'm pretty sure that I know what the problem is.

[Images not permitted - Click here to view it]

As you can see, every other line of the sprite, (which should be a lovely smiley face) seems transparent.

In fact, what's happening is that the first pixel of the image is displayed in the correct place. However, the next pixel is placed where the third should be. The third is where the fifth should be, and so on. It has occured to me that it might have been a problem with the size of the types between the sprite's array and the SpriteData array. As in, it's putting a single pixel's data into the space reserved for two. For instance, if I define the tiles in SpriteData as containing unsigned ints, instead of unsigned shorts, then the problem gets twice as worse. However, filling it with chars obviously doesn't fix the problem :p

Code:
////////////////////////////////////////////////////////////
// A lot of defines
////////////////////////////////////////////////////////////

// the u16 type defined
typedef unsigned short u16;

//macro to change the video mode
#define SetMode(mode) REG_DISPCNT = (mode)

//define some video addresses
#define REG_DISPCNT *(volatile unsigned short*)0x4000000
#define BGPaletteMem ((unsigned short*)0x5000000)
#define REG_VCOUNT *(volatile unsigned short*)0x4000006
#define REG_DISPSTAT *(volatile unsigned short *)0x4000004

//define object attribute memory state address
#define SpriteMem ((unsigned short*)0x7000000)

//define object attribute memory image address
#define SpriteData ((unsigned short*)0x6010000)

//define object attribute memory palette address
#define SpritePal ((unsigned short*)0x5000200)

//misc sprite constants
#define OBJ_MAP_2D 0x0
#define OBJ_MAP_1D 0x40
#define OBJ_ENABLE 0x1000

//attribute0 stuff
#define ROTATION_FLAG 0x100
#define SIZE_DOUBLE 0x200
#define MODE_NORMAL 0x0
#define MODE_TRANSPARENT 0x400
#define MODE_WINDOWED 0x800
#define MOSAIC 0x1000
#define COLOR_16 0x0000
#define COLOR_256 0x2000
#define SQUARE 0x0
#define TALL 0x4000
#define WIDE 0x8000

//attribute1 stuff
#define ROTDATA(n) ((n) << 9)
#define HORIZONTAL_FLIP 0x1000
#define VERTICAL_FLIP 0x2000
#define SIZE_8 0x0
#define SIZE_16 0x4000
#define SIZE_32 0x8000
#define SIZE_64 0xC000

//attribute2 stuff
#define PRIORITY(n) ((n) << 10)
#define PALETTE(n) ((n) << 12)

#define RGB(r, g, b) (unsigned short)(r + (g << 5) + (b << 10))
#include "ship.hpp"

//sprite structs
typedef struct tagSprite
{
unsigned short attribute0;
unsigned short attribute1;
unsigned short attribute2;
unsigned short attribute3;
}Sprite,*pSprite;

//create an array of 128 sprites equal to OAM
Sprite sprites[128];

//function prototypes
void WaitForVsync(void);
void UpdateSpriteMemory(void);


Code:
////////////////////////////////////////////////////////////
// The general body of code
////////////////////////////////////////////////////////////
int main(void) {
    int n;

    //set the video mode--mode 2 with sprites
    SetMode(2 | OBJ_ENABLE | OBJ_MAP_1D);

    //move all sprites offscreen to hide them
    for(n = 0; n < 128; n++)
    {
        sprites[n].attribute0 = 160;
        sprites[n].attribute1 = 240;
    }

    //set the sprite palette
    for(n = 0; n < 256; n++)
        SpritePal[n] = shipPalette[n];

    //copy the sprite image into memory
    for(n = 0; n < 64; n++)
        SpriteData[n] = shipData[n];

    //setup the first sprite
    sprites[0].attribute0 = COLOR_256 | 8;
    sprites[0].attribute1 = SIZE_8 | 8;

    while(1)
    {
        //wait for vertical retrace
        WaitForVsync();

        //display the sprite
        UpdateSpriteMemory();
    }
}
////////////////////////////////////////////////////////////
// Function: WaitForVsync
// Waits for the vertical retrace
////////////////////////////////////////////////////////////
void WaitForVsync(void)
{
while((REG_DISPSTAT & 1));
}
////////////////////////////////////////////////////////////
// Function: UpdateSpriteMemory
// Copies the sprite array into OAM memory
////////////////////////////////////////////////////////////
void UpdateSpriteMemory(void)
{
int n;
unsigned short* temp;
temp = (unsigned short*)sprites;
for(n = 0; n < 128*4; n++)
SpriteMem[n] = temp[n];
}



And the graphics file:
Code:
#define ship_WIDTH 8
#define ship_HEIGHT 8
const u16 shipData[] = {
   0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
   0xFF, 0x00, 0xFB, 0xFB, 0xFB, 0xFB, 0x00, 0xFF,
   0x00, 0xFB, 0x00, 0xFB, 0xFB, 0x00, 0xFB, 0x00,
   0x00, 0xFB, 0x00, 0xFB, 0xFB, 0x00, 0xFB, 0x00,
   0x00, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0x00,
   0x00, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0x00,
   0xFF, 0x00, 0xFB, 0xFB, 0xFB, 0xFB, 0x00, 0xFF,
   0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF
};

const u16 shipPalette[] = {
   0x00000, 0x00010, 0x00200, 0x00210, 0x04000, 0x04010, 0x04200, 0x06318,
   0x0E378, 0x07B34, 0x00088, 0x0008C, 0x00090, 0x00094, 0x00098, 0x0009C,
   0x00100, 0x00104, 0x00108, 0x0010C, 0x00110, 0x00114, 0x00118, 0x0011C,
   0x00180, 0x00184, 0x00188, 0x0018C, 0x00190, 0x00194, 0x00198, 0x0019C,
   0x00200, 0x00204, 0x00208, 0x0020C, 0x00210, 0x00214, 0x00218, 0x0021C,
   0x00280, 0x00284, 0x00288, 0x0028C, 0x00290, 0x00294, 0x00298, 0x0029C,
   0x00300, 0x00304, 0x00308, 0x0030C, 0x00310, 0x00314, 0x00318, 0x0031C,
   0x00380, 0x00384, 0x00388, 0x0038C, 0x00390, 0x00394, 0x00398, 0x0039C,
   0x02000, 0x02004, 0x02008, 0x0200C, 0x02010, 0x02014, 0x02018, 0x0201C,
   0x02080, 0x02084, 0x02088, 0x0208C, 0x02090, 0x02094, 0x02098, 0x0209C,
   0x02100, 0x02104, 0x02108, 0x0210C, 0x02110, 0x02114, 0x02118, 0x0211C,
   0x02180, 0x02184, 0x02188, 0x0218C, 0x02190, 0x02194, 0x02198, 0x0219C,
   0x02200, 0x02204, 0x02208, 0x0220C, 0x02210, 0x02214, 0x02218, 0x0221C,
   0x02280, 0x02284, 0x02288, 0x0228C, 0x02290, 0x02294, 0x02298, 0x0229C,
   0x02300, 0x02304, 0x02308, 0x0230C, 0x02310, 0x02314, 0x02318, 0x0231C,
   0x02380, 0x02384, 0x02388, 0x0238C, 0x02390, 0x02394, 0x02398, 0x0239C,
   0x04000, 0x04004, 0x04008, 0x0400C, 0x04010, 0x04014, 0x04018, 0x0401C,
   0x04080, 0x04084, 0x04088, 0x0408C, 0x04090, 0x04094, 0x04098, 0x0409C,
   0x04100, 0x04104, 0x04108, 0x0410C, 0x04110, 0x04114, 0x04118, 0x0411C,
   0x04180, 0x04184, 0x04188, 0x0418C, 0x04190, 0x04194, 0x04198, 0x0419C,
   0x04200, 0x04204, 0x04208, 0x0420C, 0x04210, 0x04214, 0x04218, 0x0421C,
   0x04280, 0x04284, 0x04288, 0x0428C, 0x04290, 0x04294, 0x04298, 0x0429C,
   0x04300, 0x04304, 0x04308, 0x0430C, 0x04310, 0x04314, 0x04318, 0x0431C,
   0x04380, 0x04384, 0x04388, 0x0438C, 0x04390, 0x04394, 0x04398, 0x0439C,
   0x06000, 0x06004, 0x06008, 0x0600C, 0x06010, 0x06014, 0x06018, 0x0601C,
   0x06080, 0x06084, 0x06088, 0x0608C, 0x06090, 0x06094, 0x06098, 0x0609C,
   0x06100, 0x06104, 0x06108, 0x0610C, 0x06110, 0x06114, 0x06118, 0x0611C,
   0x06180, 0x06184, 0x06188, 0x0618C, 0x06190, 0x06194, 0x06198, 0x0619C,
   0x06200, 0x06204, 0x06208, 0x0620C, 0x06210, 0x06214, 0x06218, 0x0621C,
   0x06280, 0x06284, 0x06288, 0x0628C, 0x06290, 0x06294, 0x06298, 0x0629C,
   0x06300, 0x06304, 0x06308, 0x0630C, 0x06310, 0x06314, 0x07BFF, 0x05294,
   0x04210, 0x0001F, 0x083E0, 0x083FF, 0x07C00, 0x07C1F, 0x0FFE0, 0x0FFFF
};

I've looked through a lot of tutorials online and I've gathered that the error is possibly due to some kind of confusion between 256 and 16 colour sprites. The frustrating thing is, I still can't figure out how to fix it! I'm a fairly proficient programmer, (learned on RPG Maker XP originally ^^), but I'm struggling a little to get used to all the hardware involved here. Any help is appreciated.
_________________
The fifth is my least favourite interval!


Last edited by Huitzilopoctli on Sat May 05, 2007 2:32 am; edited 1 time in total

#127799 - tepples - Sat May 05, 2007 2:12 am

Did you draw the sprite as a bitmap (.bmp, .pcx, .gif, .png) and convert it to GBA format, or did you hand-code the sprite within the program? What is your program's source code?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#127801 - Huitzilopoctli - Sat May 05, 2007 2:35 am

Hmm I've - feeling quite sily for having forgotain to now - edited in the code. I kicked off using Janathan Harbour's book.

The results are the same for arrays I've written and this one, which I converted with AGBconvert14. I'm pretty sure the file itself is correct.
_________________
The fifth is my least favourite interval!

#127804 - tepples - Sat May 05, 2007 4:00 am

Could you please paste your code and your sprite data into a post in this topic?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#127810 - keldon - Sat May 05, 2007 8:23 am

Huitzilopoctli wrote:
Janathan Harbour's book.

That might explain it.

#127819 - Cearn - Sat May 05, 2007 10:11 am

Huitzilopoctli wrote:
Hmm I've - feeling quite silly for having forgotten to now - edited in the code. I kicked off using Jonathan Harbour's book.

The results are the same for arrays I've written and this one, which I converted with AGBconvert14. I'm pretty sure the file itself is correct.

It's not.

The graphics data you have look like byte (u8) data, but the array itself are halfwords (u16). While the data may look the same on paper, they're actually quite different where it counts -- in memory. For example:

Code:
// Array definitions
const u8 data8[4]= { 1, 2, 3, 4 };
const u16 data16[4]= { 1, 2, 3, 4 };

// In memory:
data8  : 01 02 03 04
data16 : 01 00 02 00 03 00 04 00


Halfwords have two bytes, even though the number may only take up one. The halfword 0x01 is actually 0x0001, and because the GBA is little endian, this goes into memory as "01 00".

It's probably done because you can't write bytes to VRAM, but changing the array-type itself isn't going to fix that: you need to cast the array when copying, not the definition itself. This, however, can have its own set of problems, as discussed at tonc:data alignment. The rest of the section should be useful as well for dealing with data.

keldon wrote:
Huitzilopoctli wrote:
Jonathan Harbour's book.

That might explain it.

The problem wasn't there in this case, but there are plenty of other problems in the book. I've written down a number of annotations for someone so he could steer away from the bad stuff in the book, which I've been thinking about releasing, but it's not quite ready yet.

The summary of that: nearly every example project in the book is flawed on one or more points. There is no consistency in nomenclature anywhere, much of the routines are dreadfully inefficient and a good portion doesn't even work properly. Be very careful of what you use from the example code. Use tonc for the correct information. Use its demos and/or the libgba examples that come with devkitPro for your code base.

#127829 - Huitzilopoctli - Sat May 05, 2007 12:50 pm

Ahhh :p I'd considered that at one point, but my attempted solution, (bitshifting and oring to make a new array) didn't seem to work. Thanks!

Edit:
Now I realise that I had been doing the right thing to correct the image source, but the wrong tiles shifted *sigh*
_________________
The fifth is my least favourite interval!