Which is the better way to treat x, y, w and h of a sprite?

Started by
8 comments, last by sheep19 15 years, 3 months ago
First I need to say I'm using SDL. This is my sprite class:

#ifndef SPRITE_H
#define SPRITE_H

#include "SDL/SDL.h"

#include "constants.h"

class Sprite
{
public:
	Sprite(int _x, int _y, SDL_Surface *img);
	~Sprite();

	void setStep(unsigned int pixels) {step = pixels;}
	int getStep() {return step;}

	void move();

	void setDirectionLeft() {goesLeft = true; goesRight = false;}
	void setDirectionRight() {goesLeft = false; goesRight = true;}
	void setDirectionUp() {goesUp = true; goesDown = false;}
	void setDirectionDown() {goesUp = false; goesDown = true;}

	int getDirectionY() const; // returns 1 if goes up, -1 if goes down

	bool setImage(SDL_Surface *img);
	SDL_Surface* getImage() const;

private:
	//the copy constructor and the = operator aren't used
	Sprite(const Sprite &b);
	Sprite &operator = (const Sprite &b);

	int step; // the step (in pixels) that the ball does every time it moves, both in x and y axis

	// boolean variables that indicate the ball's direction
	bool goesUp, goesDown;
	bool goesLeft, goesRight;

	SDL_Surface *image;
};

#endif


when the surface image is loaded, it gets the width and height of the image (w and h members of SDL_surface). So it better to do this? 1. //constructor //load the image //have sprite have x, y, w and h members //x = x_pos; y = y_pos; (values passed int the constructor) //w = img->w; h = img->h; this? 2. //SDL_Rect rect as private member, values accessed via functions (eg int X()) //rect.x = x; rect.y = y; rect.w = img->w; rect.h = img->h; or this? 3. //int x, y; as private members //x = x_pos; y = _pos; (in constructor) //then, int X() const {return x;}......int W() const {return img->w;} //and have functions that set those values (only of x and y); which is the best way? I seem to prefer #3. ----------------------- I have another question.

void Sprite::move()
{
	//x axis
	if(goesLeft)
	{
		if(x - step >= 0)
		{
			x -= step;
		}
		else
		{
			goesLeft = false;
			goesRight = true;
		}
	}
	else // if(goes right), because when one of them is true, the other is false
	{
		if(x + w + step <= SCREEN_WIDTH)
		{
			x += step;
		}
		else
		{
			goesLeft = true;
			goesRight = false;
		}
	}
	
	if(goesUp)
	{
		if(y - step >= 0)
		{
			y -= step;
		}
		else
		{
			goesUp = false;
			goesDown = true;
		}
	}
	else // if goesDown == true
	{
		if(y + h + step <= SCREEN_HEIGHT)
		{
			y += step;
		}
		else
		{
			goesUp = true;
			goesDown = false;
		}
	}
}


This is the move functions. It uses SCREEN_WIDTH and SCREEN_HEIGHT which are declared in constants.h, so I need to incldude it. Is there another way to do this without including the header? maybe have functions: void setMaxX(usigned val); void setMaxY(unsigned val); But this means that the object will have an "uninitialized" state which I don't want. What is a better alternative? Thanks :)
Advertisement
Assuming you wish to go for an XYWH representation:

struct Hull { float x, y, w, h; };class Sprite{  Hull hull;public:  Sprite(/* ... */)  {    /* Initialize this->hull */  }  const Hull& GetHull() const { return hull; }};
Quote:Original post by sheep19
I have another question.

*** Source Snippet Removed ***

This is the move functions. It uses SCREEN_WIDTH and SCREEN_HEIGHT which are declared in constants.h, so I need to incldude it. Is there another way to do this without including the header? maybe have functions:
void setMaxX(usigned val); void setMaxY(unsigned val);
But this means that the object will have an "uninitialized" state which I don't want. What is a better alternative?

Thanks :)


Have you considered something like:

