Design a Sprite class

Started by
7 comments, last by iliak 15 years, 3 months ago
In my last project I designed a sprite class but it was messy. This time I want to design a sprite class which can be a base class to other ojects. Sprite.h

#ifndef SPRITE_H
#define SPRITE_H

#include "SDL/SDL.h"

//contains functions that deal with collision
#include "mySDLfunctions.h"

class Sprite
{
public:
	Sprite(Sint16 x, Sint16 y, SDL_Surface *image); // constructor
	Sprite(SDL_Surface *image); // constructor (x and y are set to 0)

	Sprite(Sprite &spr); // copy constructor
	Sprite& operator = (Sprite &spr); // assignment operator

	~Sprite(); // destructor

	SDL_Surface *image(); // returns a pointer to the sprite's image
	SDL_Rect rect() const; // returns a copy of the rectangle holding x, y, w, h

	// collision functions
	bool collidesWith(const SDL_Rect &rect) const; // returns true if there's a collision with an SDL_Rect
	virtual bool collidesWith(const Sprite &spr) const; // returns true on collision with Sprite object. Made virtual to be overrideen
	
	Sint16 x() const;
	Sint16 y() const;
	Uint16 w() const;
	Uint16 h() const;

protected:
	SDL_Rect offset; // holds x, y, w and h

private:
	SDL_Surface *img; // pointer to the sprite's image
};

#endif // SPRITE_H

