Hi all,
Since this topic seems to come up frequently on this mailing list, why
not put it in the FAQ? Or maybe it’s really time someone writes a
(free!) book about Linux game programming
Anyway, here is my approach to the sprite/background-problem:
The idea is to maintain a list of “dirty rectangles”, i.e., rectangular
areas on the screen, that need to be redrawn. This can be used for two
things:
a) For updating (via SDL_UpdateRect) only those areas of the screen that
actually need to be updated.
b) For restoring only those areas of the framebuffer before drawing the
new frame that need to be restored.
When displaying sprites on a static background, the pseudocode for the
mainloop would look like this (I’ve been playing a bit with Python
lately; I guess it shows):
for sprite in spritelist:
sprite.draw()
dirtylist.add(sprite.rect)
for rect in dirtylist:
SDL_UpdateRect(rect)
framebuffer.restore(rect)
dirtylist.clear()
The trick here is to implement a rectangle list that makes sure that the
contained areas do not overlap. This requires some overhead, but I
think updating the same region on the screen twice or although it didn’t
change is much more costly in the long run.
For those of you interested, I appended an implementation of this kind
of rectangle list I wrote some time ago. It’s written in C++, but it
shouldn’t be a problem to port this to vanilla C. Incidentally, I think
this would make a nice addition to SDL; but then again, maybe it is
already to high-level
Do with the code whatever you like. I hereby place it in the public
domain.
Cheers
Daniel
-------------- next part --------------
#include
#include
using namespace std;
/----------------------------------------
CRect interface.
----------------------------------------/
class CRect
{
public:
int X, Y, W, H;
CRect (int x=0, int y=0, int w=0, int h=0);
void assign (int x, int y, int w, int h);
void resize(int w, int h);
void move(int x, int y);
bool point_inside(int x, int y);
bool overlaps(const CRect& rect) const;
CRect intersect(const CRect& rect) const;
};
/----------------------------------------
CRect implementation.
----------------------------------------/
CRect::CRect(int x, int y, int w, int h)
: X (x)
, Y (y)
, W (w)
, H (h)
{}
void
CRect::assign(int x, int y, int w, int h)
{
X = x; Y = y;
W = w; H = h;
}
void
CRect::resize(int w, int h)
{
W = w; H = h;
}
void
CRect::move(int x, int y)
{
X = x; Y = y;
}
bool
CRect::point_inside(int x, int y)
{
return (x >= X && x < X+W && y >= Y && y<Y+H);
}
bool
CRect::overlaps(const CRect& rect) const
{
int x1 = max(X, rect.X);
int y1 = max(Y, rect.Y);
int x2 = min(X+W, rect.X + rect.W);
int y2 = min(Y+H, rect.Y + rect.H);
return !(x2 <= x1 || y2 <= y1);
}
CRect
CRect::intersect (const CRect& rect) const
{
int x1 = max(X, rect.X);
int y1 = max(Y, rect.Y);
int x2 = min(X+W, rect.X + rect.W);
int y2 = min(Y+H, rect.Y + rect.H);
return CRect(x1, y1, max(x2-x1, 0), max(y2-y1,0));
}
/----------------------------------------
CRectList interface.
----------------------------------------/
class CRectList
{
vector* Rectangles;
// Hide copying methods.
CRectList(const CRectList&);
CRectList& operator=(const CRectList&);
public:
// Constructor.
CRectList();
~CRectList();
// Functions.
void clear();
int size() const;
bool empty() const;
CRect& operator[](int index);
const CRect& operator[](int index) const;
void append(const CRect& rect);
void intersect(const CRect& rect);
void add(const CRect& rect);
void sub(const CRect& rect);
void append(const CRectList& rectlist);
void merge(const CRectList& rectlist);
};
/----------------------------------------
CRectList implementation.
----------------------------------------/
CRectList::CRectList()
: Rectangles (new vector)
{}
CRectList::~CRectList()
{
delete Rectangles;
}
void
CRectList::clear()
{
Rectangles->clear();
}
int
CRectList::size() const
{
return Rectangles->size();
}
bool
CRectList::empty() const
{
return Rectangles->empty();
}
CRect&
CRectList::operator[](int i)
{
return (*Rectangles)[i];
}
const CRect&
CRectList::operator[](int i) const
{
return (*Rectangles)[i];
}
void
CRectList::append(const CRect& rect)
{
if (rect.W > 0 && rect.H > 0)
Rectangles->push_back(rect);
}
void
CRectList::append (const CRectList& rectlist)
{
Rectangles->insert(Rectangles->end(),
rectlist.Rectangles->begin(),
rectlist.Rectangles->end());
}
void
CRectList::add (const CRect& rect)
{
CRectList rl;
rl.append(rect);
vector<CRect>::iterator iter;
for (iter = Rectangles->begin(); iter != Rectangles->end(); ++iter)
rl.sub(*iter);
this->append(rl);
}
void
CRectList::intersect(const CRect& rect)
{
CRectList* rl = new CRectList;
vector<CRect>::iterator iter = Rectangles->begin();
for (; iter != Rectangles->end(); ++iter)
{
const CRect& r = *iter;
const int x1 = max( r.X, rect.X );
const int x2 = min( r.X+r.W, rect.X+rect.W );
const int y1 = max( r.Y, rect.Y );
const int y2 = min( r.Y+r.H, rect.Y+rect.H );
rl->append( CRect( x1, y1, x2-x1, y2-y1 ));
}
delete Rectangles;
Rectangles = rl->Rectangles;
rl->Rectangles = NULL;
delete rl;
}
void
CRectList::sub(const CRect& rect)
{
CRectList* rl = new CRectList;
vector<CRect>::iterator iter;
for (iter = Rectangles->begin(); iter != Rectangles->end(); ++iter)
{
const CRect& r = *iter;
if (r.overlaps(rect))
{
const int x1 = max( r.X, rect.X );
const int x2 = min( r.X+r.W, rect.X+rect.W );
const int y1 = max( r.Y, rect.Y );
const int y2 = min( r.Y+r.H, rect.Y+rect.H );
rl->append( CRect( r.X, r.Y, r.W, y1-r.Y ));
rl->append( CRect( r.X, y1, x1-r.X, y2-y1 ));
rl->append( CRect( x2, y1, r.X+r.W-x2, y2-y1 ));
rl->append( CRect( r.X, y2, r.W, r.Y+r.H-y2 ));
}
else
rl->append(r);
}
delete Rectangles;
Rectangles = rl->Rectangles;
rl->Rectangles = NULL;
delete rl;
}
/** Merge two rectangle lists. */
void
CRectList::merge(const CRectList& rl)
{
for (int i=0; i<rl.size(); i++)
this->add( rl[i] );
}