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 > Character sprite sizes

#28547 - identitycrisisuk - Tue Nov 02, 2004 6:24 pm

I'm thinking about making a platform shooter game for my first project on the GBA and I was wondering about how to organise the sprites for the main character. I thought I'd look at a few games where the characters seem about the size I want, these being Advance Guardian Heroes and Metroid Fusion. They seem to be rather odd sizes, somewhere in the region of 40x40 as opposed to either 32x32 or 64x64. How do you think that these are done, would it be a 64x64 sprite with a fair amount of wasted space or 32x32 with extra sprites 'stuck on'?

I had been thinking about having my character in two halves, upper and lower body so there wasn't too much duplication of sprites but I'm not sure if two 32x32 sprites on top of each other would be quite wide enough and adding stuff at the side would make it really complicated. Strangely enough I'm thinking of making the character a lot like Dante out of DMC as someone else on here has asked about. However I'd just have guns, no complications through sword sweeps etc. but I might want to have his trenchcoat fly out when he's coming down from a jump. The upper and lower sections idea would also make a lot of sense so I'd just have upper_aim_right and lower_stand, lower_walk instead of a walk and stand for every aiming direction.

#28550 - expos1994 - Tue Nov 02, 2004 7:09 pm

This is kind of off the top of my head:

Code:

const u16 *p_upperanimation;
const u16 *p_middleanimation;
const u16 *p_loweranimation;

const u16 upperwalk[8] = {1,1,2,2,2,6,7,8};
const u16 middlewalk[8] = {1,1,1,1,1,1,1,1};
const u16 lowerwalk[8] = {1,2,3,4,5,4,3,2,1};

const u16 upperattack[8] = {3,4,5,5,5,6,9,11};
const u16 middleattack[8] = {1,1,1,2,2,1,1,4};
const u16 lowerattack[8] = {1,1,2,2,2,2,3,3,3};

u16 upperframes[] = {....} //All of the upper body frames
u16 middleframes[] = {.....} //All of the middle body frames
u16 lowerframes[] = {.......} //All of the lowerbody frames



Basically what happens is your sprite is made up of three sprites (or more or less), and you have a set of graphics for each one (upperframes, lowerframes,middleframes). Then you have the actions defined upper/lower/middlewalk (and attack, and idle, and whatever else) as constants.

Then you have pointers that point to these constant animation definitions. What they point to is defined by the player's action. (if he's walking you point to the upperwalk/lowerwalk/middlewalk. If he's doing something else you point somewhere else. Then each time you load a new frame you use the definitions based on the action you are animating.

I hope that makes sense. Here's some more code (don't worry about the syntax, I'm making this up as I go along.)

Code:

switch (player.playeraction)
{
       case WALKING:
        {
                    p_upperanimation = upperwalk;
                    p_middleanimation = middlewalk;
                    p_loweranimation = lowerwalk;
                    break;
         }
        case  ATTACKING:
        {
                    p_upperanimation = upperattack;
                    p_middleanimation = middleattack;
                    p_loweranimation = lowerattack;
                    break;
         }
}


Now all you have to do is load your frames based on the values stored in p_upperanimation, p_middleanimation, p_loweranimation.

I hope that makes sense.

--Chris

[/code]

#28557 - sajiimori - Tue Nov 02, 2004 8:44 pm

Doing all that by hand is way too much busywork. Write (or find) a program to intelligently break up your animation frames into multiple sprites, and have your engine interpret its output.

#28568 - expos1994 - Tue Nov 02, 2004 10:11 pm

Quote:
Write (or find) a program to intelligently break up your animation frames into multiple sprites


I wrote and use a program that does this.

It's not much busy work if you have thirty different upper body frames and thirty different lower body frames and you want to mix and match them. (like the original poster is saying). In fact, I can't see another way around it that would be easier than handcoding the animation sequences. Do you know of a better way to match up the 23rd lower body with the 1st upperbody, and the very next frame have the 24th lower body match up with the 15th upper body?

The tool I use to animate a sprite made up of different sized sprites (a frankensprite) wouldn't be efficient in this case. As my tool takes an entire sprite sheet and cuts it up the way I want it. It wouldn't work for mixing and matching upper body and lower body, which is what the guy wanted help with. Mixing and matching is a good idea because it saves a lot of time cutting and pasting the same upperhalf to make animation sequences and also saves a boatload of ROM space.

[/quote]

