Chinaunix首页 | 论坛 | 博客
  • 博客访问: 457851
  • 博文数量: 109
  • 博客积分: 1463
  • 博客等级: 上尉
  • 技术积分: 859
  • 用 户 组: 普通用户
  • 注册时间: 2011-07-22 13:21
个人简介

希望和广大热爱技术的童鞋一起交流,成长。

文章分类

全部博文(109)

文章存档

2017年(1)

2016年(2)

2015年(18)

2014年(1)

2013年(9)

2012年(15)

2011年(63)

分类: WINDOWS

2012-02-20 21:49:25

转载地址:

简介

  Simple DirectMedia Layer, 简称SDL,是一个自由的跨平台的多媒体开发包,主要通过OpenGL和2D视频帧缓冲(framebuffer)提供对音频、键盘、鼠标、游戏操纵杆(joystick)和3D硬件的底层访问。它被广泛的应用于MPEG回放软件、模拟器和多个著名游戏中,如获得大奖的《文明:权倾天下》(Civilization: Call To Power)的Linux移植版本。它的官方站点是: ,还有一个非常个性的LOGO(图1)。



  在Win32编程中,如果要操纵2D图像,可以选择使用GDI或者DirectDraw,前者速度较慢,而后者是特定针对Microsoft平台的,在非Win32平台上移植会带来很大的麻烦。这时,你可以选择使用SDL。SDL支持多种平台:Linux、Windows, BeOS, MacOS Classic, MacOS X, FreeBSD, OpenBSD, BSD/OS, Solaris, IRIX和QNX,同时也部分支持部分其他平台:Windows CE, AmigaOS, Dreamcast, Atari, NetBSD, AIX, OSF/Tru64, RISC OS和SymbianOS。当然SDL本身是针对2D图像编程的,如果使用到3D编程,那么需要将SDL和OpenGL结合使用。

  SDL是用C编写的,但是也支持C++,同时其他多种语言,下面给出了其他语言的SDL的开发包的相关信息:

  ·Ada 
   AdaSDL -
  ·C# 
   SDL.Net -
  ·Eiffel 
   ewg -
  ·Erlang 
   ESDL - ~dgud/esdl/ 
  ·Euphoria 
   SDL_Wrap -
  ·Guile 
   guile-sdl -
  ·Java 
   JSDL -
   sdljava -
  ·Lisp 
   CL-SDL -
   SDL for Corman Common Lisp -
  ·Lua 
   luaSDL -
  ·ML 
   SDLML -
   OCamlSDL -
  ·Objective C 
   SDLOBJC -
  ·Pascal 
   SDL4Freepascal -
   JEDI-SDL -
  ·Perl 
   SDLPerl -
  ·PHP 
   PHP SDL module -
  ·Pike 
   Natively supported (module SDL) -
  ·Pliant 
   Pligame -
  ·Python 
   PyGame -
  ·Ruby 
   Ruby/SDL - ~ohai/index.en.html 
   RUDL -

  功能

  视频 

  ·设置8bpp或更高的任意色彩深度的视频模式。如果某个模式硬件不支持,可以选择转化为另一模式。 

  ·直接写入线性的图像帧缓冲(framebuffer)。 

  ·用颜色键值(colorkey)或者alpha混合属性创建用于绘图的表面(surface)。 

  ·Surface的blit能自动的转化为目标格式。blit是优化过的,并能使用硬件加速。x86平台上有针对MMX优化过的blit。 

  ·硬件加速的blit和fill(填充)操作,如果硬件支持的话。 

  事件 

  ·提供以下事件: 

  o 应用程序的visibility(可视性)发生改变 

  o 键盘输入 

  o 鼠标输入 

  o 用户要求的退出 

  ·每种事件都能通过SDL_EventState()关闭或者打开。 

  ·事件经由用户指定的过滤函数再被加入到内部的事件队列。 

  ·线程安全的事件队列。

  音频 

  ·设置8位和16位的音频,单声道或者立体声,如果格式硬件不支持,可以选择转换。 

  ·由独立的线程执行音频部分,并提供用户回调(callback)机制。 

  ·设计上考虑到了客户定制的软混音器,但实际上在例程中就包含了一个完整的音频/音乐输出库。

  CD音频 

  ·完整的CD音频控制API 

  线程 

  ·简单的线程创建API 

  ·用于同步的简单的二进制信号量(semaphores) 

  定时器 

  ·读取已流逝的毫秒数。 

  ·等待指定的毫秒数。 

  ·设置一个10毫秒精度的周期性定时器。

  字节序无关 

  ·侦测当前系统的字节序 

  ·快速转换数据的函数 

  ·读写指定字节序的数据 

  下载

  SDL的官方下载地址为download-1.2.php, 最新版本为1.2.8,分为运行库Runtime Library和开发库Development Library,使用SDL开发需要下载的是后者。由于SDL支持多平台和多个开发工具如VC6、Visual Studio.NET 2003、Dev-C++等等,所以可以根据需要选择合适自己的平台版本。由于笔者采用VC6给朋友们讲解SDL的使用,因此文章中只介绍了VC6下的安装方式,并使用了一个小例子做说明。如果您使用其他的平台和工具,请在官方网站上查找相应的说明并按照要求去安装使用。 

  VC6下SDL的安装和初步使用

  首先,我们为所有的工程创建一个文件夹tutorial,将下载的开发库SDL-devel-1.2.8-VC6.zip拷贝到tutorial下并解压,并保证如下的文件夹层次(图2)如下:


图2

  下面我们打开Visual Studio6.0,开发一个非常简单的小例子。选择File->New,新建一个工程,选择Win32 Application,将工程gp保存为tutorial目录下,如图3所示。


图3

  在图4中,保持默认设置,点击Finish。


图4

  为了使用SDL,我们需要进行相关的设置,选择Project->Settings...,选择C/C++属性页,在Category中选择Code Generation,在Use run-time library中选择Debug Multithreaded DLL,如图5所示。


图5

  在Category中选择Preprocessor,在'Additional include directories'的文本框中填上..\SDL-1.2.8\include\,如图6所示。


图6

  选择Link属性页,在'Object/library modules'文本框的首部增加SDLmain.lib和sdl.lib,如图7所示。


图7

  仍然是Link属性页,选择Category中的Input,在'Additional library path'文本框中填写..\SDL-1.2.8\lib\,如图8所示。


图8

  这样我们就完成了编程前的设置工作,不过这是针对Debug版本的,如果要设置Release版本,需要选择Build->Set Active Configuration,选择Release,然后再次如上配置一次。

  下面我们正式开始编程,将E:\tutorial\SDL-1.2.8\lib\SDL.dll拷贝到gp文件夹下,然后给gp工程增加一个C++文件main.cpp。如图9所示。


图9

  将以下代码拷贝到main.cpp并保存。

#include
#if defined(_MSC_VER)
#include "SDL.h"
#else
#include "SDL/SDL.h"
#endif

SDL_Surface *screen;

void render()

 // 独占资源,将surface 锁定
 if (SDL_MUSTLOCK(screen)) 
  if (SDL_LockSurface(screen) < 0) 
   return;

 // 获取当前时间,以毫秒计时
 int tick = SDL_GetTicks();

 // 声明变量
 int i, j, yofs, ofs;

 // 对窗口进行绘制
 yofs = 0;
 for (i = 0; i < 480; i++)
 {
  for (j = 0, ofs = yofs; j < 640; j++, ofs++)
  {
   ((unsigned int*)screen->pixels)[ofs] = i * i + j * j + tick;
  }
  yofs += screen->pitch / 4;
 }

 // 解除锁定
 if (SDL_MUSTLOCK(screen)) 
  SDL_UnlockSurface(screen);

 // 使用SDL对窗口进行更新
 SDL_UpdateRect(screen, 0, 0, 640, 480); 
}