class play_area{private:    rect r;public:    play_area(rect r) : r(r) { }    rect get_area() const { return r; }};int main(){    play_area a(rect(0,0,WIDTH,HEIGHT));    sprite s;    while(loop)        {        s->move(play_area);        }}void sprite::move(const play_area &a){    if(left)        {        // etc        }}


This way, if you need to add more complex info (other collidable blocks, whatever) to the play area, you have a place now to do it and it is simple to change play areas.

Also, just a minor nitpick, but rather than having two separate GoesLeft and GoesRight booleans (that you have to keep in sync), would it not be easier to just have an int Direction set to -1 or +1?
Thanks for the replies!

Hmm, so create another class playArea?
How about a rect named playArea private to Sprite?

Edit: Ok, I made it a private member.

However when trying to compiler I get some strange errors (?)

#ifndef MY_SDL_FUNCTIONS_H#define MY_SDL_FUNCTIONS_H#include "SDL/SDL.h"#include "rect.h"bool pointInRect(const int pointX, const int pointY, const SDL_Rect &rect);bool collision(const Rect &rectA, const Rect &rectB);bool collision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2);bool collision(const SDL_rect &rectA, const SDL_Rect &rectB);#endif



#include "mySDLfunctions.h"bool pointInRect(const int pointX, const int pointY, const SDL_Rect &rect){	if(pointX >= rect.x && pointX <= rect.x + rect.w )		if(pointY >= rect.y && pointY <= rect.y + rect.h )			return true;	return false;}bool collision(const Rect &rectA, const Rect &rectB){	// create a temporary SDL_Rect struct so the values of rectB can be passed to pointInRect easily	SDL_Rect temp;	temp.x = rectB.x;	temp.y = rectB.y;	temp.w = rectB.Width();	temp.h = rectB.Height();	for(int i = rectA.x; i <= rectA.x + rectA.Width(); ++i)	{		for(int j = rectA.y; j <= rectA.y + rectA.Height(); ++j)		{			if(pointInRect(i, j, temp))				return true;		}	}	return false;}bool collision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2){	SDL_Rect temp;	temp.x = x2;	temp.y = y2;	temp.w = w2;	temp.h = h2;	for(int i = x1; i <= x1 + w1; ++i)	{		for(int j = y1; j <= y1 + h1; ++j)		{			if(pointInRect(i, j, temp))				return true;		}	}	return false;}bool collision(const SDL_rect &rectA, const SDL_Rect &rectB){	for(int i = rectA.x; i <= rectA.x + rectA.w; ++i)	{		for(int j = rectA.y; j <= rectA.y + rectA.h; ++j)		{			if(pointInRect(i, j, rectB))				return true;		}	}	return false;}


1>------ Build started: Project: Pong, Configuration: Debug Win32 ------
1>Compiling...
1>mySDLfunctions.cpp
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.h(12) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.h(12) : error C2143: syntax error : missing ',' before '&'
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(54) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(54) : error C2143: syntax error : missing ',' before '&'
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(56) : error C2065: 'rectA' : undeclared identifier
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(56) : error C2228: left of '.x' must have class/struct/union
1> type is ''unknown-type''
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(56) : error C2065: 'rectA' : undeclared identifier
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(56) : error C2228: left of '.x' must have class/struct/union
1> type is ''unknown-type''
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(56) : error C2065: 'rectA' : undeclared identifier
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(56) : error C2228: left of '.w' must have class/struct/union
1> type is ''unknown-type''
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(58) : error C2065: 'rectA' : undeclared identifier
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(58) : error C2228: left of '.y' must have class/struct/union
1> type is ''unknown-type''
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(58) : error C2065: 'rectA' : undeclared identifier
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(58) : error C2228: left of '.y' must have class/struct/union
1> type is ''unknown-type''
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(58) : error C2065: 'rectA' : undeclared identifier
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(58) : error C2228: left of '.h' must have class/struct/union
1> type is ''unknown-type''
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.cpp(60) : error C2065: 'rectB' : undeclared identifier
1>Generating Code...
1>Compiling...
1>main.cpp
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.h(12) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\users\user\documents\visual studio 2008\projects\pong\pong\mysdlfunctions.h(12) : error C2143: syntax error : missing ',' before '&'
1>Generating Code...
1>Build log was saved at "file://c:\Users\User\Documents\Visual Studio 2008\Projects\Pong\Pong\Debug\BuildLog.htm"
1>Pong - 19 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

EDIT:
Found it, I had SDL_rect instead of SDL_Rect :p

[Edited by - sheep19 on January 8, 2009 4:46:05 AM]
I'm getting a logical error, the ball is moving in the Y axis while I have no code doing so :|

in main:
// the ball	SDL_Rect bounds; bounds.x = 0; bounds.y = 0; bounds.w = SCREEN_WIDTH; bounds.h = SCREEN_HEIGHT;	Sprite ball(100, 100, loadImage("ball.gif"), bounds );	ball.setStep(BALL_SPEED);


sprite.h
#ifndef SPRITE_H#define SPRITE_H#include "SDL/SDL.h"#include "constants.h"class Sprite{public:	//constructor and destructor	Sprite(int _x, int _y, SDL_Surface *img, const SDL_Rect &_playArea);	~Sprite();	void setStep(unsigned int pixels) {step = pixels;}	int getStep() {return step;}	//perform a move	void move();	int X() const;	int Y() const;	int W() const;	int H() const;	bool setImage(SDL_Surface *img);	SDL_Surface* getImage() const;	//get a rect to x, y, w and h	SDL_Rect getOffset() const;	enum DIRECTION {TO_ZERO, NOT_MOVING, TO_INFINITY}; // the sprite's direction	DIRECTION getDirectionX() const;	DIRECTION getDirectionY() const;private:	//the copy constructor and the = operator aren't used	Sprite(const Sprite &b);	Sprite &operator = (const Sprite &b);	int step; // the step (in pixels) that the ball does every time it moves, both in x and y axis		DIRECTION directionX, directionY;	SDL_Surface *image;	//offset holds the x and y positions of the sprite, plus its width and height	SDL_Rect offset;	//	SDL_Rect playArea;};#endif


sprite.cpp
#include "sprite.h"Sprite::Sprite(int _x, int _y, SDL_Surface *img, const SDL_Rect &_playArea): image(0), step(1){	image = SDL_DisplayFormat(img);		offset.x = _x;	offset.y = _y;	offset.w = image->w;	offset.h = image->h;	playArea = _playArea;	directionX = TO_ZERO;	directionY = NOT_MOVING;}Sprite::~Sprite(){	SDL_FreeSurface(image);	image = 0;}void Sprite::move(){	//x axis	if(directionX == Sprite::DIRECTION::TO_ZERO)	{		if(offset.x - step >= playArea.x)		{			offset.x -= step;		}		else		{			directionX = Sprite::DIRECTION::TO_INFINITY;		}	}	if(directionX == Sprite::DIRECTION::TO_INFINITY) // if(goes right), because when one of them is true, the other is false	{		if(offset.x + offset.w + step <= playArea.w)		{			offset.x += step;		}		else		{			directionX = Sprite::DIRECTION::TO_ZERO;		}	}}bool Sprite::setImage(SDL_Surface *img){	if(!image) // if the image is null assign to it and return true	{		image = SDL_DisplayFormat(img);		return true;	}	return false;}SDL_Surface* Sprite::getImage() const{	return image;}SDL_Rect Sprite::getOffset() const{	return offset;}int Sprite::X() const{	return offset.x;}int Sprite::Y() const{	return offset.y;}int Sprite::W() const{	return offset.w;}int Sprite::H() const{	return offset.h;}Sprite::DIRECTION Sprite::getDirectionX() const{	return directionX;}Sprite::DIRECTION Sprite::getDirectionY() const{	return directionY;}


The ball is moving diagonally!!

even this make it move dianonally..
void Sprite::move(){	//x axis	offset.x += step;}


Why is that?

[Edited by - sheep19 on January 8, 2009 5:00:38 AM]
The posted code looks ok so far.

Are you sure it's not just the displaying code that uses the offset.x for both x and y positions?

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Something's really strange here.

In main, I do this to display the ball:
applySurface(ball.X(), ball.Y(), ball.getImage(), screen);

I changed it to this:
applySurface(ball.X(), 0, ball.getImage(), screen);
and the ball still moves diagonally!! :| I'm not calling applySurface on that image anywhere else.
ball.move();Uint32 c = SDL_MapRGB(screen->format, 255, 255, 255);SDL_FillRect(screen, &ball.getOffset(), c);SDL_UpdateRect(screen, 0, 0, 0, 0);applySurface(ball.getOffset().x, ball.getOffset().y, ball.getImage(), screen);


I did this for some testing. The rectangle behaves well, it moves like it should. On the other hand the ball's image stays still.

When I use to code to move only on Y axis the ball doesn't move at all.
When I use to move ONLY on X axis it moves diagonally.

By the way I checked the values of x and y using the debugger and they are correct.

Image and video hosting by TinyPic

Is something wrong when I draw the surfaces? This is strange.
Argh.

void applySurface(Sint16 x, Sint16 y, SDL_Surface *source, SDL_Surface *destination){	SDL_Rect offset;	offset.x = x;	offset.y = x; // damn man	SDL_BlitSurface(source, 0, screen, &offset);}

This topic is closed to new replies.

Advertisement