#28591 - tepples - Wed Nov 03, 2004 3:40 am

expos1994 wrote:
Mixing and matching is a good idea because it saves a lot of time cutting and pasting the same upperhalf to make animation sequences and also saves a boatload of ROM space.

Anybody who has ever ROM-hacked Super Mario Bros. for NES can attest to this.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#28595 - sajiimori - Wed Nov 03, 2004 4:25 am

Quote:
It's not much busy work if you have thirty different upper body frames and thirty different lower body frames and you want to mix and match them. (like the original poster is saying). In fact, I can't see another way around it that would be easier than handcoding the animation sequences.
Oh, I didn't notice that he asked about that. The focus seemed to be on wasted empty space and the breaking up of single frames, which is a problem that should be solved before moving on to composite objects with interchangable parts.
Quote:
Do you know of a better way to match up the 23rd lower body with the 1st upperbody, and the very next frame have the 24th lower body match up with the 15th upper body?
It depends on whether you're doing programmatically-generated animation (which looks stiffer due to lack of hand-tweaking, and can result in seemingly "impossible" poses if you're not careful), or explicitly designing each sequence.

In the first case, yeah, it's all code.

In the second case, it's only a question of optimization. Theoretically, you could have an entire seperate image for each frame, ignoring redundancy. But you could have your frame splitter find redundancy between frames, too. If it's too hard to write a general search, you can give it hints about where the dividing lines are between the different sections that are expected to have a lot of redundancy.

So, it would still end up looking like you have a set of complete frames. Basically, go for whichever way makes the code the simplest while meeting your standards for animation quality.

#28611 - identitycrisisuk - Wed Nov 03, 2004 12:48 pm

Hmm, thanks for all the replies, I think I was kinda more interested in how games end up not wasting space right now. I've done a little bit of PSX 2D stuff before and am familiar with the idea of putting two sprites together, I did the simplest of almost-overhead shooters where there was a clear division between the body and legs so either could move in 4 directions.

With a program to do it, I suppose you don't really have to worry as much about the size of characters, just so long as it looks right on screen. Do you think that would ever lead to you not knowing fully what was going on with the sprites though? For example if one frame was bigger it might not line up properly with others and you'd have to go through trial and error to set it properly. I'm still quite interested in how other games do it, do you think there is anywhere I could look at game spritesets? (like mario as someone mentioned there)

I was looking at Metroid fusion more closely last night and it seemed like most of the different animations were for the whole body, not upper and lower i.e. the stance of the legs changes slightly when you aim upwards. Either they thought it looked better with different versions of legs as well or the sprite was drawn as a whole each time. So that makes me wonder whether her sprite would be something like:
Code:
+------------+------+
|32x16 sprite|16x16 |
|            |sprite|
|            |      |
+------------+------+
|32x32 sprite|16x32 |
|            |sprite|
|            |      |
|            |      |
|            |      |
|            |      |
+------------+------+

That seems like the best organisation for something larger than 32x32 but not quite 64x64. Or maybe that flipped upside down so that the main chunk would be (0,0) which is easier to understand.

I'm still not quite sure about how to split up my idea though. In some ways there are things which won't need much replication. If you're aiming up and right then you're also going to be walking so you'll never need to combine it with a stand pose. But at the same time I'd like to be able to fire at any time and if I wanna put some recoil into the arm holding the gun or something, then that would be best separated as it would conflict with what frame the walk animation was on. I'm not really much of an artist and what I've tried to draw is pretty rubbish but I'd like to get the ideas sorted properly incase I could ever find someone who could do some nice sprites for me and then I could just drop them in.

#28629 - sajiimori - Wed Nov 03, 2004 7:28 pm

For the changing-sizes-between-frames problem, use a reference point in each frame that represents a non-moving point in the character, such as the middle of the area they're standing on. You'll want a graphical tool to place such points.

#28633 - Touchstone - Wed Nov 03, 2004 8:10 pm

Ooh, I'm in the middle of writing an exporter that lets you create your character-animations in Macromedia Director and using them in your GBA game. I've done the basic exporting channels and keyframes and a demo that moves a couple of sprites around according to the Director animation and now I'm putting rotated sprites in there.
_________________
You can't beat our meat

#28673 - Marill - Thu Nov 04, 2004 5:25 am

sajimori, how difficult it is to be changing sizes of a sprite between frames of animations?

