Page 1 of 2

Sprite drawing problem

Posted: Sun Jan 30, 2011 11:52 am
by elefas
Hello all,

Another newbie trying to dwell into genesis programming world. I am trying to make an arkanoid style of game (a paddle and a ball). I have problems in showing the correct sprites of the paddle and the ball.

The ball is a 16x16 pixels bmp image of 2 colors (black and red, I set black for transparent) and the paddle is a 24x8 bmp image (again black color here is to be set transparent)

I export the tiles and palettes of the bmps (in C format) using imagenesis with the following settings:

Mode:
- 15color, 4bpp, 1 plane, 8x8 tiles
- Drawing in the Y direction first, then in the X (Sprites)
- Enable transparency
- Select transparent color -> I choose the black color mentioned before

Then I load the tiles in vram, create the sprites and check with kmods
both vram and sprites. What I see is that the tiles are distorted, in fact it seems that every pixel is doubled through the x-axis and thus the result is wrong.

Can somebody guide me what I am doing wrong?

here is the original ball sprite (drawn in Paint)
Image

and here is what sprites list has loaded
Image

As you can hardly distinguish the right side pixels seem to be missing in the last image

finally I am attaching the source code in case it might be of any use

main.c

Code: Select all

/*
 * main.c
 *
 *  Created on: 28 Ιαν 2011
 *      Author: elefas
 */

#include "genesis.h"

#define numberof(table)	(sizeof(table) / sizeof(*(table)))
#define TILE 32
#define numberOfTiles(x) (sizeof(x)/TILE)

//tiles
extern unsigned char ball_tiles[128];
extern unsigned char paddle_tiles[96];

//palettes
extern unsigned short ball_palette[16];
extern unsigned short paddle_palette[16];

//sprite list
_spritedef *ball;
_spritedef *paddle;

u8 gravity;

u16 width ;
u16 height ;

u16 score;
u8 delay;
u32 frames;
u8 direction;
u8 border;


void setupGraphics()
{


	u16 ballVramAddress = 0x0040;	//vram address for ball tiles is 64 (0x40)
	u16 paddleVramAddress = ballVramAddress + numberOfTiles(ball_tiles);

	VDP_setScreenHeight224();
	VDP_setScreenWidth320();

	height = VDP_getScreenHeight();
	width = VDP_getScreenWidth() - 24;

	VDP_loadTileData(ball_tiles, ballVramAddress, numberOfTiles(ball_tiles), 1);
	VDP_loadTileData(paddle_tiles, paddleVramAddress, numberOfTiles(paddle_tiles), 1);

	VDP_setPalette(0, ball_palette);
	VDP_setPalette(1, paddle_palette);

	/*set the ball sprite data at position (160,0 = 128+160= 288, 128+0= 128), 0xA=0101b=16x16 (sprite size in tiles),
	 * 32832 = 32768 + 64 = 1000000000000000b(priority/vflip/hflip) + 1000000b(vram position of tile data)*/
	VDP_setSprite(0, width/2, 0, 0x5, 0x8000 + ballVramAddress, 1);
	VDP_setSprite(1, width/2, height - border, 0x8, 0x8000 + paddleVramAddress, 0);



}

void init()
{
	setupGraphics();
	frames = 0;
	gravity = 1;
	score = 0;
	delay = 80;
	border = 5;


	VDP_drawTextBG(APLAN, "SCORE: ", 0x8000, 0, 0);
}

void dropBall(u32 frames, u16 delay)
{
	char str[16];
	ball = &spriteDefCache[0];
	int y = ball->posy;
	int x = ball->posx;
	if (frames%delay==0)
	{
		if (gravity)
		{
			if(y<height)
			{
				y += 1;
				//random change direction
				if (direction == 0)
				{
					if (x<width-border)
						x++;
					else
						direction = !direction;
				}
				else
				{
					if (x>0 + border)
						x--;
					else
						direction = !direction;
				}
			}
		}
		else if (!gravity)
		{
			if(y>0 + border)
			{
				y -= 1;

				//random change direction
				if (direction == 0)
				{
					if (x<width-border)
						x++;
					else
						direction = !direction;
				}
				else
				{
					if (x>0 + border)
						x--;
					else
						direction = !direction;
				}
			}
			else
			{
				gravity = !gravity;
			}
		}
		VDP_setSpritePosition(0, x, y);
	}

//	intToStr(ball->posy, str, 1);
//	VDP_drawText(str,  2, 27);
}

