Hi,
I am very new to programming on the DS, although I would classify my C++ knowledge as decent.
I have been working off of Patater's tutorial and found it very instructive. But when I try to put my own sprites in and display more than one of each, I begin to encounter problems with the palettes. I've got a gameboard made up of an array of columns, with each column's constructor displaying several of my square sprite. Then I have a set of pieces, with the constructor displaying several piece sprites. (Simplifying to one of each appears to make no difference in my problems). I've moved the initialization and display into these objects, and had to attempt to extrapolate what I should be doing for multiple sprites, with moderate success.
I am having two problems, both palette related, the first the more bothersome:
1) If I display only the board or only the pieces, everything is fine. But if I try to do both, the pieces (the second to initialize) come up strangely. I keep trying different things and I get different weird problems, but never quite right. Most recently, the first piece looks great but the rest all use the square's palette.
2) I've been having trouble trying to use multiple palettes, and although I just got it working, I'm not quite sure why. I figured with 16 palettes in 256 colours, I could just set up each palette to use 16 colours. The usenti program encourages this belief with the ability to choose which row of 16 colours it's displaying with. But if I have a transparent on each line, my results seem to be that it's being ignored. I would have thought with my palette set up as such:
Black, transparent, red, junk x 13
Black, transparent, green, junk x 13
Then I could just add 16 to get to the second palette. But they seem to be capped at 16, as after that point it isn't following my palette at all. OK, so maybe:
Black, transparent, red, black, transparent, green
and add 3.
But here's what's working:
Black, transparent, red, black, green
and adding 2. This pattern continues; if I keep alternating black and another colour then my 3-colour palette replaces the black with black and the red with the other colour, and I just add 4, 6, etc. It's ignoring the transparent altogether. So... what's up with that?
I suspect you'll want to see some code. Well, I suspect you want it all, but for now I'm going to try to avoid that because I'm not sure if I can attach it, and it'll look ugly in text since it's spread across many files. Maybe somebody will know immediately with only what I suspect are the pertinent lines:
Stuff in main:
Column * colArray[boardSize];
int main() {
powerOn(POWER_ALL_2D);
lcdMainOnBottom();
initVideo();
initBackgrounds();
displayBackgrounds();
// Set up a few sprites.
OAMTable *oam = new OAMTable();
initOAM(oam);
int numSprites = 0;
int nextID = 0;
for (int col = 0; col < boardSize + 2; col++)
{
colArray[col] = new Column(oam,numSprites,nextID);
numSprites += boardSize;
nextID = (*colArray[col]).getNextID();
for (int row = 0; row < boardSize; row++)
{
colArray[col]->squareInfo[row]->entry->x = startX + col * squareSize;
}
}
ObjPiece *p = new ObjPiece(oam, numSprites,nextID);
touchPosition touch;
for (;;) {
updateInput(&touch);
handleInput(&touch);
swiWaitForVBlank();
updateOAM(oam);
}
return 0;
}
In obj_piece:
ObjPiece::ObjPiece(OAMTable * oam, int c, int nextID) {
SpriteInfo spriteInfo[boardSize * 2];
int nextAvailableTileIdx = nextID;
// Construct the pieces
for (int player = 0; player < 2; player++)
{
for (int i = 0; i < boardSize; i++)
{
pieceInfo[player] = &spriteInfo[player*boardSize+i];
SpriteEntry * pieceEntry = &oam->oamBuffer[c+player*boardSize+i];
pieceInfo[player]->oamId = c+player*boardSize+i;
pieceInfo[player]->width = 32;
pieceInfo[player]->height = 32;
pieceInfo[player]->angle = 0;
pieceInfo[player]->entry = pieceEntry;
pieceEntry->isRotateScale = true;
pieceEntry->isSizeDouble = false;
pieceEntry->blendMode = OBJMODE_NORMAL;
pieceEntry->isMosaic = false;
pieceEntry->colorMode = OBJCOLOR_16;
pieceEntry->shape = OBJSHAPE_SQUARE;
pieceEntry->x = startX + player * (squareSize * (boardSize + 1));
pieceEntry->y = startY + (i-1) * squareSize;
pieceEntry->rotationIndex = pieceInfo[player]->oamId;
pieceEntry->size = OBJSIZE_32;
pieceEntry->gfxIndex = nextAvailableTileIdx;
nextAvailableTileIdx += pieceTilesLen / BYTES_PER_16_COLOR_TILE;
pieceEntry->priority = OBJPRIORITY_1;
pieceEntry->palette = pieceInfo[player]->oamId;
}
}
drawPieces(oam);
next_id = nextAvailableTileIdx;
}
int ObjPiece::getNextID()
{
return next_id;
}
void ObjPiece::drawPieces(OAMTable * oam) {
for (int player = 0; player < 2; player++)
{
for (int i = 0; i < boardSize; i++)
{
rotateSprite(&oam->matrixBuffer[pieceInfo[player]->oamId],
pieceInfo[player]->angle);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, piecePal+2,&SPRITE_PALETTE[
pieceInfo[player][i]->oamId * COLORS_PER_PALETTE], piecePalLen);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, pieceTiles, &SPRITE_GFX[(pieceInfo[
player][i]->entry->gfxIndex) * OFFSET_MULTIPLIER], pieceTilesLen);
SpriteEntry * e = pieceInfo[player][i]->entry;
}
}
}
And column:
Column::Column(OAMTable * oam, int c, int nextID) {
SpriteInfo spriteInfo[boardSize];
int nextAvailableTileIdx = nextID;
// Construct the row of squares
for (int i = 0; i < boardSize; i++)
{
squareInfo[i] = &spriteInfo[i];//c+i];
SpriteEntry * squareEntry = &oam->oamBuffer[c+i];
squareInfo[i]->oamId = c+i;
squareInfo[i]->width = 32;
squareInfo[i]->height = 32;
squareInfo[i]->angle = 0;
squareInfo[i]->entry = squareEntry;
squareEntry->isRotateScale = false;
squareEntry->isSizeDouble = false;
squareEntry->blendMode = OBJMODE_NORMAL;
squareEntry->isMosaic = false;
squareEntry->colorMode = OBJCOLOR_16;
squareEntry->shape = OBJSHAPE_SQUARE;
squareEntry->y = startY + i * squareSize;
squareEntry->rotationIndex = squareInfo[i]->oamId;
squareEntry->size = OBJSIZE_32;
squareEntry->gfxIndex = nextAvailableTileIdx;
nextAvailableTileIdx += squareTilesLen / BYTES_PER_16_COLOR_TILE;
squareEntry->priority = OBJPRIORITY_2;
squareEntry->palette = squareInfo[i]->oamId;
}
drawBoard(oam);
next_id = nextAvailableTileIdx;
}
int Column::getNextID()
{
return next_id;
}
void Column::drawBoard(OAMTable * oam)
{
for (int i = 0; i < boardSize; i++) {
rotateSprite(&oam->matrixBuffer[squareInfo[i]->oamId],
squareInfo[i]->angle);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, &squarePal[0], &SPRITE_PALETTE[
squareInfo[i]->oamId * COLORS_PER_PALETTE], squarePalLen);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, squareTiles, &SPRITE_GFX[
squareInfo[i]->entry->gfxIndex * OFFSET_MULTIPLIER], squareTilesLen);
}
}
Column has a public array (yeah, I know it shouldn't be public):
SpriteInfo * squareInfo[boardSize];
And Piece has this (although protected, here):
SpriteInfo * pieceInfo[2][boardSize];
Oh, and the constants:
const int squareSprite = 32;
const int spaceBetween = -1;
const int squareSize = squareSprite + spaceBetween;
const int boardSize = 3;
const int numRows = boardSize;
const int numCols = boardSize + 2;
const int startX = (256 - squareSprite * numCols - spaceBetween * (numCols - 1)) / 2;
const int startY = (192 - squareSprite * numRows - spaceBetween * (numRows - 1)) / 2;
static const int COLORS_PER_PALETTE = 16;
static const int BOUNDARY_VALUE = 32; // This is the default boundary value
// (can be set in REG_DISPCNT)
static const int OFFSET_MULTIPLIER = BOUNDARY_VALUE / sizeof(SPRITE_GFX[0]);
static const int BYTES_PER_16_COLOR_TILE = 16;
Thanks for any help you can give me, even if it's just to direct me elsewhere. As I said, I'm pretty new to this community. Thanks!
Palettes
Re: Palettes
Just a quick observation:
the palettes are ordered in the DS as:
Transparent, your colours x 15 for 16 color palettes, and
Transparent colour, your colours x 255 for the 256 color palettes.
In other words, palette position 0 is always treated as the transparent colour, except for the very first one - palette[0] of either the 256 or the 16 color palette 0 is the background color.
I'm in the middle of re-formatting my PC's at the moment, so I have not had a good look at your code, I'll have a look once I get a dev kit installed.
the palettes are ordered in the DS as:
Transparent, your colours x 15 for 16 color palettes, and
Transparent colour, your colours x 255 for the 256 color palettes.
In other words, palette position 0 is always treated as the transparent colour, except for the very first one - palette[0] of either the 256 or the 16 color palette 0 is the background color.
I'm in the middle of re-formatting my PC's at the moment, so I have not had a good look at your code, I'll have a look once I get a dev kit installed.
Re: Palettes
I've made some progress on this. Things seem to be capping at eight now, but I can set the squares and pieces to use separate base palettes and then dictate which is used and which subpalette to display.
I think that what was happening before is that every new sprite was storing its base palette in memory separately, and then I was probably running out of space (and looping around or something). So instead of setting palette to the oamID, I use 0 and 1 now (which I will shortly make into constants):
pieceEntry->palette = 1;//pieceInfo[player]->oamId;//
Then I just need to refer to &SPRITE_PALETTE[pieceEntry->palette * COLORS_PER_PALETTE] as the third parameter to dmaCopyHalfWords, because apparently that's telling it where to find the start of the palette for this sprite, and the second parameter is which part of the palette I'm actually using. In this case: piecePal, piecePal+2, piecePal+4, et cetera.
I've updated my Draw functions to handle multiple OAMs since showing you this, but here it is now:
void ObjPiece::drawPieces(OAMData * oam) {
for (int player = 0; player < 2; player++)
{
for (int i = 0; i < boardSize; i++)
{
int pal = pieceInfo[player]->entry->palette * COLORS_PER_PALETTE;
int gfx = (pieceInfo[player]->entry->gfxIndex) * OFFSET_MULTIPLIER;
rotateSprite(&oam->table->matrixBuffer[pieceInfo[player]->oamId],
pieceInfo[player]->angle);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, piecePal+4,(oam->main ? &SPRITE_PALETTE[pal] :
&SPRITE_PALETTE_SUB[pal]), squarePalLen);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, pieceTiles, (oam->main ? &SPRITE_GFX[gfx] :
&SPRITE_GFX_SUB[gfx]), pieceTilesLen);
}
}
}
Basically I created an OAMData class which contains the OAMTable and a boolean (main) which says whether it's the main or sub engine. The below code wasn't tested but should be about the equivalent to what I had before, but fixed:
void ObjPiece::drawPieces(OAMTable * oam) {
for (int player = 0; player < 2; player++)
{
for (int i = 0; i < boardSize; i++)
{
rotateSprite(&oam->matrixBuffer[pieceInfo[player]->oamId],
pieceInfo[player]->angle);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, piecePal+4, &SPRITE_PALETTE[pieceInfo[player]->entry->palette * COLORS_PER_PALETTE], squarePalLen);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, pieceTiles, &SPRITE_GFX[(pieceInfo[player]->entry->gfxIndex) * OFFSET_MULTIPLIER], pieceTilesLen);
}
}
}
Also, thanks StevenH about the transparency. That's not what I had understood, but it explains a thing or two. Now I just need to figure out why my attempts to switch to 256 colours per sprite are displaying garbage.
I think that what was happening before is that every new sprite was storing its base palette in memory separately, and then I was probably running out of space (and looping around or something). So instead of setting palette to the oamID, I use 0 and 1 now (which I will shortly make into constants):
pieceEntry->palette = 1;//pieceInfo[player]->oamId;//
Then I just need to refer to &SPRITE_PALETTE[pieceEntry->palette * COLORS_PER_PALETTE] as the third parameter to dmaCopyHalfWords, because apparently that's telling it where to find the start of the palette for this sprite, and the second parameter is which part of the palette I'm actually using. In this case: piecePal, piecePal+2, piecePal+4, et cetera.
I've updated my Draw functions to handle multiple OAMs since showing you this, but here it is now:
void ObjPiece::drawPieces(OAMData * oam) {
for (int player = 0; player < 2; player++)
{
for (int i = 0; i < boardSize; i++)
{
int pal = pieceInfo[player]->entry->palette * COLORS_PER_PALETTE;
int gfx = (pieceInfo[player]->entry->gfxIndex) * OFFSET_MULTIPLIER;
rotateSprite(&oam->table->matrixBuffer[pieceInfo[player]->oamId],
pieceInfo[player]->angle);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, piecePal+4,(oam->main ? &SPRITE_PALETTE[pal] :
&SPRITE_PALETTE_SUB[pal]), squarePalLen);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, pieceTiles, (oam->main ? &SPRITE_GFX[gfx] :
&SPRITE_GFX_SUB[gfx]), pieceTilesLen);
}
}
}
Basically I created an OAMData class which contains the OAMTable and a boolean (main) which says whether it's the main or sub engine. The below code wasn't tested but should be about the equivalent to what I had before, but fixed:
void ObjPiece::drawPieces(OAMTable * oam) {
for (int player = 0; player < 2; player++)
{
for (int i = 0; i < boardSize; i++)
{
rotateSprite(&oam->matrixBuffer[pieceInfo[player]->oamId],
pieceInfo[player]->angle);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, piecePal+4, &SPRITE_PALETTE[pieceInfo[player]->entry->palette * COLORS_PER_PALETTE], squarePalLen);
dmaCopyHalfWords(SPRITE_DMA_CHANNEL, pieceTiles, &SPRITE_GFX[(pieceInfo[player]->entry->gfxIndex) * OFFSET_MULTIPLIER], pieceTilesLen);
}
}
}
Also, thanks StevenH about the transparency. That's not what I had understood, but it explains a thing or two. Now I just need to figure out why my attempts to switch to 256 colours per sprite are displaying garbage.
Who is online
Users browsing this forum: Ahrefs [Bot], Google [Bot] and 5 guests