I'm concerened about the wasted spaces within a 64x64 sprite.... if I could be changing the sprite size in 1 frame from 64x64 to the next frame which may be 32x64 and then back to 64x64 again, will that involve lots of overheads in allocating/de-allocating OAM too?

#28677 - sajiimori - Thu Nov 04, 2004 6:09 am

Quote:
Ooh, I'm in the middle of writing an exporter that lets you create your character-animations in Macromedia Director and using them in your GBA game.
Hey, that's really cool!
Quote:
I'm concerened about the wasted spaces within a 64x64 sprite.... if I could be changing the sprite size in 1 frame from 64x64 to the next frame which may be 32x64 and then back to 64x64 again, will that involve lots of overheads in allocating/de-allocating OAM too?
It's entirely practical not only to rebuild all the OAM entries, but to free and reload animation frames as they change. Treat VRAM as the more valuable asset because it's easier to optimize for speed than for space, if the need ever arises.

#28865 - identitycrisisuk - Mon Nov 08, 2004 12:56 am

Thought I'd ressurect this thread as I now have some more questions about sprites, partially to do with size. So far I've gone through ?yvind Johansen and gbajunkies tutorials off gbadev and I was thinking about how the latter does animation earlier today. They basically load all of the animation frames into the OAM Data location and change offsets for different frames, which makes sense to me and is very easy to use. However, I started thinking that if you had a 64x64 sprite and tried to use this method, you would be screwed in terms of memory very quickly right?

So what is the better way of doing it, having a sprite object type thing that always looks at the same place in OAM memory and then you write in the particular frame you want? That seems like a lot to do, transferring 2048 values around for a 64x64 sprite but I guess it's maybe not that bad in the scale of things. Could it lead to slowdown though?

I also still kinda don't get how somethings are done on the GBA given the limitations on sprites and things. For example take this shot from Advance Guardian Heroes:
[Images not permitted - Click here to view it]

You have that huge robot thing, where the main part of the body is 64x64, your character is also a reasonable size, I assume some of the status bar and text are sprites as well. On top of that, a little later there are big missiles flying in, explosions and the possibility that you can generate your shield at any time (Couldn't time a really good shot with everything). Surely that's more than what can be stored?

I'm also interested in how you go about duplicating sprites, I assume that's not too hard - you just have two different sprite objects where the start of attribute2 points to the same place. But then how do you mix that with animation so that different enemies don't always do the same animation?

Sorry if some of that isn't expressed too well, the fact that different tutorials have different structures and names for registers is confusing me a little at the moment.

#28867 - poslundc - Mon Nov 08, 2004 12:59 am

Have a look at tepples' excellent FAQ for managing sprite VRAM; it should answer most of your questions.

The short of it is that it's basically better to preload into VRAM and switch in OAM when it's practical to do so, but as you pointed out it's very often not practical to do so because of the extreme limits to VRAM. In this situation, you should have plenty of time during VBlank to update VRAM.

Dan.

#28868 - identitycrisisuk - Mon Nov 08, 2004 1:14 am

I think I understand most of that, is some of it talking about assembler stuff though? I've not really come across anything about DMA copying yet, am I doing that if I just do a copy from an array to OAMData in C? I've also gone this far without seeing the OAM area described as VRAM, though that makes sense. When you say switch in OAM, you mean the sprite description things with attribute0, 1, 2 again yes? I also noted that he says this approach is not really for RTS games, this is because of the duplicate entities who aren't necessarily on the same frame like I said yes?

I guess you really have to plan what you're doing to get the best out of the memory available, you can't just copy tutorials and have it all work the more you add.

#28871 - poslundc - Mon Nov 08, 2004 4:30 am

OK, this confusion about OAMData is not your fault. This is because of a big misnomer in the GBA dev scene.

For the sake of your own sanity/confusion, stop calling it OAMData and refer to it as Sprite VRAM instead, which is what it technically is.

OAM stands for Object Attribute Memory, and it is a completely separate area of memory. OAM contains meta-data about the sprites - things like screen position, size, special effects, etc. - and Sprite VRAM contains the actual tile data that defines the sprites' bitmaps.

You have 32K of sprite VRAM - enough for 1024 16-colour tiles (or 512 256-colour ones). You have 1K of OAM, which is enough to define up to 128 entries or "objects" on the screen at a time.

So when I say switch in OAM, I mean having the tiles you want to display already in VRAM and then yes, switching attribute 2 to point to different tiles. However, it's often not practical to fit all of your sprites into 32K, in which case it becomes necessary to dynamically load new tiles into VRAM during the VBlank period. This is what tepples' FAQ talks about.

DMA does not require the use of assembly, but if you find it intimidating then don't worry about it and just copy stuff using for-loops. You can always easily switch to using the DMA later if you need the extra speed (which isn't really that much anyway). You can read about how to use the DMA in The Pern Project tutorials.