static void handleInput()
{
    u16 value;

    value = JOY_readJoypad(JOY_1);

    char str[16];

		//paddle left right movement
    if (frames%delay/10==0)
    {
    	paddle = &spriteDefCache[1];
		if (value & BUTTON_START)
		{
			init();
		}
		if (value & BUTTON_LEFT)
		{
			u16 xpos =paddle->posx;
			if (xpos > 0) xpos -= 1;
			VDP_setSpritePosition(1, xpos, paddle->posy);
		}
		if (value & BUTTON_RIGHT)
		{
			u16 xpos = paddle->posx;
			if (xpos < width -8) xpos += 1;
			VDP_setSpritePosition(1, xpos, paddle->posy);
		}
		if (value & BUTTON_UP)
		{
			if (delay >1) delay --;
		}
		if (value & BUTTON_DOWN)
		{
			if (delay <10) delay ++;
		}
//        intToStr(paddle->posx, str, 1);
//        VDP_drawText(str,  2, 25);
    	//    }
    }

}

void drawScore()
{
	char scoreStr[16];
	intToStr(score, scoreStr, 1);
	VDP_drawTextBG(APLAN, scoreStr, 0x8000, 7, 0);
}

void collisionDetection()
{
	vu16 *pw;
	pw = (u16 *) GFX_CTRL_PORT;

	u16 collision = *pw & 0x20; //check control register bit no. 5 for sprite collision
	if (collision) //if collision happens reverse gravity, add up score and choose randomly a direction (left or right)
	{
		gravity = !gravity;
		direction = random()/32768;
		score = score + 100/delay;
		drawScore();
	}

}


//main starts here
int main(){

	//initialize game
	init();

	while(1)
	{
		dropBall(frames, delay);
		handleInput();
		collisionDetection();
		VDP_blit();

		//count frames
		frames ++;
	}

}
sprites.c

Code: Select all

/*
 * sprites.c
 *
 *  Created on: 28 Ιαν 2011
 *      Author: elefas
 */


/**
 * sprite tile data
 */

const unsigned char ball_tiles[128]={
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x11,0x11,0x00,0x01,0x10,0x00,	/* Tile #0 */
		0x01,0x11,0x10,0x01,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
		0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x01,0x11,0x11,0x11,	/* Tile #1 */
		0x00,0x01,0x11,0x11,0x00,0x01,0x11,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,
		0x11,0x11,0x10,0x00,0x11,0x11,0x11,0x10,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,	/* Tile #2 */
		0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
		0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,	/* Tile #3 */
		0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x10,0x11,0x11,0x10,0x00
	};

const unsigned char paddle_tiles[96] = {
	0x11,0x11,0x11,0x11,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,	/* Tile #0 */
	0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x23,0x33,0x33,0x33,0x11,0x11,0x11,0x11,
	0x11,0x11,0x11,0x11,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,	/* Tile #1 */
	0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x33,0x33,0x33,0x33,0x11,0x11,0x11,0x11,
	0x11,0x11,0x11,0x11,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,	/* Tile #2 */
	0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x33,0x33,0x33,0x33,0x11,0x11,0x11,0x11
};




/**
 * sprite palettes data
 */

const unsigned short ball_palette[16]={
		0x0000,0x000E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
	};

const unsigned short paddle_palette[16] = {
	0x0000,0x0000,0x0CCC,0x0888,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
};




Posted: Sun Jan 30, 2011 5:36 pm
by Moon-Watcher
You have to use TILE_ATTR_FULL() as the 5th parameter of your VDP_setSprite() calls.

It's defined in vdp_tile.h and is used to specify the characteristics of tile charged.

Hope it works.

EDIT:
This^ wouldn't solve your problem. A better look to your code and I realized I speak too fast. Sorry.

Posted: Sun Jan 30, 2011 8:25 pm
by Chilly Willy
On the Genesis, sprite tiles are the same as background tiles - they are defined as 8x8 pixels, being 8 rows of 8 pixels. If your sprite is wider than 8x8, you have to break it into 8x8 pieces which you then position correctly for wider/taller. If you look at the docs, a 16x16 sprite is one 8x8 cell in the upper left position, followed by the next cell being in the lower left position, followed by the next cell going to the upper right, and the last to the lower right.