// Entry point
int main(int argc, char *argv[])
{
 // 初始化SDL子系统,这里只对视频进行初始化
 if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) 
 {
  fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
  exit(1);
 }

 // 注册SDL_Quit,当退出时调用,使得退出时程序自动清理
 atexit(SDL_Quit);

 // 使用32位象素创建640x480的窗口
 screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

 // 若失败,则退出
 if ( screen == NULL ) 
 {
  fprintf(stderr, "Unable to set 640x480 video: %s\n", SDL_GetError());
  exit(1);
 }

 // 主循环
 while (1)
 {
  // Render stuff
  render();

  // SDL中的事件轮询机制
  SDL_Event event;
  while (SDL_PollEvent(&event)) 
  {
   //对消息进行处理
   switch (event.type) 
   {
    // 如果按下某键的消息响应
    case SDL_KEYDOWN: 
     break;
     //如果某键按下后弹起的消息响应
    case SDL_KEYUP:
     //若按下ESC键,则退出
     if (event.key.keysym.sym == SDLK_ESCAPE)
      return 0;
     break;
     //退出消息响应
    case SDL_QUIT:
     return(0);
   }
  }
 }
 return 0;
}

  对工程gp进行编译,可以看看动态水波纹的效果了:) 如图10所示。


图10

  如果你能够看到图10中的美丽动态窗口,那么恭喜你,你可以开始漫游SDL的精彩世界了。下面我们将正式开始我们的SDL之旅。

  简单的象素绘制

  下文从使用SDL的编程函数开始,介绍了如何使用SDL在屏幕上进行象素绘制的基本知识,并给出了一个简单的例子。

  如果要在VC中用SDL库,必须在源文件头部包含以下头文件:

#include “SDL.h”

  初始化SDL是通过SDL_Init()函数来实现的。如果初始化失败,函数返回值为0。函数只接受初始化对象作为参数。如果要初始化视频屏幕,传入常数SDL_INIT_VIDEO作为参数;初始化音频,传入常数SDL_INIT_AUDIO;如果同时初始化视频和音频,传入SDL_INIT_VIDEO|SDL_INIT_AUDIO。其它还有一些量可以传入作为参数的(如果同时传入多个量要使用|将它们隔开):

SDL_INIT_TIMER
SDL_INIT_AUDIO
SDL_INIT_VIDEO
SDL_INIT_CDROM
SDL_INIT_JOYSTICK
SDL_INIT_NOPARACHUATE
SDL_INIT_EVENTTHREAD
SDL_INIT_EVERYTHING

  如果我们要初始化,可以使用如下语句:

if ( SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0 )
{
 printf(“Unable to init SDL: %s\n”, SDL_GetError());
 return 1;
}

  如果初始化失败,则函数SDL_GetError()返回关于错误的一个字符串提示信息。

  当从你的C++语句返回时,记住使用SDL_Quit()进行程序的清理工作,否则就会出现奇怪的现象。可以使用如下语句进行描述:

atexit(SDL_Quit);

  这样的话就不需要在main函数中每个return语句前加入SDL_Quit()了。

  在SDL中你可以拥有多个surface,每件物体都是一个surface。你可以在一个surface上进行绘图或者在其他surface上绘制另外一个surface。程序中一个surface的表示即为指向结构SDL_Surface的指针。如果要获得一个surface只要如下定义:

SDL_Surface *screen;

  如果需要对screen所指向的surface上进行绘图,你可以使用函数SDL_SetVideoMode()来设置屏幕分辨率:

screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);

  前三个参数分别为屏幕宽度,高度和屏幕上的每象素包含的位数(bits per pixel, BPP)。如果填入0则SDL自动选择最合适的BPP。第四个参数用来给出某些特殊标志位。如果要在屏幕上进行图形绘制就必须使用SDL_HWSURFACE或者SDL_SWSURFACE。以下给出了一个你可以指定的标志位:

  SDL_SWSURFACE:在系统内存中创建视频Surface

  SDL_HWSURFACE:在视频内存中创建视频Surface

  SDL_ASYNCBLIT:允许在显示surface上使用异步更新。在单CPU机器上会变慢,但在SMP系统上会有显著的性能提升。

  SDL_ANYFORMAT:一般的,如果指定位数的bpp不可用,那么SDL就会模拟使用阴影surface。如果传入SDL_ANYFORMAT就会阻止这样做,并且不管色深强制使用视频surface。

  SDL_HWPALETTE:给予SDL特许的画盘的访问权,使用这个标志位就不需要总是使用SDL_SetColors或者SDL_SetPalette来获取所需的颜色。

  SDL_DOUBLEBUF:允许硬件双缓冲;只是和SDL_HWSURFACE一起使用时比较有用。调用SDL_Flip将会flip整个缓冲并且更新屏幕。所有的绘制将会在当前未显示的surface上发生。如果双缓冲被允许,那么SDL_Flip将会对整个屏幕进行SDL_UpdateRect操作。

  SDL_FULLSCREEN:SDL将会尝试使用全屏模式。如果硬件分辨率的调整由于某种情况无法完成,那么下一个稍高的分辨率将会被使用,并且显示窗口将会处于一个黑色背景的中央。

  SDL_OPENGL:创建一个OPENGL rendering context。使用前必须已经使用SDL_GL_SetAttribute对OpenGL视频属性进行设置。

  SDL_OPENGLBLIT: 和上一个选项一样创建一个 OPENGL rendering context, 但是允许使用正常的blitting操作。

  SDL_RESIZABLE; 创建一个可伸缩大小的窗口。当用户调整窗口大小时,将会触发一个SDL_VIDEORESIZE事件,SDL_SetVideoMode将会使用新大小作为参数再次被调用。

  SDL_NOFRAME: 如果可以的话,SDL_NOFRAME将会创建出一个没有标题栏和边界修饰的窗口,全屏方式自动设置此标志位。

  建议使用SDL_HWSURFACE | SDL_DOUBLEBUF,如果出现错误可以尝试使用SDL_SWSURFACE。

  SDL_SetVideoMode如果操作成功,则返回一个指向SDL_Surface的指针,否则的话返回NULL。可以使用如下语句检查发生的错误:

If ( screen == NULL )
{
 printf(“Unable to set 640x480 video: %s\n”, SDL_GetError());
 return 1;
}

  以上介绍了如何对SDL进行初始化,下面可以开始绘制了。但是还有一些需要注意的关键地方,首先是一些SDL使用的容易让人产生迷惑的数据类型:

Uint8 – 相当于unsigned char
Uint16 – 16位(2字节) unsigned integer
Uint32 – 32位(4字节) unsigned integer
Uint64 - 64位(8字节) unsigned integer
Sint8 – 相当于signed char
Sint16 – 16位(2字节) signed integer
Sint32 – 32位(4字节) signed integer
Sint64 - 64位(8字节) signed integer

  还有,有的时候如果初始化出现错误,没必要完全退出。例如当初始化了SDL_INIT_VIDEO而没有初始化SDL_INIT_AUDIO,那么你可以继续这个错误只是没有音频而已。要检查是否音频初始化是否成功,可以使用SDL_WasInit()函数来检查。下面是一个例子:

Uint32 init = SDL_WasInit(SDL_INIT_AUDIO);
If (init & SDL_INIT_AUDIO)
{
 sound = 1;
} else {
 sound = 0;
}

  你可以在程序初始化的某些地方加入以上语句。

  以下是SDL介绍中进行象素绘制的语句,可以暂时不需要完全了解其中的意思:

void DrawPixel(SDL_Surface *screen, int x, int y, Uint8 R, Uint8 G, Uint8 B) 

 Uint32 color = SDL_MapRGB(screen->format, R, G, B); 
 switch (screen->format->BytesPerPixel) 
 { 
  case 1: // Assuming 8-bpp 
  { 
   Uint8 *bufp; 
   bufp = (Uint8 *)screen->pixels + y*screen->pitch + x; 
   *bufp = color; 
  } 
  break; 
  case 2: // Probably 15-bpp or 16-bpp 
  { 
   Uint16 *bufp; 
   bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x; 
   *bufp = color; 
  } 
  break; 
  case 3: // Slow 24-bpp mode, usually not used 
  { 
   Uint8 *bufp; 
   bufp = (Uint8 *)screen->pixels + y*screen->pitch + x * 3; 
   if(SDL_BYTEORDER == SDL_LIL_ENDIAN) 
   { 
    bufp[0] = color;
    bufp[1] = color >> 8; 
    bufp[2] = color >> 16; 
   } else{ 
    bufp[2] = color; 
    bufp[1] = color >> 8; 
    bufp[0] = color >> 16; 
   } 
  } 
  break; 
  case 4: // Probably 32-bpp 
  { 
   Uint32 *bufp; 
   bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x; 
   *bufp = color; 
  } 
  break; 
 } 
}

  通过这个函数,可以传递给surface要绘制的点的(x,y)坐标和RGB值。

  同时,由于绘制的屏幕不能同时接受两个函数的同时操作,我们需要其他两个辅助函数,用于在绘制前对屏幕进行锁定,以及在绘制完成之后解除锁定。这两个工作一般由SDL_MUSTLOK(SDL_Surface *screen)和SDL_UnlockSurface(SDL_Surface *screen)完成。使用如下两个自定义的函数会更加简单:

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

  这样,我们可以得到一个简单的主程序框架如下:

#include
#include
#include "SDL.h"

// The functions are not shown to save space 
void DrawPixel(SDL_Surface *screen, int x, int y, Uint8 R, Uint8 G, Uint8 B); 
void Slock(SDL_Surface *screen); 
void Sulock(SDL_Surface *screen); 

int main(int argc, char *argv[]) 

 if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ) 
 { 
  printf("Unable to init SDL: %s\n", SDL_GetError()); 
  exit(1); 
 } 
 atexit(SDL_Quit); 
 SDL_Surface *screen; 
 screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF); 
 if ( screen == NULL ) 
 { 
  printf("Unable to set 640x480 video: %s\n", SDL_GetError()); 
  exit(1); 
 } 

 // DRAWING GOES HERE 
 return 0; 
}

  如果对该程序进行编译运行,那么只能得到一闪而过的一个黑色的窗口。我们需要对窗口进行绘制,并且对基本的键盘鼠标事件进行处理。

  绘制的基本原理是,先在缓冲区绘制,再一次性将缓冲区绘制到屏幕上。这样比起一次一个象素点在屏幕上绘图的方式效率更高,速度更快,也不易出错。首先使用循环在screen所指向的surface(缓冲区)上绘制,然后调用SDL_Flip函数将screen surface绘制到真实电脑屏幕上。SDL_Flip函数的作用是:在支持双缓冲(double-buffering)的硬件上,建立flip并返回。硬件将等待vertical retrace,然后在下一个视频surface blit或者执行锁定返回前交换视频缓冲区。如果硬件不支持双缓冲,那么等同于调用SDL_UpdateRect(screen, 0, 0, 0, 0),即对整个screen的绘制区域进行刷新。

void DrawScene(SDL_Surface *screen) 

 Slock(screen); 
 for(int x=0;x<640;x++) 
 { 
  for(int y=0;y<480;y++) 
  { 
   DrawPixel(screen, x,y,y/2,y/2,x/3); 
  } 
 } 
 Sulock(screen); 
 SDL_Flip(screen); 
}

  在SDL中对采用结构SDL_Event来描述事件,并采用轮询的机制对事件进行处理,程序中使用一个SDL_Event的实例(Instance)来检查事件的发生:

SDL_Event event;

  轮询采用while循环来检查:

while ( SDL_PollEvent(&event))
{
 if ( event.type == SDL_QUIT) 
 {
  //code here….
 }
 if ( event.type == SDL_KEYDOWN) 
 {
  //code here….
 }
 //…..
}

  事件轮询完毕之后,调用DrawScene(sreen)进行一次绘制。

  本例中的源代码如下:

#include
#include
#include "SDL.h"

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);
 }
}
void DrawPixel(SDL_Surface *screen, int x, int y,
Uint8 R, Uint8 G, Uint8 B)
{
 Uint32 color = SDL_MapRGB(screen->format, R, G, B);
 switch (screen->format->BytesPerPixel)
 {
  case 1: // Assuming 8-bpp
  {
   Uint8 *bufp;
   bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;
   *bufp = color;
  }
  break;
  case 2: // Probably 15-bpp or 16-bpp
  {
   Uint16 *bufp;
   bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;
   *bufp = color;
  }
  break;
  case 3: // Slow 24-bpp mode, usually not used
  {
   Uint8 *bufp;
   bufp = (Uint8 *)screen->pixels + y*screen->pitch + x * 3;
   if(SDL_BYTEORDER == SDL_LIL_ENDIAN)
   {
    bufp[0] = color;
    bufp[1] = color >> 8;
    bufp[2] = color >> 16;
   } else {
    bufp[2] = color;
    bufp[1] = color >> 8;
    bufp[0] = color >> 16;
   }
  }
  break;
  case 4: // Probably 32-bpp
  {
   Uint32 *bufp;
   bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;
   *bufp = color;
  }
  break;
 }
}
void DrawScene(SDL_Surface *screen)
{
 Slock(screen);
 for(int x=0;x<640;x++)
 {
  for(int y=0;y<480;y++)
  {
   DrawPixel(screen, x,y,y/2,y/2,x/3);
  }
 }
 Sulock(screen);
 SDL_Flip(screen);
}

int main(int argc, char *argv[])
{
 if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )
 {
  printf("Unable to init SDL: %s\n", SDL_GetError());
  exit(1);
 }
 atexit(SDL_Quit);
 SDL_Surface *screen;
 screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF);
 if ( screen == NULL )
 {
  printf("Unable to set 640x480 video: %s\n", SDL_GetError());
  exit(1);
 }
 int done=0;
 while(done == 0)
 {
  SDL_Event event;
  while ( SDL_PollEvent(&event) )
  {
   if ( event.type == SDL_QUIT ) { done = 1; }
   if ( event.type == SDL_KEYDOWN )
   {
    if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }
   }
  }
  DrawScene(screen);
 }
 return 0;
}

  程序运行结果如下:


  打开和显示图片

  在这个例子里我们将学习如何使用SDL程序中打开和显示图片。示例程序将绘制一个漂亮的背景,上面显示一个正方形图案,并可以使用键盘的方向键移动它。如果比较熟悉“推箱子”这个游戏,可以看出这个程序实际就是推箱子游戏的基础。

  在程序首部包含以下头文件(stdlib.h供调用atexit()时使用):

#include
#include
#include “SDL.h”

  声明3个SDL_Surface类型的全局变量,同时声明2个整型变量用于记录正方形的坐标:

SDL_Surface *back;
SDL_Surface *image;
SDL_Surface *screen;

int xpos = 0, ypos = 0;

  我们构造一个函数InitImage函数来打开bitmap文件(.bmp)中的图像信息,传递给SDL_Surface显示。InitImage将会被main()函数调用。在InitImage函数中我们使用到了SDL_LoadBMP函数,它把bmp文件的文件名作为参数传入,返回指向存储图像文件信息内存区域的指针。InitImage中我们将背景图片bg.bmp打开并使用back指针进行记录,将正方形图片image.bmp打开并使用image指针记录。

int InitImages() 

 back = SDL_LoadBMP("bg.bmp"); 
 image = SDL_LoadBMP("image.bmp"); 
 return 0; 
}

  下面我们介绍将图像绘制到屏幕的两个函数,两个函数都被命名为DrawIMG。第一个DrawIMG函数使用SDL_BlitSurface函数来在屏幕上显示图像。在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)这两个变量是被忽略不计的。第一个DrawIMG函数非常简单:

void DrawIMG(SDL_Surface *img, int x, int y) 

 SDL_Rect dest; 
 dest.x = x; 
 dest.y = y; 
 SDL_BlitSurface(img, NULL, screen, &dest); 
}

  下面我们考虑一个复杂点的情况,如图:


  如果我们需要将阴影区域传递给srcrect进行绘制,我们就需要使用到srcrect结构里的几个变量了:x,y指定了所要绘制的源区域的起点坐标,而w和h分别指定了源区域的宽度和高度。如果图中的阴影坐标起点坐标为(20,25),宽61个象素高70个象素,那么我们可以得到:srcrect中x = 20, y = 25, w = 61, h = 70。

  第二个DrawIMG定义如下:

void DrawIMG(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); 
}

  绘制背景的函数DrawBG()比较简单:

void DrawBG()
{
 Slock(screen);
 DrawIMG(back, 0, 0);
 Sulock(screen);
}

  绘制正方形图像的函数将会比较复杂,首先我们用背景图案填充当前正方形图像所在区域,如果不这样做的话,正方形图像的移动就会造成背景上留下黑色的方形移动轨迹,如图所示:


  这里,我们只填充正方形图像移动后的轨迹区域,这样比填充整个区域速度快。由于正方形区域大小是128x128,由于每次正方形只能朝某个方向移动1个象素(pixel),为了彻底消除移动轨迹,我们背景的新填充区域扩大到132x132,这样就可以完全覆盖由于移动造成的轨迹残留。最后使用SDL_Flip对新的图像绘制区域进行更新。函数如下:

void DrawScene()
{
 Slock(screen);
 DrawIMG(back, xpos-2, ypos-2, 132, 132, xpos-2, ypos-2);
 DrawIMG(image, xpos, ypos);

 SDL_Flip(screen);
 Sulock(screen);
}

  由于要移动正方形图像,我们需要对键盘的方向键的按下事件进行响应,因此在main函数开始时定义:

Uint8* keys;

  keys用来获得每一时间的键盘状态。获得键盘状态的函数为SDL_GetKeyState(),它返回一个指向Uin8类型的数组头部的指针。数组的每个元素都对应记录了某个按键是否被按下的标志。这里的实现,我们不在事件轮询SDL_PollEvent中检查按键,因为事件轮询中是只有触发事件也即SDL_PollEvent(&event) == 1才能进入循环的,因而如果我们一直按下某键不放开是不会触发新的事件发生的,要使得正方形移动我们必须一下又一下的敲击某个方向键,显示这不是我们所要的。我们希望是按下某键不放开的话,正方形一直保持向此方向移动。因此我们将检查按键的程序段放到事件轮询之后处理。这里没有使用else if…结构,因此可以多个方向键同时按下进行移动,程序段如下:

int done=0;

while(done == 0)
{
 SDL_Event event;

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

  if ( event.type == SDL_KEYDOWN )
  {
   if ( event.key.keysym.sym == SDLK_ESCAPE ) { 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();
}

  程序运行结果如下(我们已经将正方形图像区域移动到了窗口中央):


  如果想学习本程序的完整代码,可以从以下地址下载:http://cone3d.gamedev.net/cgi-bin/index.pl?page=tutorials/gfxsdl/download.pl?file=lesson2.zip&blah=1

  以上我们简单介绍了VC6下SDL的安装和简单应用,并举了两个小例子,当然SDL的功能远远不止这些,包括音频、定时器和线程编程等等,这里就不一一鳌数了。如果有兴趣,SDL官方网站提供了非常详尽的函数说明、文档和使用指南,你可以在网站上获取非常多的信息。由于SDL是跨平台和开发工具的,方便游戏在各种平台下的移植,同时还支持常用的各种开发语言。因此,有足够的理由相信,在游戏产业蓬勃发展的今天,SDL一定会得到更多开发者的青睐,用它开发出各种有趣的游戏来。 
阅读(5825) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~