zoukankan      html  css  js  c++  java
  • 裁剪精灵图片

           如果我们要做一个游戏,会涉及到大量的图片,如果这些图片都用单个文件保存,那程序里会加载大量的图片,会有很多表面,处理起来很麻烦,一般会把游戏里使用的图片分类,一类图片保存到一个大图片里,使用的时候在按要求裁剪出需要的部分,这张大图就叫精灵图,裁剪出来的部分叫精灵。

          下面我们做一个简单的例子,来演示一下如何裁剪精灵图,需要的图片有两张,第一张是背景图,随便用什么都可以,第二张是精灵图。我们会让精灵在地图上走动。

    spiritbackground spirit

            精灵图里是一个小人各种样子,小人大小都是一样的,图片大小为96*192,所以可以计算出每个小人(精灵)大小为32*48,这样我们就可以按照这个比例抠出精灵。

    SDL_Surface *gpScreen;//显示表面 
    SDL_Surface *gpBackGround;//背景表面 
    SDL_Surface *gpSpirit;//精灵表面
    
    
    //显示背景图片 
    
    gpBackGround = loadImage("background.jpg"); 
    SDL_BlitSurface(gpBackGround,NULL,gpScreen,NULL); 
    SDL_Flip(gpScreen);
    
    //抠出第1副精灵的图 
    SDL_Rect    src[4][3];
    
    src[0][0].x = src[0][0].y = 0;//精灵图左上角坐标 
    src[0][0].w = SPIRITWIDTH; //精灵的大小 
    src[0][0].h = SPIRITHEIGH; 
    SDL_BlitSurface(gpSpirit,&src[0],gpScreen,NULL);//在屏幕左上角显示第一个精灵 
    SDL_Flip(gpScreen);

           我们定义了一个SDL_Rect类型的数组src[4][3],每一个数组元素代表了一个精灵在大图上的位置,数组的每一行代表了图片上的每一行图片,然后设置src[0][0]各个成员的值,然后把gpSpirit传输到显示表面上,不过这次我们不是将整个表面传输,而是传输src[0][0]指示的大小,所以在屏幕上显示的不是整个精灵大图,而是由src[0][0]指示的第一个精灵的图。精灵图的裁剪就是这么简单。运行效果:

    image

          但精灵图的黄色背景也会显示,这会很难看,如何去除这个背景色呢?SDL中有一个函数可以设定图像的指定颜色透明。

         int SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key);

         参数:surface是指设定的是那个表面;

                   flag的值如下表所示

    flag值

    含义

    0 取消colorkey 功能
    SDL_SRCCOLORKEY 以 key 为透明色。我们可以用  SDL_MapRGB() 来求得与 surface 的像素格式相符的颜色值。
    SDL_RLEACCEL 用RLE (Run Length Encoding) 的方式来提高 Blit 的效率。

         其中key是要设定为透明的颜色,使用SDL_MapRGB()来取,为什么不能直接指定RGB颜色值呢,因为表面的颜色格式和RGB颜色格式不同,所以必须使用这个函数来返回将RGB颜色值转换成表面格式的颜色值。

         Uint32 SDL_MapRGB(SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b);

         参数:fmt指定表面;r、g、b是颜色值,你可以通过画图软件取得指定图像的颜色值。

       有了这两个函数,我们就可以将精灵大图gpSpirit的背景颜色设成透明,显示的时候就没有背景色了。修改前面的代码:

    gpBackGround = loadImage("background.jpg");    
    SDL_BlitSurface(gpBackGround,NULL,gpScreen,NULL); 
    SDL_Flip(gpScreen);    
    gpSpirit = loadImage("spirit.bmp");//载入精灵大图 
    
    colorkey= SDL_MapRGB(gpSpirit->format, 255, 238, 187); 
    SDL_SetColorKey(gpSpirit, SDL_SRCCOLORKEY , colorkey ); 

    //抠出第1副精灵的图

    SDL_Rect    src[4][3];
    src[0][0].x = src[0][0].y = 0;//精灵图左上角坐标 src[0][0].w = SPIRITWIDTH; //精灵的大小 src[0][0].h = SPIRITHEIGH; SDL_BlitSurface(gpSpirit,&src[0][0],gpScreen,NULL);//在屏幕左上角显示第一个精灵 SDL_Flip(gpScreen);

      运行后显示效果:

    image

      那么你是否还记得我们讲过的键盘事件检测?如果你会了键盘事件检测,我们可以做些有意思的尝试了,我们看一下精灵大图,会发现第一排小人是小人从上往下走的不同姿势,第二排从右往左走,第三排是从左往右走,第四排是从下往上走;那么我们是否可以通过键盘按键来让小人走路,转向呢?

      我们首先实现小人直行,要一直往前走,要不停的切换同一排小人图片,比如让小人从上往下走,第一幅图示src[0][0],下一步图示src[0][1],在走一步是src[0][2],那么我们可以设置一个变量step来标示下一步要出现的图片,其初值为0。

      1 /*
      2   功能:演示精灵图裁剪
      3   作者:csl
      4   日期:2012-5-11
      5 */
      6 #include <stdio.h> 
      7 #include <stdlib.h> 
      8 #include "SDL.h"
      9 
     10 #include <windows.h>
     11 
     12 
     13 //屏幕尺寸
     14 #define SCREENWIDTH  640
     15 #define SCREENHEIGH  480
     16 #define BPP  32
     17 
     18 //精灵尺寸
     19 #define SPIRITWIDTH  32
     20 #define SPIRITHEIGH  48
     21 
     22 SDL_Surface *gpScreen;//显示表面
     23 SDL_Surface *gpBackGround;//背景表面
     24 SDL_Surface *gpSpirit;//精灵表面
     25 
     26 SDL_Event myEvent;//事件
     27 
     28 SDL_Surface *loadImage(char *aFilename);
     29 char *localeToUTF8(char *src);
     30 
     31 int main(int argc,char *argv[])
     32 {
     33     int quit = 0;
     34     char caption[20]={""};
     35     SDL_Rect src[4][3];
     36     SDL_Rect dst;
     37     Uint32 colorkey;
     38     int spiritX = 0;//小球的初始坐标
     39     int spiritY = 0;
     40     int speed = 5;
     41     int step = 0;//步伐
     42     int i,j;
     43     
     44 
     45     if((SDL_Init(SDL_INIT_VIDEO)==-1)) //初始化视频子系统
     46     {
     47         printf("Unable to init SDL: %s\n", SDL_GetError());
     48         exit(-1);
     49     }
     50     atexit(SDL_Quit);// 注册SDL_Quit,当退出时调用,使得退出时程序自动清理
     51 
     52     //创建32位600*480窗口
     53     gpScreen = SDL_SetVideoMode(SCREENWIDTH,SCREENHEIGH, BPP, SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF );
     54     if(!gpScreen) 
     55     { 
     56         exit(1);
     57     }
     58     printf("%s\n",caption);
     59     SDL_WM_SetCaption(localeToUTF8("兵棋"),NULL);
     60 
     61     //SDL_WM_SetCaption(caption,NULL);
     62     SDL_EnableKeyRepeat(500,30);//起动粘连键
     63 
     64     gpBackGround = loadImage("background.jpg");    
     65     SDL_BlitSurface(gpBackGround,NULL,gpScreen,NULL);
     66     SDL_Flip(gpScreen);    
     67     gpSpirit = loadImage("spirit.bmp");//载入精灵大图
     68 
     69     colorkey= SDL_MapRGB(gpSpirit->format, 255, 238, 187);
     70     SDL_SetColorKey(gpSpirit, SDL_SRCCOLORKEY , colorkey );        
     71 
     72     //设定每一个精灵在精灵大图上的位置
     73     for (i = 0;i<4;i++)
     74     {
     75         for (j = 0;j<3;j++)
     76         {
     77             src[i][j].x = j*SPIRITWIDTH;
     78             src[i][j].y = i*SPIRITHEIGH;
     79             src[i][j].w = SPIRITWIDTH;
     80             src[i][j].h = SPIRITHEIGH;
     81         }
     82     }
     83 
     84     //抠出第1副精灵的图
     85     dst.x=dst.y = 0;
     86     SDL_BlitSurface(gpSpirit,&src[0][0],gpScreen,&dst);//在屏幕左上角显示第一个精灵
     87     SDL_Flip(gpScreen);
     88 
     89     while (!quit)
     90     {
     91         while (SDL_PollEvent(&myEvent))
     92         {
     93             switch (myEvent.type)
     94             {
     95             case SDL_QUIT:
     96                 quit = 1;
     97                 break;
     98             case SDL_KEYDOWN:
     99                 switch(myEvent.key.keysym.sym)
    100                 {
    101                 case SDLK_DOWN:
    102                     spiritY+=speed;
    103                     step++;
    104                     spiritY = (spiritY+SPIRITHEIGH)>SCREENHEIGH?(SCREENHEIGH-SPIRITHEIGH):spiritY;
    105                     break;
    106                 case SDLK_MINUS:
    107                     speed--;
    108                     speed=speed>0?speed:0;
    109                 case SDLK_EQUALS:
    110                     speed++;
    111                 }
    112                 
    113                 step %=3;
    114                 SDL_BlitSurface(gpBackGround,NULL,gpScreen,NULL);
    115                 dst.x = spiritX;
    116                 dst.y = spiritY;
    117                 dst.w = SPIRITWIDTH;
    118                 dst.h = SPIRITHEIGH;
    119                 SDL_BlitSurface(gpSpirit,&src[0][step],gpScreen,&dst);
    120                 SDL_Flip(gpScreen);
    121                 break;
    122             case SDL_KEYUP:
    123                 switch(myEvent.key.keysym.sym)
    124                 {
    125                 case SDLK_LEFT:                            
    126                     printf("小人的坐标:(%d,%d)\n",spiritX,spiritY);
    127                     break;
    128                 case SDLK_RIGHT:
    129                     printf("小人的坐标:(%d,%d)\n",spiritX,spiritY);
    130                     break;
    131                 case SDLK_UP:
    132                     printf("小人的坐标:(%d,%d)\n",spiritX,spiritY);
    133                     break;
    134                 case SDLK_DOWN:                        
    135                     printf("小人的坐标:(%d,%d)\n",spiritX,spiritY);
    136                     break;
    137                 }
    138                 break;
    139             }            
    140         }
    141     }
    142     
    143     //system("pause"); 
    144     return 0; 
    145 }
    146 
    147 
    148 /*--------------------------------------------------------------------
    149     函数名:    loadImage
    150     参  数:    char *filename  图像文件的名字
    151     返回值: SDL_Surface * 返回指向图像表面的指针
    152     功  能:    载入图像
    153     备  注:
    154 ----------------------------------------------------------------------*/
    155 SDL_Surface *loadImage(char *aFilename)
    156 {
    157     SDL_Surface* loadedImage = NULL;
    158     SDL_Surface* optimizedImage = NULL;
    159 
    160     //载入图像
    161     loadedImage = IMG_Load( aFilename);
    162 
    163     if( NULL != loadedImage )//If the image loaded
    164     {
    165         //创建优化图像
    166         optimizedImage = SDL_DisplayFormat( loadedImage );
    167 
    168         //释放loadImage
    169         SDL_FreeSurface( loadedImage );
    170     }
    171     return optimizedImage;
    172 }
    173 
    174 /*--------------------------------------------------------------------
    175     函数名:    loadImage
    176     参  数:    char *src  中文字符串
    177     返回值: char * UTF8字符串
    178     功  能:    将汉字转换成UTF8字符串
    179     备  注:
    180 ----------------------------------------------------------------------*/
    181 char *localeToUTF8(char *src)
    182 {
    183     static char *buf = NULL;
    184     wchar_t *unicode_buf;
    185     int nRetLen;
    186 
    187     if(buf){
    188         free(buf);
    189         buf = NULL;
    190     }
    191     nRetLen = MultiByteToWideChar(CP_ACP,0,src,-1,NULL,0);
    192     unicode_buf = (wchar_t*)malloc((nRetLen+1)*sizeof(wchar_t));
    193     MultiByteToWideChar(CP_ACP,0,src,-1,unicode_buf,nRetLen);
    194     nRetLen = WideCharToMultiByte(CP_UTF8,0,unicode_buf,-1,NULL,0,NULL,NULL);
    195     buf = (char*)malloc(nRetLen+1);
    196     WideCharToMultiByte(CP_UTF8,0,unicode_buf,-1,buf,nRetLen,NULL,NULL);
    197     free(unicode_buf);
    198     return buf;
    199 }

      第73-82行将每个小人的位置存储到src数组里,这样我们显示图像就不会错了,第101行我们检测了是否按下了↓键,如果按下了,我们把step的值加1,显示下一步的图片,但向下行走只有三幅图片,所以当step等于3时,应当将其置成0,这一步在第113行用了一个模运算完成,接下来就是定位将图片显示在屏幕上的位置,这个在键盘检测教程就已经说了,这不在累述,然后显示。运行结果可以看到很像一个人在行走。  

      现在,程序还有一点问题,就是不能转向,我们可以再设一个变量direct表示方向,其值为0表示向下,1向左,2向右,3向上,并且direct的值对应src行下标,然后再增加对其他三个方向的检测就可以了。另外我们在这个教程里设置了应用程序的名字并且名字是中文,所以需要加载windows.h头文件,这个会在后续章节讲述。

      这个程序没有对小人进行碰撞检测,所以小人遇树撞树,以后会做碰撞检测的讲解,完整的代码请点击这儿下载。

  • 相关阅读:
    什么是 bean 的自动装配?
    什么是 Spring 的内部 bean?
    什么是 Spring 的 MVC 框架?
    Spring AOP and AspectJ AOP 有什么区别?
    解释 JDBC 抽象和 DAO 模块?
    volatile 类型变量提供什么保证?
    一个 Spring Bean 定义 包含什么?
    什么是 Spring MVC 框架的控制器?
    使用 Spring 访问 Hibernate 的方法有哪些?
    什么是 Callable 和 Future?
  • 原文地址:https://www.cnblogs.com/landmark/p/2496205.html
Copyright © 2011-2022 走看看