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头文件,这个会在后续章节讲述。

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

  • 相关阅读:
    排序之插入排序
    swfupload在chrome中点击上传图片按钮无反应的解决办法
    ASP.NET网站限制访问频率
    SQL 合并列值和拆分列值
    替换字符串第一次出现的某个字符
    HttpWebResponse远程服务器返回错误: (500) 内部服务器错误
    撒列实现关键字过虑
    sql关键字过滤C#方法
    WebRequest 对象的使用
    Request 分别获取具有相同 name 属性表单元素值
  • 原文地址:https://www.cnblogs.com/landmark/p/2496205.html
Copyright © 2011-2022 走看看