So I have my own destructor, copy constructor and assignment operator because I have an SDL_Surface * struct which is stored on the heap and its memory must be freed. I created two constructors to add some flexibility (a pointer to an SDL_Surface is always required though, it can't be set later). collision functions bool collidesWith is overloaded. The arguments I pass are declared as const (plus the functions). This way I can't have side effects right? (e.g when two sprites collide change their speed etc; ok I don't have speed but Sprite more of a generic type so classes that inherit it can have it). So to have side effect I have to removed the const keywords and pass a reference, am I correct? The Sprite version is also declared as virtual to be overriden. SDL_Rect offset. It's protected. I have some questions regarding this. If I make it private, will child classes be able to call functions such as x() (that returns offset.x) ? It makes me think that if it's protected, child classes will be free to change the width and height of the rectangle (offset.w/h) and mess things up. So I thought of this: keep it private and provide functions to set x and y (not w and h). What do you think about the design? I will be using the Sprite class in all my projects. -------------------------------------------------------------------------------- This is sprite.cpp by the way.

#include "Sprite.h"

Sprite::Sprite(Sint16 x, Sint16 y, SDL_Surface *image): img(0)
{
	img = SDL_DisplayFormat(image);
	
	offset.x = x;
	offset.y = y;
	offset.w = img->w;
	offset.h = img->h;
}

Sprite::Sprite(SDL_Surface *image): img(0)
{
	img = SDL_DisplayFormat(image);

	offset.x = 0;
	offset.y = 0;
	offset.w = image->w;
	offset.h = img->h;
}

Sprite::Sprite(Sprite &spr)
{
	SDL_FreeSurface(img);
	img = 0;
	img = SDL_DisplayFormat(spr.image());

	offset = spr.rect();
}

Sprite::~Sprite()
{
	SDL_FreeSurface(img);
}

Sprite& Sprite::operator =(Sprite &spr)
{
	if(this != &spr) // if we are not assigning to itself
	{
		SDL_FreeSurface(img);
		img = 0;
		img = SDL_DisplayFormat(spr.image());

		offset = spr.rect();
	}

	return *this;
}

SDL_Surface* Sprite::image()
{
	return img;
}

SDL_Rect Sprite::rect() const
{
	return offset;
}

bool Sprite::collidesWith(const SDL_Rect &rect) const
{
	return collision(offset, rect);
}

bool Sprite::collidesWith(const Sprite &spr) const
{
	return collision(offset, spr.rect());
}

Sint16 Sprite::x() const
{
	return offset.x;
}

Sint16 Sprite::y() const
{
	return offset.y;
}

Uint16 Sprite::w() const
{
	return offset.w;
}

Uint16 Sprite::h() const
{
	return offset.h;
}

I haven't inlined one line functions. The compiler will do it right?
Advertisement
1. Make your destructor virtual since you're planning on deriving this class.

2. Definitely have setter functions for x and y. Otherwise the sprite is of limited use.

3. Yes, making the rect private will still let derived classes call the (still public) x(), y(), etc. functions.

4. Compiler may or may not inline small functions for you. What is your compiler? Only some can handle link-time inlining (since you don't provide the source in the header, that's what it would need to do). Perhaps someone more experienced than I can give you a more complete answer here.

5. How about making rect() virtual? That way derived classes can use something other than their image's dimensions. Also, call rect() rather than directly getting the offset rectangle in collision.

6. You initialize img twice in each constructor.
Original post by Ezbez

Quote:
2. Definitely have setter functions for x and y. Otherwise the sprite is of limited use.


void setX(int val) { offset.x = val;}void setY(int val) { offset.y = val;}

Would it be a good idea to make them virtual? I can't think though how they could be changed though.

Quote:
4. Compiler may or may not inline small functions for you. What is your compiler? Only some can handle link-time inlining (since you don't provide the source in the header, that's what it would need to do). Perhaps someone more experienced than I can give you a more complete answer here.


I'm using Visual C++ 2008 EE.

Quote:
5. How about making rect() virtual? That way derived classes can use something other than their image's dimensions.


Why would they want to do that?

Quote:
Also, call rect() rather than directly getting the offset rectangle in collision.


What do you mean?

Quote:
6. You initialize img twice in each constructor.


How?


Thanks for the reply!

[Edited by - sheep19 on January 11, 2009 5:43:29 AM]
2. Up to you.

4. I don't remember which compilers do it, but I'm about 50% sure MSVC++ is one that can do it. Whether or not it actually does is up to it, of course.

5. Well, to me the obvious class to derive from a Sprite is an animated sprite of some sort. This class would use an image which has a sheet of sprites, each in different posses or states or whatever. Then you'd specify which of those states it would use. In short, it would only ever draw a portion of an image. Clearly the rectangle it would use would be smaller than the entire image. And I mean:
bool Sprite::collidesWith(const Sprite &spr) const{	return collision(rect(), spr.rect());}


And the same for the other collidesWith() overload.

6. You initialize it both in the initialization list and by assigning to it.
Sprite::Sprite(SDL_Surface *image): img(0){	img = SDL_DisplayFormat(image);

Both the ": img(0)" and the "img = " part initialize img, but the assignment overrides the original initialization. This is true for both of your constructors.
Hi

My 2 cents advice. Don't make collision test with the sprite image rectangle, but instead with another rectangle. This allow you to make collision with bigger or smaller rectangles. This let's make the player's sprite smaller so it's harder to collide with it. Or make the sprite missile bigger so it easier to hit monsters.
- Iliak -
[ ArcEngine: An open source .Net gaming framework ]
[ Dungeon Eye: An open source remake of Eye of the Beholder II ]
Quote:Original post by iliak
Hi

My 2 cents advice. Don't make collision test with the sprite image rectangle, but instead with another rectangle. This allow you to make collision with bigger or smaller rectangles. This let's make the player's sprite smaller so it's harder to collide with it. Or make the sprite missile bigger so it easier to hit monsters.


bool Sprite::collidesWith(const SDL_Rect &r) const{	return collision(rect(), r);}


I do that one, that's it.

bool Sprite::collidesWith(const Sprite &spr) const // virtual function{	return collision(rect(), spr.rect());}


This is done so when it collides with objects of the same type (or derived), side effects will occur.

EDIT: It shouldn't be const right?

BTW is it a good idea to check for collision and do side effects in the same function?
Just what i would do:
instead of adding plain setters for x/y think of things you would want to do with these values and add methods that do this with descriptive names like for x/y beeing the position you may want to set the position or move the object so instead of just adding setX/setY i would add
void setPosition( Sint16 x, Sint16 y );void move( Sint16 dx, Sint16 dy );

It's not that much of a difference in functionality but imho makes it much clearer what they are doing than using combinations of getters/setters to i.e. move the sprite.
In my opinion, I wouldn't do it this way, you're thinking of the sprite as a game object in itself. In reality, a sprite is just a veil that is contained within a game object.

My "sprite" implementation is as follows:

struct Texture{	SDL_Rect srcRect; //location to blit from srcSurface	SDL_Surface* srcSurface;};


Thought of as a texture since I come from a 3D background. It covers an object, I don't manually move it, it just follows an object (through the draw call below) that contains respective x/y coordinates for its location in game space.
void EntityManager::Draw(){//draws all entities in the entity map	for(m_entityIter = m_entityMap.begin();m_entityIter!=m_entityMap.end();++m_entityIter){		SDL_BlitSurface( m_entityIter->second->m_texture->srcSurface, &m_entityIter->second->m_texture->srcRect, m_entityIter->second->m_destination, &m_entityIter->second->location );	}}
I do this way too.

Technicaly speaking, a sprite is only the visual part. On the design part, a sprite is a game object (like the player or a monster) with coordinate, a velocity, a set of actions (AI), a score and other things.

That is what I was telling you in my post. Your sprite class, in your first post, (for me) is only the visual part of a game object.
- Iliak -
[ ArcEngine: An open source .Net gaming framework ]
[ Dungeon Eye: An open source remake of Eye of the Beholder II ]

This topic is closed to new replies.

Advertisement