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.

C/C++ > This has me stumped...

#7302 - Ninja - Sat Jun 14, 2003 9:51 am

I am sorry for keep coming in here with nothing but questions... but I am really stuck on why this doesn't work. Here is a simplified example of what I am trying to do:

Code:

u16** tempSprite = (u16**)SpriteInA2DArray;
//SpriteInA2DArray has several frames that are switched by changing the first number.
//example:
//const u16 SpriteInA2DArray[2][512] = {.....}

displaySprite(tempSprite[0]);

//The above code does NOT work.  It gives a whole bunch of nothing.
//The code below works as it should.

displaySprite(SpriteInA2DArray[0]);


Now I am just not seeing a difference in the two ways of doing this. I am forced to do it in the top way though, as I am passing the address of the array to an animation function, which flips the frames of the sprite at the proper times. It needs access to all the frames of the sprite at once, and I can't declare it as a global.

Can someone please explain to me why this doesn't work? And if possible, an example on how I can make it work?

#7310 - Cyberman - Sat Jun 14, 2003 6:32 pm

Code:

u16** tempSprite = (u16**)SpriteInA2DArray;


This is a pointer to a pointer being assigned to a 2d array of data.. oops. This is not a good way to start your day to say the least :)

Code:
 
//SpriteInA2DArray has several frames that are switched by changing the first number.
//example:
//const u16 SpriteInA2DArray[2][512] = {.....}

displaySprite(tempSprite[0]);

//The above code does NOT work.  It gives a whole bunch of nothing.
//The code below works as it should.

displaySprite(SpriteInA2DArray[0]);


The problem is you are not using pointers correctly.
you can 'make' it work by this code

Code:

typedef u16 * SpritePtr; // define a pointer to sprite data
SpritePtr * tempSprite;
tempSprite = (Sprite Ptr*)malloc(sizeof(SpritePtr) * 2); // allocate two pointers to sprites
tempSprite[0] = &SpriteInA2DArray[0];
tempSprite[1] = &SpriteInA2DArray[1];


That will work but it's combersome and not likely what you are really trying to do (woo?).
Code:

SpriteInA2DArray[0]

This should not be used I note, it works likely because you are passing an array and C and C++ passes arrays by passing a pointer ( in C++ you can pass them by reference).
you should use this instead so you get use to proper conventions:
Code:

displaySprite(&SpriteInA2DArray[0]);


The & operator passes the address of what is to its right in this case SpriteInA2DArray the first elements address.

What are you trying to accomplish?
That might be a good start of how to give you help :)[/code]

#7318 - Ninja - Sat Jun 14, 2003 11:31 pm

Hmmm... I was sure I had done something similar to that on Unix before when I was writing a shell... I guess given my results though, I am going to have to simply say that you are correct.

Thanks for the help so far, but here's the end result of what I am trying to do.

I am constructing a class for characters in a game. The class holds the base locations of the 2D arrays that hold the various different sprites that the character can use. The character is assigned a spot in OAM big enough to hold the largest sprite, and the sprites are swapped out as neccessary when a state change occurs. It SEEMS to be working correctly, except that the sprite isn't showing up. I just need a good way of passing a 2D array to that class. I seemed to remember using a pointer to a pointer when passing a 2D array to something before, but I guess I was wrong.

The displaySprite(u16* inputSprite) function I used in the example is a small part of the class that handles the sprite swapping functions. As an argument, it takes a pointer to the beginning of an array defining the sprite. I was taught that C++ will always simply pass a pointer to the function when an array is used as an argument, so I didn't know that I needed to use the & operator. But thanks for the tip, and I will try that out later on.

I suppose that the code that you have shown me will work, but I was hoping for an easier solution that can take an array of any size.

#7342 - Cyberman - Sun Jun 15, 2003 9:20 pm

I suggest a possibly different tact all together then.

It's not as neat at first but it will make sense in a bit.

First don't use 2d arrays for sprite data, this is going to create problems with your code. What I suggest is instead each unique image map has it's own pointer. Why? If you need to get more memory space your 2d arrays are OUT THE WINDOW and you have to recode a LOT. I suggest having an array of sprite references and #defined offsets into this array.

Example

typedef struct
{
unsigned char *SpriteMap;
unsigned short SpriteSize;
unsigned char SpriteFlags;
} sprite_struct;

#define MK_SPRITE_MAP(S, F) = \
{ &(S), sizeof((S)), F }

#define SPRITE_NONE 0x00
#define SPRITE_COMPRESSED 0x01
#define SPRITE_SIZE_8x8 (0 << 1)
#define SPRITE_SIZE_16x16 (1 << 1)
#define SPRITE_SIZE_32x32 (2 << 1)
#define SPRITE_SIZE_64x64 (3 << 1)