So in memory, the cells are stored as

cell0, cell1, cell2, cell3

while in space, they are drawn as

cell0 cell2
cell1 cell3

Posted: Mon Jan 31, 2011 9:09 am
by KanedaFr
what did you use to produce your sprites.c ?
because what you see on screen seems to be what is defined.....

Posted: Mon Jan 31, 2011 9:59 am
by KanedaFr
I strongly suggest you to use some features of sgdk to handle refresh and input.

I took the liberty to update your code how it could be done, be free to use it or not

Code: Select all

/*
 * main.c
 *
 *  Created on: 28 Ιαν 2011
 *      Author: elefas
 */

#include "genesis.h"

#define numberof(table)   (sizeof(table) / sizeof(*(table)))
#define TILE 32
#define numberOfTiles(x) (sizeof(x)/TILE)

//tiles
extern unsigned char ball_tiles[128];
extern unsigned char paddle_tiles[96];

//palettes
extern unsigned short ball_palette[16];
extern unsigned short paddle_palette[16];

void init();

//sprite list
_spritedef *ball;
_spritedef *paddle;

u8 gravity;

u16 width ;
u16 height ;

u16 score;
u8 delay;
u32 frames;
u8 direction;
u8 border;




void setupGraphics()
{
	u16 ballTileIdx = 2; //0x0040;   //vram address for ball tiles is 64 (0x40)
	u16 paddletileIdx = ballTileIdx + numberOfTiles(ball_tiles);

	VDP_setScreenHeight224();
	VDP_setScreenWidth320();

	height = VDP_getScreenHeight();
	width = VDP_getScreenWidth() - 24;

	VDP_loadTileData( (const u32*) ball_tiles, ballTileIdx, numberOfTiles(ball_tiles), 1);
	VDP_loadTileData( (const u32*) paddle_tiles, paddletileIdx, numberOfTiles(paddle_tiles), 1);

	VDP_setPalette(PAL0, ball_palette);
	VDP_setPalette(PAL1, paddle_palette);

	/*set the ball sprite data at position (160,0 = 128+160= 288, 128+0= 128), 0xA=0101b=16x16 (sprite size in tiles),
	 * 32832 = 32768 + 64 = 1000000000000000b(priority/vflip/hflip) + 1000000b(vram position of tile data)*/
	VDP_setSprite(0, width/2, 0, 0x5, TILE_ATTR_FULL(PAL0, 0, 0, 0, ballTileIdx) , 1);
	VDP_setSprite(1, width/2, height - border, 0x8,TILE_ATTR_FULL(PAL1, 0, 0, 0, paddletileIdx), 0);
}


void dropBall( ) //u32 frames, u16 delay)
{
	char str[16];
	u8 xSpeed=1;
	u8 ySpeed=1;

	ball = &spriteDefCache[0];
	int y = ball->posy;
	int x = ball->posx;


	if (gravity)
	{
		if(y<height)
		{
			y += ySpeed;
			//random change direction
			if (direction == 0)
			{
				if (x<width-border)
					x+=xSpeed;
				else
					direction = !direction;
			}
			else
			{
				if (x>0 + border)
					x-=xSpeed;
				else
					direction = !direction;
			}
		}
	}
	else if (!gravity)
	{
		if(y>0 + border)
		{
			y -= ySpeed;

			//random change direction
			if (direction == 0)
			{
				if (x<width-border)
					x+=xSpeed;
				else
					direction = !direction;
			}
			else
			{
				if (x>0 + border)
					x-=xSpeed;
				else
					direction = !direction;
			}
		}
		else
		{
			gravity = !gravity;
		}
	}

	VDP_setSpritePosition(0, x, y);


	//   intToStr(ball->posy, str, 1);
	//   VDP_drawText(str,  2, 27);
}

static void handleInput(u16 joy, u16 changed, u16 state)
{
	if (state & BUTTON_START)
	{
		init();
	}
}

void drawScore()
{
	char scoreStr[16];
	intToStr(score, scoreStr, 1);
	VDP_drawTextBG(APLAN, scoreStr, TILE_ATTR_FULL(PAL2, 0, 0, 0, 0), 7, 0);
}

