Sprite drawing problem

SGDK only sub forum

Moderator: Stef

elefas
Newbie
Posts: 5
Joined: Sun Jan 30, 2011 11:28 am

Sprite drawing problem

Post by elefas » Sun Jan 30, 2011 11:52 am

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
};




Moon-Watcher
Very interested
Posts: 117
Joined: Sun Jan 02, 2011 9:14 pm

Post by Moon-Watcher » Sun Jan 30, 2011 5:36 pm

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.
Last edited by Moon-Watcher on Mon Jan 31, 2011 12:39 am, edited 1 time in total.

Chilly Willy
Very interested
Posts: 2816
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sun Jan 30, 2011 8:25 pm

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

KanedaFr
Administrateur
Posts: 1128
Joined: Tue Aug 29, 2006 10:56 am
Contact:

Post by KanedaFr » Mon Jan 31, 2011 9:09 am

what did you use to produce your sprites.c ?
because what you see on screen seems to be what is defined.....

KanedaFr
Administrateur
Posts: 1128
Joined: Tue Aug 29, 2006 10:56 am
Contact:

Post by KanedaFr » Mon Jan 31, 2011 9:59 am

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 ! :)

elefas
Newbie
Posts: 5
Joined: Sun Jan 30, 2011 11:28 am

Post by elefas » Mon Jan 31, 2011 7:48 pm

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.

KanedaFr
Administrateur
Posts: 1128
Joined: Tue Aug 29, 2006 10:56 am
Contact:

Post by KanedaFr » Mon Jan 31, 2011 8:59 pm

could you share with us your bmp, since png & jpg aren't useable.... ?

Chilly Willy
Very interested
Posts: 2816
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Mon Jan 31, 2011 9:34 pm

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.

elefas
Newbie
Posts: 5
Joined: Sun Jan 30, 2011 11:28 am

Post by elefas » Tue Feb 01, 2011 8:50 pm

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.

Chilly Willy
Very interested
Posts: 2816
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Tue Feb 01, 2011 9:30 pm

Looks like the tiles aren't being generated properly. Try using sixpack instead.

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

Pascal
Very interested
Posts: 200
Joined: Wed Nov 29, 2006 11:29 am
Location: Belgium
Contact:

Post by Pascal » Wed Feb 02, 2011 8:55 am


Stef
Very interested
Posts: 3004
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Wed Feb 02, 2011 1:06 pm

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.

djcouchycouch
Very interested
Posts: 683
Joined: Sat Feb 18, 2012 2:44 am

Post by djcouchycouch » Sat Feb 18, 2012 3:04 am

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?

oofwill
Very interested
Posts: 173
Joined: Mon May 03, 2010 6:12 pm
Location: France - Niort

Post by oofwill » Sat Feb 18, 2012 8:48 am

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

Pascal
Very interested
Posts: 200
Joined: Wed Nov 29, 2006 11:29 am
Location: Belgium
Contact:

Post by Pascal » Sat Feb 18, 2012 11:39 am

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

Post Reply