SDL video子系统学习 (二)



掌叔
2008-06-10 09:21:24

摘自:[url]http://blog.sina.com.cn/vcbear[/url]
作者:vcbear

上次学了如何在屏幕上绘图,这次我们将学习装载位图以及简单的键盘控制。
下面是完整的代码:
-------------------------------------------------------------------------

#include "libnge.h"
#include "stdio.h"



int xpos = 0, ypos = 0; //记录小熊坐标
SDL_Surface *bear = NULL; //小熊图片
SDL_Surface *background = NULL; //背景图片
SDL_Surface *screen = NULL;



void Slock(SDL_Surface *screen)
{
if( SDL_MUSTLOCK(screen) )
{
if( SDL_LockSurface(screen) < 0 )
{
return;
}
}
}


void Sulock(SDL_Surface *screen)
{
if ( SDL_MUSTLOCK(screen) )
{
SDL_UnlockSurface(screen);
}
}


SDL_Surface *load_image( std::string filename )
{
//Temporary storage for the image that's loaded
SDL_Surface* loadedImage = NULL;

//The optimized image that will be used
SDL_Surface* optimizedImage = NULL;

loadedImage = SDL_LoadBMP( filename.c_str() );
if( loadedImage != NULL )
{
//Create an optimized image
optimizedImage = SDL_DisplayFormat( loadedImage );

//Free the old image
SDL_FreeSurface( loadedImage );
}
return optimizedImage;
}



void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination )
{
//Make a temporary rectangle to hold the offsets
SDL_Rect offset;

//Give the offsets to the rectangle
offset.x = x;
offset.y = y;

//Blit the surface
SDL_BlitSurface( source, NULL, destination, &offset );
}



void apply_surface(SDL_Surface *img, int x, int y, int w, int h, int x2, int y2)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
SDL_Rect dest2;
dest2.x = x2;
dest2.y = y2;
dest2.w = w;
dest2.h = h;
SDL_BlitSurface(img, &dest2, screen, &dest);
}



void DrawScene()
{
Slock(screen);
apply_surface(background, xpos-2, ypos-2, 57, 89, xpos-2, ypos-2);
apply_surface(xpos, ypos,bear, screen);

SDL_Flip(screen);
Sulock(screen);
}



extern "C"
int main(int argc, char* argv[])
{

int done =0;
Uint8* keys;


/* Initialize the SDL library */
if( SDL_Init(SDL_INIT_VIDEO) < 0 )
{
fprintf(stderr,"Couldn't initialize SDL: %s
", SDL_GetError());
exit(1);
}

/* Clean up on exit */
atexit(SDL_Quit);

/*
* Initialize the display in a 640x480 8-bit palettized mode,
* requesting a software surface
*/
screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);
if ( screen == NULL )
{
fprintf(stderr, "Couldn't set 640x480x8 video mode: %s
",SDL_GetError());
exit(1);
}

bear = load_image( "bear.bmp" );
if (bear != NULL) printf("bear load succes!
");
SDL_SetColorKey (bear, SDL_SRCCOLORKEY | SDL_RLEACCEL,*(Uint8 *) bear->pixels);


background = load_image( "background.bmp" );
if (background != NULL) printf("background load succes!
");

apply_surface( 0, 0, background, screen );

while (done == 0)
{

SDL_Event event;
while (SDL_PollEvent(&event))
{
if ( event.type == SDL_QUIT )
{
done = 1;
}
}

keys = SDL_GetKeyState(NULL);
if ( keys[SDLK_UP] ) { ypos -= 1; }
if ( keys[SDLK_DOWN] ) { ypos += 1; }
if ( keys[SDLK_LEFT] ) { xpos -= 1; }
if ( keys[SDLK_RIGHT] ) { xpos += 1; }

DrawScene();

}

SDL_FreeSurface( bear );
SDL_FreeSurface( background );

exit(0);
}

-----------------------------------------------------------------------



在这个程序中,我们将加载一副小熊的位图,然后我们可以通过键盘来控制小熊上下左右移动。

首先,我们先声明一些变量, 声明3个SDL_Surface类型的全局变量,同时声明2个整型变量用于记录小熊的坐标。定义一个函数load_image来加载位图,返回一个SDL_Surface指针,在该函数内部,我们使用了SDL函数SDL_LoadBMP来真正加载位图,它把bmp文件的文件名作为参数传入,返回指向存储图像文件信息内存区域的指针。load_image中我们将背景图片background.bmp打开并使用background指针进行记录,将小熊图片bear.bmp打开并使用bear指针记录。
下面就是两个将图像绘制到屏幕的两个函数,名字都叫apply_surface,参数不同。在该函数内部,我们使用了SDL函数SDL_BliSurface来真正绘制图像。函数原型:

int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);

src 是需要进行绘制的surface,而dst是进行显示的surface。SDL_Rect是一个包含4个16位整型变量的结构:x, y, w(width)和h(height)。srcrect用来描述源surface中需要绘制部分,而dstrect用来描述在目的surface何处进行绘制。如果设置srcrect为NULL,那么源文件中包含的整个图像都会被显示。dstrect中的x和y变量指定了在何处blit SDL_Surface src。对于dstrect来说,w(width)和h(height)这两个变量是被忽略不计的。

如果需要只对源surface的一部分进行绘制并显示,就要用到第二个apply_surface函数了,这样就要用到源的x, y, w和h。再接下来,就是画小熊的函数了,首先我们用背景图案填充当前小熊图像所在区域,如果不这样做的话,小熊图像的移动就会造成背景上留下移动轨迹。就象下面的图:
[attach]87[/attach]
由于小熊图象是53X85的,我们将新填充区域扩大到57X89,这样就可以覆盖由于移动造成的轨迹残留。最后使用SDL_Flip对新的图像绘制区域进行更新。

以上绘图部分基本完成,下面将要完成的键盘控制小熊图片的移动。由于要移动小熊图像,我们需要对键盘的方向键的按下事件进行响应,因此在main函数开始时定义了Uint8* keys。

keys用来获得每一时间的键盘状态。获得键盘状态的函数为SDL_GetKeyState(),它返回一个指向Uin8类型的数组头部的指针。数组的每个元素都对应记录了某个按键是否被按下的标志。这里不在象先前的几个例子中那样在事件轮询SDL_PollEvent中检查按键,因而如果我们一直按住某键不放,是不会触发新的事件发生的,要使得小熊移动我们必须一下又一下的敲击某个方向键,显示这很傻。应该是按下某键不放开的话,小熊一直保持向此方向移动。因此我们将检查按键的程序段放到事件轮询之后处理。由于这里没有使用else if…结构,因此可以多个方向键同时按下进行移动。

到此,程序就算完成了,编译一下,下面是程序运行的截图:
[attach]88[/attach]
看起来还是有些小小的不和谐,那就是小熊图片上的黄色背景,可以去掉吗?当然可以,SDL提供了此方面的函数,我们只要在主函数中

...

load_image( "bear.bmp" );

...

加上

SDL_SetColorKey (bear, SDL_SRCCOLORKEY|SDL_RLEACCEL,*(Uint32 *) bear->pixels);

再编译运行下,成功了吧?
[attach]89[/attach]
经过实验SDL_SetColorKey 默认的取的是图片左上角第一个像素的颜色作为透明色,如果另有需求的话,也可以使用函数SDL_MapRGB来指定用何种颜色做透明色。