void collisionDetection()
{
	vu16 *pw;
	pw = (u16 *) GFX_CTRL_PORT;

	u16 collision = *pw & 0x20; //check control register bit no. 5 for sprite collision
	if (collision) //if collision happens reverse gravity, add up score and choose randomly a direction (left or right)
	{
		gravity = !gravity;
		direction = random()/32768;
		score = score + 100/delay;
		drawScore();
	}
}

static void VBlankUpdate()
{
	paddle = &spriteDefCache[1];

	u16 value = JOY_readJoypad(JOY_1);
	if (value & BUTTON_LEFT)
	{
		u16 xpos =paddle->posx;
		if (xpos > 0) xpos -= 1;
		VDP_setSpritePosition(1, xpos, paddle->posy);
	}
	else if (value & BUTTON_RIGHT)
	{
		u16 xpos = paddle->posx;
		if (xpos < width -8) xpos += 1;
		VDP_setSpritePosition(1, xpos, paddle->posy);
	}
	if (value & BUTTON_UP)
	{
		//if (delay >1) delay --;
	}
	else if (value & BUTTON_DOWN)
	{
		//if (delay <10) delay ++;
	}

}
void init()
{
	frames = 0;
	gravity = 1;
	score = 0;
	//delay = 80;
	border = 5;

	//after border is defined
	setupGraphics();

	JOY_setEventHandler(handleInput);
	setVBlankCallback(VBlankUpdate);

	VDP_drawTextBG(APLAN, "SCORE: ", TILE_ATTR_FULL(PAL2, 0, 0, 0, 0), 0, 0);
}

//main starts here
int main(){

	//initialize game
	init();

	while(1)
	{
		dropBall( ); //frames, delay);
		//handleInput();
		collisionDetection();

		VDP_waitVSync();
		VDP_blit();
		//count frames
		frames ++;
	}

}
you could notice

1/ VDP_loadTileData waits for a tile index, not a vram adress
2/ I mainly use constant (PAL0, PAL1...)
3/ Like Moon-Watcher said, I use TILE_ATTR_FULL and so easily fix your setSprite & VDP_drawTextBG (drawText used pal0 that replaced so... invisible text!)
4/ I wait each update for refresh, not a delay or a frame num....
5/ speed will be changed using xSpeed and ySpeed not according delay (you should use a xPadSpeed also)
6/ I handle joy input in joy handler when possible


Tiles tutorial is online now, so perhaps it will be easier for you now

FYI, collision using GFX_CTRL_PORT is only useful on some rare case...
read my doc for more information : http://gendev.spritesmind.net/page-collide.html

Good luck ! :)

Posted: Mon Jan 31, 2011 7:48 pm
by elefas
Thanks a million times for the replies (and the code refactoring)

Unfortunately my problem still persists. The sprites are not displayed correctly. I changed the images of the ball and the paddle, recreated the C arrays via ImaGenesis but in vain.

ball image: Image

paddle image: Image

Can somebody please instruct me (steps and tools) how to create the correct sprite arrays of these 2 images? I have created them with Paint as 16-color bitmaps.

Posted: Mon Jan 31, 2011 8:59 pm
by KanedaFr
could you share with us your bmp, since png & jpg aren't useable.... ?

Posted: Mon Jan 31, 2011 9:34 pm
by Chilly Willy
Considering the ball is only 6x6, why aren't you just using a single 8x8 tile for it? Does it change size? Similar question about the paddle, which is only 8 pixels tall.

Posted: Tue Feb 01, 2011 8:50 pm
by elefas
Ok, I have redrawn the images to 8x8 pixels for the ball and 32x8 pixels for the paddle. Converted the to C arrays with ImaGenesis and here are the results:

original images (sorry cant use tag with bmps):
ball
http://sierra-club.gr/tmp/ball.bmp

paddle
http://sierra-club.gr/tmp/paddle.bmp

What VDP draws is:
ball
http://sierra-club.gr/tmp/ball_vdp.bmp

paddle
http://sierra-club.gr/tmp/paddle_vdp.bmp

The paddle seems almost identical to the original but the ball is half-drawn.

the C- arrays

Code: Select all