Quote:
I guess you really have to plan what you're doing to get the best out of the memory available, you can't just copy tutorials and have it all work the more you add.


Bingo. Some people never learn this, though.

Dan.

#28887 - identitycrisisuk - Mon Nov 08, 2004 2:14 pm

Cheers for the clarification, that's really useful.

I've also just got the newest version of VBA and discovered the debug tools that allow you to see what's going on at any time. It's really cool to see what parts of a commercial game are backgrounds, which are sprites and how those sprites are cut up and duplicated within VRAM. I can't think of any other format where you would be able to do this so easily and it's certainly starting to answer some of my questions about how effects are achieved that make you think they are using more sprites than possible.

#28902 - tepples - Mon Nov 08, 2004 4:42 pm

Try using more sprites than possible by unlocking OAM hblank access and then copying in a new set of sprites halfway down the screen.

A few NES emulators have viewers as well; look at Nintendulator.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#29026 - greyboy - Wed Nov 10, 2004 9:31 am

Thanks for the clarifications in this thread and for tepples's article reference. I would like to ask, in a very newbie fashion, how do you get data into the "game pak rom" section (0x08000000)? Are there any good demos that clearly demonstrate this?

I am trying to make an action type game and was thinking of using tepples's approach. Do I use this __attribute__ lingo to stuff the sprite data I create into the appropriate location?

#define CODE_IN_IWRAM __attribute__ ((section (".iwram"), long_call))

I guess I could just use the 256k in ewram to store all my spirte data but I'm cluelessly figuring that this approach is not correct. Can someone point me to a good demo/source to read, or a few newbie tips?

A pointer to an explanation of what the syntax above means or a faq on it would be great. Thanks in advance.

#29029 - greyboy - Wed Nov 10, 2004 9:41 am

Because I'm so good at answering my own questions after spending 15 minutes looking for answers, posting a question, then spending another 1 minute, here is what I found so far :P

info on ewram

any additional information (especially on game pak rom) is appreciated.

#29031 - greyboy - Wed Nov 10, 2004 10:51 am

I found this on rodata, if this if of use for someone explaining it. I found the material a bit foreign, but is this the approach I should be using?

Putting Data in ROM

Thanks for any help.

#29037 - poslundc - Wed Nov 10, 2004 2:43 pm

- Any variables or arrays declared as const will be put in ROM.
- All of your code by default gets put in ROM as well.
- Both global and local variables default to IWRAM.

EWRAM is used instead of ROM if your game is multiboot. Other than that, EWRAM is not used by default (although malloc/new are usually configured to use EWRAM).

The first point is the most important one with regards to answering your question. Just declare stuff "const" to put it in ROM.

Dan.

#29042 - identitycrisisuk - Wed Nov 10, 2004 6:26 pm

Right, decided to look at the pern project tutorial that involved DMA and made his modification to the CopyOAM function so that it looks like this:
Code:
REG_DM3SAD = (u32)sprites;
REG_DM3DAD = (u32)OAM;
REG_DM3CNT = 128 * 4 |DMA_16NOW;

That seemed to give an instant speedup to one tutorial I modified but made my code seem jerky (however this is through VBA running at 50% at best, still waiting for a flash cart). As far as I understand this same code can be used to copy sprite data to VRAM so I decided to modify a tutorial to copy a sprite to VRAM each frame, alternating between 2 different ones so it would flicker (horrible I know but I suppose it represents a worst case scenario). This looks like:
Code:
if(spch)
{
   REG_DM3SAD = (u32)EXPLOData;
   REG_DM3DAD = (u32)OAMData;
   REG_DM3CNT = 128|DMA_16NOW;
}
else
{
   REG_DM3SAD = (u32)block1Data;
   REG_DM3DAD = (u32)OAMData;
   REG_DM3CNT = 128|DMA_16NOW;
}
spch = !spch;

