如果我們在SDL程序中需要每個一個固定時間去處理一件事情,那么我們需要一個定時器,就像生活中鍾表一樣可以整點報時,這個功能就是SDL的定時器子系統,你要使用SDL的定時器必須初始化定時器子系統,請看《SDL起動與退出》,SDL定時器比較簡單就是一個間隔固定時間的函數調用,如果你要添加一個定時器可以使用函數SDL_AddTimer,其原型為:
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param);
其中:函數的返回值SDL_TimerID是一個結構體指針,SDL文檔中並沒有給出其結構,所以我們只要知道這是一個指向添加的定時器的指針就可以了。
函數的第一個參數interval是定時器間隔的時間,以毫秒(1秒=1000毫秒)為單位;第二個參數是要執行的函數,也稱回調函數,是定時器每個interval時間段調用的函數,不是我們自己調用的,SDL_NewTimerCallback是一個函數指針類型,指明了要調用的函數原型:
typedef Uint32 (SDLCALL *SDL_NewTimerCallback)(Uint32 interval, void *param);
要調用的函數的原型滿足返回值是Uint32,函數的第一個參數是定時器的時間間隔(一般和SDL_AddTimer的interval一樣),第二個參數是需要給函數傳遞的參數,不需要傳參的話可以寫NULL。SDL_AddTimer第三個參數也是同樣的。
下面我們做一個SDL電子表,顯示當前時間,所需圖片如下圖所示:
所需圖片 | 程序效果 |
![]() |
|
程序代碼如下:
1 /* 2 功能:Timer的用法 3 作者:csl 4 日期:2012-5-24 5 */ 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <time.h> 9 #include <string.h> 10 #include <SDL.h> 11 12 //窗口長和寬 13 #define SCREENWIDTH 280 14 #define SCREENHEIGH 40 15 #define BPP 32 16 17 //圖片長和寬 18 #define DIGITALWIDTH 13 19 #define DIGITALHEIGH 23 20 21 SDL_Surface *gpScreen;//顯示表面 22 SDL_Surface *gpDigitals;//數字的精靈圖 23 SDL_Event myEvent;//事件 24 25 //時間定時器 26 SDL_TimerID myTime; 27 const unsigned int INTERVALS = 1000;//1秒 28 29 SDL_Surface *loadImage(char *aFilename); 30 void cleanUp(); 31 int cToD(char ch); 32 SDL_Rect locatePosition(char ch); 33 Uint32 displayTime(Uint32 interval, void *param); 34 35 int main(int argc,char *argv[]) 36 { 37 int quit = 0; 38 SDL_Rect src,dst; 39 40 if((SDL_Init(SDL_INIT_EVERYTHING)==-1)) //初始化視頻子系統 41 { 42 printf("Unable to init SDL: %s\n", SDL_GetError()); 43 exit(-1); 44 } 45 atexit(cleanUp);// 注冊cleanUp,當退出時調用,使得退出時程序自動清理 46 47 //創建32位600*480窗口 48 gpScreen = SDL_SetVideoMode(SCREENWIDTH,SCREENHEIGH, BPP, SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF ||SDL_NOFRAME ); 49 if(!gpScreen) 50 { 51 exit(1); 52 } 53 gpDigitals = loadImage("jpg\\digitals.jpg"); 54 //displayTime(1000,0); 55 56 myTime = SDL_AddTimer(INTERVALS,displayTime,NULL); 57 if (!myTime) 58 { 59 exit(0); 60 } 61 62 while (!quit) 63 { 64 while (SDL_PollEvent(&myEvent)) 65 { 66 switch (myEvent.type) 67 { 68 case SDL_QUIT: 69 quit = 1; 70 break; 71 } 72 } 73 } 74 75 system("pause"); 76 return 0; 77 }
要使用定時器,我們首先要定義一個SDL_TimerID myTime;值得注意的是myTime是一個指針,在主函數里初始化子系統的時候要初始化定時器子系統,所以我們的初始化SDL_Init(SDL_INIT_EVERYTHING),是初始化所有的SDL子系統,然后要首先添加一個定時器,在56行我們添加了定時器,如果不成功則要結束程序。添加以后每隔1秒也就是1000毫秒就會調用回調函數displayTime來顯示時間。displayTime代碼如下:
/*-------------------------------------------------------------------- 函數名: displayTime 參 數: interval定時器的時間間隔; param需要傳遞給displayTime函數的參數 返回值: 下一次要調用該函數的時間間隔 功 能: 定時器回調函數,每隔interval時間段被系統調用 備 注: ----------------------------------------------------------------------*/ Uint32 displayTime(Uint32 interval, void *param) { struct tm *currentTime; SDL_Rect src; SDL_Rect dst = {(SCREENWIDTH-19*DIGITALWIDTH)/2,(SCREENHEIGH-DIGITALHEIGH)/2,DIGITALWIDTH,DIGITALHEIGH}; char strTime[20]; int i,len; time_t t = time(NULL);//取得當前系統時間 //取得當前時間,並按格式轉換成字符串 currentTime = localtime(&t);//將t轉換成當地時間 strftime(strTime,20,"%Y-%m-%d %H:%M:%S",currentTime); //在屏幕中央顯示系統時間 for (i = 0;strTime[i]!='\0';i++) { src = locatePosition(strTime[i]); SDL_BlitSurface(gpDigitals,&src,gpScreen,&dst); dst.x+=DIGITALWIDTH; } SDL_Flip(gpScreen); return INTERVALS; }
在函數里,我們首先要取得當前系統時間,其中time_t是c語言標准庫里定義的,是一個整型類型,C語言里用這個類型的變量保存系統時間,實際上保存的是按unix時間格式保存自1970年1月1日0時0分0秒起至現在的總秒數,這個我們很難看懂,所以我們使用了另外一種描述時間的結構struct tm:
struct tm { int tm_sec; /* 秒–取值區間為[0,59] */ int tm_min; /* 分 - 取值區間為[0,59] */ int tm_hour; /* 時 - 取值區間為[0,23] */ int tm_mday; /* 一個月中的日期 - 取值區間為[1,31] */ int tm_mon; /* 月份(從一月開始,0代表一月) - 取值區間為[0,11] */ int tm_year; /* 年份,其值從1900開始 */ int tm_wday; /* 星期–取值區間為[0,6],其中0代表星期天,1代表星期一,以此類推 */ int tm_yday; /* 從每年的1月1日開始的天數–取值區間為[0,365],其中0代表1月1日,1代表1月2日,以此類推 */ int tm_isdst; /* 夏令時標識符,實行夏令時的時候,tm_isdst為正。不實行夏令時的進候,tm_isdst為0;不了解情況時,tm_isdst()為負。*/ };
我們需要把time_t格式的時間轉換成struct tm表示的時間,這個轉換函數就是localtime,轉換完成后我們就可以得到當前時間了,strftime函數可以將strcut tm時間轉換成一個字符串,並且可以指定轉換格式:
%a |
星期的縮略形式 |
%A |
星期的完整形式 |
%b |
月份的縮略形式 |
%B |
月份的完整形式 |
%c |
月份的縮略形式 |
%d |
月中的第幾天(1-31) |
%H |
小時, 24小時格式 (0-23) |
%I |
小時, 12小時格式 (1-12) |
%j |
年中的第幾天(1-366) |
%m |
月份 (1-12). Note: 某些版本的Microsoft Visual C++ 可能使用取值范圍0-11. |
%M |
分鍾(0-59) |
%p |
本地時間的上午或下午(AM or PM) |
%S |
秒鍾(0-59) |
%U |
年中的第幾周,星期天是一周的第一天 |
%w |
星期幾的數字表示(0-6, 星期天=0) |
%W |
一年中的第幾周,星期天是一周的第一天 |
%x |
標准日期字符串 |
%X |
標准時間字符串 |
%y |
年(0-99) |
%Y |
用CCYY表示的年(如:2004) |
%Z |
時區名 |
%% |
百分號 |
轉換完成后我們得到一個字符格式的當前日期,比如說:2012-05-24 7:47:23,這個串里的每一個字符對對應精靈圖里的一個精靈,所以我們就可以根據字符取得它在精靈圖里的位置,然后將它顯示在窗口上,我們是通過locatePosition函數取得位置的,它的代碼如下:
/*-------------------------------------------------------------------- 函數名: locatePosition 參 數: ch需要確定位置的字符 返回值: 字符在精靈圖中的位置 功 能: 計算字符在精靈圖中的位置 備 注: ----------------------------------------------------------------------*/ SDL_Rect locatePosition(char ch) { int y; SDL_Rect tmp; switch(ch) { case '-': tmp.y=0; break; case ' ': tmp.y=DIGITALHEIGH; break; case ':': tmp.y = 2 * DIGITALHEIGH ; break; default: tmp.y=(12-cToD(ch))*DIGITALHEIGH; } tmp.x = 0; tmp.w = DIGITALWIDTH; tmp.h = DIGITALHEIGH; return tmp; }
每一副精靈圖其x坐標都是0,y坐標可以分門別類加以列舉,每一副精靈圖的長和寬固定,最后返回一個矩形變量(SDL_Rect)就可以了 。
我們取得了字符的精靈圖,在計算它應該顯示在屏幕的位置就可以了,然后將其傳輸到顯示表面上顯示就可以了,顯示完一副圖片后將dst的x坐標加上圖片寬度就是下一副圖片的位置。
這樣我們就可以每隔1秒鍾顯示下當前的系統時間了。最后不要忘記調用SDL_RemoveTimer(myTime)移除定時器。
例子的完整代碼請點擊這兒下載。
各位看官,如果你覺得那里不清楚請給回復,如果你覺得還可以請加關注,加粉絲,謝謝!總之有點反映。