const unsigned char ball_tiles[32]={
		0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x11,0x00,0x01,0x12,0x21,0x01,0x12,0x21,0x12,	/* Tile #0 */
		0x01,0x11,0x12,0x21,0x00,0x01,0x11,0x12,0x00,0x00,0x01,0x11,0x00,0x00,0x00,0x00
	};

const unsigned char paddle_tiles[128] = {
		0x00,0x00,0x00,0x00,0x00,0x03,0x33,0x33,0x03,0x33,0x33,0x30,0x03,0x33,0x30,0x03,	/* Tile #0 */
		0x03,0x30,0x03,0x33,0x00,0x03,0x33,0x33,0x00,0x00,0x00,0x00,0x11,0x11,0x11,0x11,
		0x00,0x00,0x00,0x00,0x30,0x03,0x33,0x33,0x03,0x33,0x33,0x30,0x33,0x33,0x30,0x03,	/* Tile #1 */
		0x33,0x30,0x03,0x33,0x30,0x03,0x33,0x33,0x00,0x00,0x00,0x00,0x11,0x11,0x11,0x11,
		0x00,0x00,0x00,0x00,0x30,0x03,0x33,0x33,0x03,0x33,0x33,0x30,0x33,0x33,0x30,0x03,	/* Tile #2 */
		0x33,0x30,0x03,0x33,0x30,0x03,0x33,0x33,0x00,0x00,0x00,0x00,0x11,0x11,0x11,0x11,
		0x00,0x00,0x01,0x12,0x30,0x00,0x01,0x12,0x03,0x30,0x01,0x12,0x33,0x30,0x01,0x12,	/* Tile #3 */
		0x33,0x30,0x01,0x12,0x30,0x00,0x01,0x12,0x00,0x00,0x01,0x12,0x11,0x11,0x11,0x12
	};
P.S. After Kaneda's code refactoring and suggestions, my collision detection routine has stopped working, the 5th-bit in GFX_CTRL_PORT now never changes when the two sprites collide together. Any hint why this happens?

Thank you very much for helping me out.

Posted: Tue Feb 01, 2011 9:30 pm
by Chilly Willy
Looks like the tiles aren't being generated properly. Try using sixpack instead.

http://jiggawatt.org/badc0de/sixpack/

Posted: Wed Feb 02, 2011 8:55 am
by Pascal

Posted: Wed Feb 02, 2011 1:06 pm
by Stef
I think you're using 8 bits bmp where your converter software expect 4 bits bmp so converted tiles are incorrects. You should try some others converts tools or just try to change your bmp files to 4bpp.

Posted: Sat Feb 18, 2012 3:04 am
by djcouchycouch
Hi!

Newbie here.

I'm also hitting the same kind of garbled sprites using ImaGenesis. I'm using the 4000 beta version as it exports to C code.[*]

I've tried various combinations of:
- 24bit, 8bit and 4bit per pixel images.
- Drawing in either the X or Y direction options
- enabled and disabled transparency

The effect seems like a squash and then a stretch horizontally. There are horizontal lines that are doubled. Palettes seem to be exported OK.

No idea if the problem is the quantizing or the exporting. I haven't tried exporting as Binary.

I've also tried Genitile but the sprites came out even more distorted. Haven't experimented much with it, though.

I'm using the latest version of SGDK.


[*] Are there other tools that export to C code?

Posted: Sat Feb 18, 2012 8:48 am
by oofwill
Hi,

I dont know ImaGenesis, so i can't help you.

For my images, i'm using Genitile.
Saving my bmp file (8bits/pixel)

With genitile, it seems to me we have to give X and Y in the command line for converting sprite, opposite to juste converting an image. (this cause me spirte didn't appear in order if X & Y not included in the command line)

I used to have a script wich do it automatically.
I'll see if i find it back cause my HDD just crash...

Try to create a .bat file and edit it :

C:\YOUR_PATH\genitile yourspritefile.bmp-pal -sprw -sprh -o asgcc

(sprw = sprite width, sprh = sprite height)

I haven't tested yet (lol) but it should work.

Read readme.txt, it's very usefull

Posted: Sat Feb 18, 2012 11:39 am
by Pascal
oofwill is right , if you want to generate sprites in genitile you need to use -sprw and -sprh option.

saying your sprite is 16x16 pixels you need to do -sprw 2 -sprh 2