You'll have to forgive me if this has been covered before, but I'm trying to devise a scheme of setting up data for my game. I want to essentially have arbitrary tile sizes for a object/character. The easiest method is having a fixed setting for everything, but obviously you end up with limitations. So I came up with this sort of draft for RAM and ROM settings for objects (this is pseudo code)
Code:
RAM
struct Object ; 7 bytes?
{
.byte sprites_start ; sprite # in OAM
.byte tile_start ; tile start in CHR-RAM
.byte frame_counter ; for frame duration
.byte health ; health, duh
.byte damage ; damage dealt
.byte X
.byte Y
}
ROM
struct Header ; potential 20 bytes of overhead
{
.word animation_pointers ; for sake of argument, this = {@idle, @walk, @run, @jump, @attack, @hit, @death }
.byte sprites_reserved ; sprites to reserve in OAM
.byte number_of_tiles ; number of tiles to jam into CHR-RAM
.word tile_pointer
.byte health ; starting health
.byte damage
}
struct Animation ; 3 byte overhead per animation
{
.byte number_of_frames
.word first_frame
}
struct Frame ; 11 bytes overhead at least per frame (ouch)
{
.byte duration
.byte tile_number ; number of tiles to process
.byte[] tile_offsets ; where tile is located in stored CHR-RAM
.byte attributes ; associated attribute with all tiles (flip, etc)
.byte[] collision ; 4 bytes associated with collision rectangle
.word size ; width and height
.word next_frame ; next frame to jump to if counter = duration
}
So for each object I'd end up with about a 7 byte chunk of RAM. ROM wise, for say, a 3 frame, 7 animation object, there'd be almost 700byte of overhead alone (not counting tiles to shove into CHR-RAM). Theres some obvious optimizations, like pulling collision into the animation layer or even into the main object layer.
Any suggestions to help me out on this, improvements, better setup?
For representing the metasprites themselves, I just use a simple system, 4 bytes per sprite:
Y, Tile, Attributes, X
And use a Y position of 0x80 as the terminator.
When you call the code that inserts it into your sprite RAM, you also have variables for the object's Screen coordinates and current attribute mask. Just add X, add Y, and OR the attributes byte. This makes it easy to make multiple colors of the same sprite, just change the Attributes variable before you call the sprite add code.
Don't forget to check for sprite tiles going out of bounds near the edge of the screen, look at the sign bits of Before, Offset, and After to detect that.
Flipped versions of metasprites would need to be defined explicitly.
Anyway, I also stick in a pointer table so you can request a metasprite by number, so metasprite #1 could be the player facing right, metasprite #2 could be one frame of some animation sequence, etc.
Size: For a Super Mario-sized sprite, 16x32, using 8x16 tiles, it's 4 tiles, for 2 bytes (from pointer table), 4*4 bytes (from sprite definitions), and 1 byte (from terminator).
Also, what I'm describing is just the definitions of the metasprites themselves, and a simple way to generate sprites from them. This is separate from the animation part, or other game object tables.
I once made a little C# program that reads a PSD file and an image of the pattern tables, and generates sprite definitions by matching the graphics.
Meh, I guess I still don't really know WTF I'm doing with all this, but my main issue is I'm trying to use arbitray object size. I want to make a game where my main character is basically mario sized and can go up against enemies that use nearly the entirety of the OAM RAM.
Therefore, having fixed character/object sizes is out of the question. I'm also not using CNROM, I'm using UNROM so pretty much all my CHR data has to be put into RAM and I won't quite know where I'm actually sticking it. There is a variable I'm using that shows the start of the next available data and I calculate how much is left before sticking more graphics in. Generally I do this during level load time.
CNROM would allow me to use something like your tool, this method does not.
ManicGenius wrote:
CNROM would allow me to use something like your tool, this method does not.
You can still use Dwedit's method, you just have to add the base tile index to the tile index read from the meta-sprite data before writing to OAM. The sprite indexes in the meta-sprite definitions will be relative to the base address that you will know once the patterns are loaded.
I use a very similar system, but instead of OR'ing an attribute byte with the attribute bytes from the meta-sprites I EOR them, which allows me to flip sprites, something I find much more useful than recoloring (nearly all objects have to be flipped, but usually only a handful are recolored). You can still recolor with EOR, but you have to arrange your palettes more carefully.
Whatever you do regarding sprites, just don't use fixed OAM locations. That's a terrible newbie mistake that makes it hard to use the entirety of OAM (because of fragmentation) and causes sprites to constantly disappear because there is no sprite cycling.
I am using a system that meets your requirements. All sprites are rectangular and no pattern reuse is possible. If these limitations are acceptable then you might consider it.
I lay out all of the unique frames of animation in CHR-RAM in sequential order. Each object type has an attribute table that tells me where the sprite pattern data starts in the ROM and how large the sprite is (in terms of 8x16 sprite tiles). Then when I am generating OAM data I just use nested for loops. It is a fairly simple scheme to implement if you can manage your CHR-RAM and you've got plenty of PRG-ROM for the uncompressed sprites.
You might also think about using tables instead of structs. Structs are very difficult to code for in ASM and they use more CPU cycles.
qbradq wrote:
You might also think about using tables instead of structs. Structs are very difficult to code for in ASM and they use more CPU cycles.
The structs were for pseudocode purposes and did not represent actual structs. It's kinda how I lay out formats in my head. Like self contained entities.
I don't find ASM structs difficult to program for?
variable: .tag structName
variable+structName::subVariable
I meant an array of structs is slower and more difficult to program for.
tokumaru wrote:
Whatever you do regarding sprites, just don't use fixed OAM locations. That's a terrible newbie mistake that makes it hard to use the entirety of OAM (because of fragmentation) and causes sprites to constantly disappear because there is no sprite cycling.
Can you please explain more what you mean by "fixed OAM locations", I'm asking because this is probably exactly what I have done.
As I understand tokumaru, "fixed OAM location" means there is a direct mapping from game objects to addresses in OAM. The player goes in the first six entries, the player's missiles go in the next couple, etc.
tepples wrote:
As I understand tokumaru, "fixed OAM location" means there is a direct mapping from game objects to addresses in OAM. The player goes in the first six entries, the player's missiles go in the next couple, etc.
Yes, this is exactly what I'm doing, and why is this a bad thing?
This is a problem when you need to start cycling the sprites (and trust me, you will even for them most simple of games). If you leave the sprites in a fixed location then when more than 8 of them appear on the same row only the first eight IN OAM ORDER will be displayed. To work around this (and get the NES's trademark flicker effect) you need to change which locations are being used in the OAM buffer for each object each frame.
A simple example, and one that seems to work well for a lot of games, is to increment your starting address by 8, 16 or 32 every frame. You do this with an 8-bit index so it wraps back to 0, and you know you have reached the end of your buffer when your next index equals your starting value.
qbradq wrote:
A simple example, and one that seems to work well for a lot of games, is to increment your starting address by 8, 16 or 32 every frame. You do this with an 8-bit index so it wraps back to 0, and you know you have reached the end of your buffer when your next index equals your starting value.
And by "starting address" you mean the starting address of the DMA copy process? I mean this:
Code:
LDA #>sprite
STA $4014
Where "sprite" is at $0200. But if I start from $0208, $0216 or $0232 doesn't the DMA process go on for a whole page? like $0208-$0308?
The DMA process always starts at $xx00 and ends at $xxFF.
The point is that on some frames you start from $0200 and write through $02FF, and on other frames you start from $0280 and write through $02FF, then wrap back to $0200 and write through $027F.
picccca wrote:
But if I start from $0208, $0216 or $0232 doesn't the DMA process go on for a whole page? like $0208-$0308?
Like tepples said, the DMA will always copy from $xx00 to $xxFF... You only get to select the page the data is copied from, but you can't change the order in which the bytes in that page are copied. What you can change is the order in which you fill that page while rendering meta-sprite definitions, which achieves basically the same result.
Like qbradq mentioned, a simple method of sprite cycling consists in starting using the sprite page at different locations every frame. Since the index registers (X and Y) are only 8 bits wide, once you reach the end of the sprite page they will automatically wrap back to the start. All you have to do is make sure you don't output more than 64 sprites in a frame, otherwise you'll start overwriting the first ones you wrote.
tepples wrote:
The point is that on some frames you start from $0200 and write through $02FF, and on other frames you start from $0280 and write through $02FF, then wrap back to $0200 and write through $027F.
Oh, I think I get it. So let me ask, is it normal for a game to constantly overwrite the whole page 2 ($0200-$02FF) of RAM in order to shift the sprite data to another OAM location, like every frame?
tokumaru wrote:
What you can change is the order in which you fill that page while rendering meta-sprite definitions, which achieves basically the same result.
So only copying the sprite definitions to page 2 once at startup and then just modifying the X and Y bytes of a sprite in page 2 to move it just doesn't cut it?
tokumaru wrote:
All you have to do is make sure you don't output more than 64 sprites in a frame, otherwise you'll start overwriting the first ones you wrote.
because 4 bytes per sprite and 64 sprites equals 256 byte, right?
picccca wrote:
Oh, I think I get it. So let me ask, is it normal for a game to constantly overwrite the whole page 2 ($0200-$02FF) of RAM in order to shift the sprite data to another OAM location, like every frame?
Yes, games commonly rewrite the whole sprite data every frame.
tokumaru wrote:
So only copying the sprite definitions to page 2 once at startup and then just modifying the X and Y bytes of a sprite in page 2 to move it just doesn't cut it?
For very simple programs (where you have a constant number of objects and know for sure no more than 8 sprites will ever be aligned in a row) it might, but for most applications, no.
tokumaru wrote:
because 4 bytes per sprite and 64 sprites equals 256 byte, right?
Yeah. After writing 64 sprites, the index will point back to the first one you wrote.
Thanks, it's great to have this cleared up and confirmed.