This seemed to slow things down considerably (I could tell as I was changing simple input demo that moves the sprite), even though less is being copied than to the OAM area. Is this because it is copying from ROM and not RAM? (block1Data and EXPLOData are const arrays) Also where should this code be placed (if it is a good way of doing it), before or after the function that waits for Vsync? I also noticed that the sprite would only flicker when it was not being moved but would stay as one of the two when a directional key is pressed, which I don't understand at all. If I just copy with arrays in the same way that the sprites are initialised then it always flickers and doesn't seem much faster or slower than DMA.

I know I'm just hacking things about to see what works and what doesn't, so I'm sorry if I've got the wrong end of the stick. I could really do with a tutorial on sprite copying through DMA but can't seem to find anything. If you could make any explainations as simple and comprehensive as possible that would be appreciated ;) It's also possible that it's just really unpredictable due to the bad performance of my emulator.

#29049 - ScottLininger - Wed Nov 10, 2004 7:18 pm

I've also noticed that using DMA multiple places in your code can occasionally cause crashes on hardware. So before you go and replace all of your manual ROM to RAM copying functions with OAM calls, back up your work and test throughly on hardware.

I'm sure there is a *reason* for this, but so far I've been forced to use the guess-and-test method of figuring out what's up. The frustrating thing is that I've identified that DMA is causing the problems, but they are TOTALLY inconsistent. I have some builds that will run for hours without a crash one moment only to crash after twenty seconds the next time I turn on my GB. And that's when there is NO USER INPUT in either case, so I can't imagine what the difference is.

-Scott

#29063 - greyboy - Wed Nov 10, 2004 10:48 pm

ic-uk, I believe if I understand correctly, that the dma-ing to the sprite-data area in vram should be occuring after the vsync(), not before. The point of this is to swap out that data while the rasterizer is in the vblank, not while it is drawing. Otherwise, you would be in danger of tearing.

Can someone who knows what he/she's talking about verify that what I said made sense? Or is it just a bunch of hooey? :)

#29070 - identitycrisisuk - Wed Nov 10, 2004 11:41 pm

Hmm, that's kinda what I thought about placement as well but I thought it might produce more immediately noticable effects if it was in the wrong place. I'd still like to hear an expert opinion and whether my code is a load of rubbish or not.

That sounds a bit nasty on the random DMA crash front, starting to put me off it now :s Inconsistent crashes are the most horrible thing in the world, I often refer to it as lecturer syndrome, as in when in Uni when I finally managed to grab a lecturer to look at a problem the program runs perfectly (which leads to you having to control your swearing while he's there and him wandering off thinking you're a weirdo).

#29077 - tepples - Thu Nov 11, 2004 12:58 am

Inconsistent crashes are often the result of a race condition. Which interrupts are you using, and on what scanline do you start DMA?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#29083 - ScottLininger - Thu Nov 11, 2004 4:09 am

tepples wrote:
Inconsistent crashes are often the result of a race condition. Which interrupts are you using, and on what scanline do you start DMA?


I'm using Krawall in Mono mode, which fires a DMA1 at the beginning of each Vblank. Then I'm calling a series of DMA2's and DMA3's to copy in my maps every frame. This has never crashed.

When I start using DMA3 to also copy in my sprite animations each frame and to update the OAM, it starts with the random crashes. If I go back to the old fashioned "one number at a time" approach to these, it's fine. And at least so far the performance is acceptable.

Anything I should try? I'm not firing the DMA on a specific scanline. In theory it's all happening during Vblank, though it could easily be spilling over.

-Scott

#29084 - tepples - Thu Nov 11, 2004 4:18 am

Ever tried an unrolled LDMIA/STMIA copy as ARM code in IWRAM? I've been told it plays nice with interrupts and other DMAs.

Does it stop locking up if you update OAM before updating sprite cel VRAM, so that you know you're still in vblank when you do it? Does it stop locking up if you turn on the "Unlock OAM during hblank" bit in DISPCNT?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#29087 - Marill - Thu Nov 11, 2004 5:44 am

Scott, i'm not sure about Krwall, but i'm using AAS and it used to give me this problem.

After further reading, I found that AAS also uses DMA3 in the mixer and my own sprite DMA3 routines are screwing with the AAS own DMA3 routines and causes the crash.

AAS provided an extra DMA3 routine for use so instead of using myLibDMA3Copy(), I'll use AAS_DoDMA3() routine instead. This solved my problem.

Is krwall using some DMA3 too?