const sprite_struct sprite_map_list[] =
{
MK_SPRITE(MONSTER_000_0, SPRITE_COMPRESSED | SPRITE_SIZE_32x32),
MK_SPRITE(MONSTER_000_1, SPRITE_COMPRESSED | SPRITE_SIZE_32x32),
MK_SPRITE(MONSTER_001_0, SPRITE_COMPRESSED | SPRITE_SIZE_32x32),
MK_SPRITE(MONSTER_001_1, SPRITE_COMPRESSED | SPRITE_SIZE_32x32),
MK_SPRITE(MONSTER_002_0, SPRITE_COMPRESSED | SPRITE_SIZE_32x32),
MK_SPRITE(MONSTER_002_1, SPRITE_COMPRESSED | SPRITE_SIZE_32x32),
};

lets say you have a number of files with raw sprites in them and they are called MONSTER_000_0 etc in a nice neat organized manner.
#define SPRITE_IMAGE_COUNT 2
#define SPRITE_ID(N) ((N)*SPRITE_IMAGE_COUNT)
#define DEMODRAGON SPRITE_ID(0)
#define DA_ANT SPRITE_ID(1)
#define PET_DOG SPRITE_ID(2)

When you create your sprite class you can reference all this useful information to get your sprite data into memory and in the state you want. If you have a huge amount of sprite data compression is your friend (LZ77 is pretty straight forward compression).

This method allows you to create your sprites in an organized readily understood manner, and gives you the option of compressing it, or having different sizes it's stored in (for example you may need a sprite object that is 128 x 64 this would require 2 64x64 sprites and there pairs). This method makes it easy to store the information in compressed or uncompressed form without a big cost to you initially (a byte per sprite well worth it) and includes information about the sprite as well. the Size information allows you to save yourself time knowing how much space to allocate (lets assume size is uncompressed size needed for it in memory) for the sprite if it needs decompressed.

You also have nice constant offsets into your data array. It is well worth your time to organize most of your real data before you create your classes to use it. Perhaps not all of your sprites will have 2 images each.. maybe some will need 20 images you need to swap through or some just 1. Leave yourself the option to later change things.

Cyb

#7345 - Ninja - Sun Jun 15, 2003 10:30 pm

Thank you for all your help. I really do appreciate it. I did solve this problem on my own last night though. I realized that the data in a 2D array is contiguous, and I could just get a pointer to the start of the array, and offset by the array size every time I wanted to move to the next sprite. Since the properties of the sprite tell me the array size, it's no biggie for me to do it that way, and the change to my existing code took all of 2 minutes. It's working fine now.

The example you have shown me has sort of gone over my head at the moment, but I will keep referring back to it if I start to have space problems, or need to make my code more elegant. My own code seems like a brute force hack compared to yours, but it's working at the moment, and I really can't complain. I will use your own code for reference on how it SHOULD be done though. ;) I might implement compression later on, but I think I am happy with how it's working right now.

Thank you for all your help though, and I really do appreciate all that you have taught me.

#7371 - Cyberman - Mon Jun 16, 2003 2:13 pm

Well, actually all I was doing was hinting you might try to organize your data better, large projects DEMAND it to begin with. For all you know your little project can ballon into a full blown game in no time, then you have to live with the 'sins' of your past. Trust me it's better to do all the organization FIRST then add the details, I've BEEN there.

I've implemented 4 menu systems for applications with small memory and less code space (sigh). So I have the T-shirt on this one ;)

Cyb - The T-Shirt reads
"Don't be a slob with your code, or it will eat you for a light snack one night."

#7385 - Ninja - Mon Jun 16, 2003 5:53 pm

Actually, the end result of this project is supposed to be a full game. :)

I am aware that I am doing some things sort of uncleanly, but I am still somewhat of a newbie to all this, and this is my first attempt at a real game. Regardless, it's fairly easy for me to create and manipulate sprites.

Essentially how it works is I have two classes. One is built on top of the other. It's a little slow in structure, but the GBA seems to handle it ok.

The first class holds a base sprite. It sets up the sprite in OAM, and specifies all the attributes appropriately, depending on the sprite properties, which are passed in through the constructor. It has basic functions, including rotation, scaling, sprite swapping, and stuff of that sort.

The second class holds the data for a character. It manages timing, state changes, animations, moving on the map, and physics for each character.

A planned third class is built upon the first class again, but is intended for objects instead, that don't have the different states, such as running, crouching, etc.

Basically, I think that it's not as fast as it could be, but it's about as clean as my skill level allows, and once I finally get it all up and running and have killed all the bugs, it should be relatively easy to work with.