




已閱讀5頁,還剩9頁未讀, 繼續(xù)免費閱讀
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
SDL入門教程(十三):1、多線程,從動畫說起 作者:龍飛1.1:簡單動畫游戲離不開動畫。我們考慮最簡單的情況:將一個角色從一個位置移動到另外一個位置。這個行為表述給電腦就是,將一個surface不斷的blit(),從起始位置的坐標,移動到結束位置的坐標。移動速度取決于每次blit()的坐標差和blit()的時間間隔(v = ds/dt )。我們來設計一個函數實現這個簡單的動畫。我們需要的數據有:起始坐標(int beginX, int beginY),結束坐標(int endX, int endY),以及作為SDL基礎的ScreenSurface窗口(const ScreenSurface& screen)。一般的考慮是,將這5個數據以參數的方式傳入函數;但是一種更加通用一點的方式是,將這5種數據合并成一個結構,這樣函數的參數形式會更加的統一,這正是觸發(fā)多線程的函數所需要的。在SDL中,我們通過函數:SDL_Thread *SDL_CreateThread(int (*fn)(void *), void *data);觸發(fā)多線程,其中所需要的函數指針形式為:typedef int (*fn)(void*);而void*類型的data就是函數(*fn)()需要的的數據。我們可以將任意的結構體指針,轉化為void*,作為這個函數的第二個參數需要。因此,我們可以為我們需要的動畫函數定義一個結構作為傳遞所有數據的載體:struct AmnArgint beginX;int beginY;int endX;int endY;const ScreenSurface& screen;AmnArg(int begin_x, int begin_y, int end_x, int end_y, const ScreenSurface& _screen): beginX(begin_x), beginY(begin_y), endX(end_x), endY(end_y), screen(_screen);這樣,我們可以將AmnArg對象的指針傳遞給動畫函數考慮到多線程函數的需要,我們再曲折一點:先將AmnArg*轉換成void*傳遞給函數,在函數內部再將其轉換回來以供調用。int amn(void* data)AmnArg* pData = (AmnArg*)data;PictureSurface stand(./images/am01.png, pData-screen);stand.colorKey();PictureSurface bg(./images/background.png, pData-screen);const int SPEED_CTRL = 300;int speedX = (pData-endX - pData-beginX) / SPEED_CTRL;int speedY = (pData-endY - pData-beginY) / SPEED_CTRL;for ( int i = 0; i beginX += speedX;pData-beginY += speedY;bg.blit(pData-beginX, pData-beginY, pData-beginX, pData-beginY, stand.point()-w, stand.point()-h, 2, 2);stand.blit(pData-beginX, pData-beginY);pData-screen.flip();return 0;注意:我們這里僅僅設定了每次blit()的位移差(ds)而沒有設定時間差(dt)。這并不意味著dt = 0,事實上,電腦處理數據是需要時間的,包括運算和顯示。我們這里事實上將dt的設定交給了電腦,也就是說,讓電腦以其最快的速度來完成。為什么要這么做呢?這是為了演示多線程的一個現象,賣個關子,后面解釋。:)1.2:動畫函數在主程序中的調用#include SurfaceClass.hpp#include amn.hppint main(int argc ,char* argv)/Create a SDL screen.const int SCREEN_WIDTH = 640;const int SCREEN_HEIGHT = 480;const Uint32 SCREEN_FLAGS = 0; /SDL_FULLSCREEN | SDL_DOUBLEBUF | SDL_HWSURFACEconst std:string WINDOW_NAME = Amn Test;ScreenSurface screen(SCREEN_WIDTH, SCREEN_HEIGHT, WINDOW_NAME, 0, SCREEN_FLAGS);PictureSurface bg(./images/background.png, screen);bg.blit(0);screen.flip();AmnArg test1(0, 250, 600, 250, screen);amn(void*)&test1);SDL_Event gameEvent;bool gameOver = false;while ( gameOver = false )while ( SDL_PollEvent(&gameEvent) != 0 )if ( gameEvent.type = SDL_QUIT )gameOver = true;if ( gameEvent.type = SDL_KEYDOWN )if ( gameEvent.key.keysym.sym = SDLK_ESCAPE )gameOver = true;screen.flip();return 0;當這個程序運行的時候,我們會發(fā)現一些很明顯的問題:1、圖片移動的時候,界面不接受任何信息。這是因為必須把amn()執(zhí)行完畢才會運行到有事件響應的事件輪詢循環(huán)。2、如果我們需要另外一張圖片移動起來,我們唯一能做的事情,是修改amn()函數,而不是把amn()以不同的參數調用兩次如果以不同的參數調用兩次,那么移動總是有先后的是不可能完成“同時”移動的。1.3:創(chuàng)建線程如果要將這個程序從主線程(主進程)調用函數修改為通過新創(chuàng)建的線程調用函數,只需要做很小的修改,即將amn(void*)&test1);修改為:SDL_Thread* thread1 = SDL_CreateThread(amn, (void*)&test1);然后在return 0;之前加入清理線程的語句:SDL_KillThread(thread1);這樣,程序在執(zhí)行動畫的同時,事件輪詢就已經開始,我們可以隨時結束程序,SDL界面也不會出現不響應的情況。SDL入門教程(十三):2、初識多線程 作者:龍飛2.1:競爭條件(Race Conditions)我們在前面將一個普通函數調用轉換成了用線程調用,這意味著我們可以“同時”調用兩個以上的線程。例如,我們希望在屏幕的另外一個位置也播放這段簡單的動畫,我們只需要添加一個線程的調用就可以了。int main(int argc ,char* argv)/Create a SDL screen.const int SCREEN_WIDTH = 640;const int SCREEN_HEIGHT = 480;const Uint32 SCREEN_FLAGS = 0; /SDL_FULLSCREEN | SDL_DOUBLEBUF | SDL_HWSURFACEconst std:string WINDOW_NAME = Amn Test;ScreenSurface screen(SCREEN_WIDTH, SCREEN_HEIGHT, WINDOW_NAME, 0, SCREEN_FLAGS);PictureSurface bg(./images/background.png, screen);bg.blit(0);screen.flip();AmnArg test1(0, 250, 600, 250, screen);SDL_Thread* thread1 = SDL_CreateThread(amn, (void*)&test1);AmnArg test2(0, 0, 600, 0, screen);SDL_Thread* thread2 = SDL_CreateThread(amn, (void*)&test2);SDL_Event gameEvent;bool gameOver = false;while ( gameOver = false )while ( SDL_PollEvent(&gameEvent) != 0 )if ( gameEvent.type = SDL_QUIT )gameOver = true;if ( gameEvent.type = SDL_KEYDOWN )if ( gameEvent.key.keysym.sym = SDLK_ESCAPE )gameOver = true;screen.flip();SDL_KillThread(thread1);SDL_KillThread(thread2);return 0;這段程序看起來似乎沒有什么問題,但是運行的時候,不可預知的情況出現了:理論上我們幾乎同時調用了兩個線程,動畫似乎應該是同步播放的,但是實際上,兩段動畫的播放并不同步,并且每次執(zhí)行的效果都不一樣有時候上面的圖片移動快,有時候下面的圖片移動快,并且速度不均勻。這就是典型的race conditions的表現。還記得我說過沒有定義dt嗎,我們讓電腦以其所能達到的最快速度決定dt,換句話說,我們每一個線程都試圖“咬死”CPU的運算,當然,在實際中多任務的OS會幫助CPU分配任務,但是如何分配卻是不確定的,因為OS并不知道哪些任務需要優(yōu)先執(zhí)行,所以,兩個線程實際上在競爭電腦的性能資源,產生的結果就是不確定的。2.2:松開“死咬”的CPUvoid SDL_Delay(Uint32 ms);解決race conditions的方法就是給CPU足夠的時間“休息”,而這正好也是我們自己定義dt所需要的。SDL_Delay()在這個時候就顯得意義重大了。當今電腦的運算速度非常非??欤灾劣谀呐挛覀儍H僅給電腦0.01秒的時間“休息”(每次循環(huán)中),電腦都會顯得很輕松了。int amn(void* data)AmnArg* pData = (AmnArg*)data;PictureSurface stand(./images/am01.png, pData-screen);stand.colorKey();PictureSurface bg(./images/background.png, pData-screen);const int SPEED_CTRL = 300;int speedX = (pData-endX - pData-beginX) / SPEED_CTRL;int speedY = (pData-endY - pData-beginY) / SPEED_CTRL;for ( int i = 0; i beginX += speedX;pData-beginY += speedY;bg.blit(pData-beginX, pData-beginY, pData-beginX, pData-beginY, stand.point()-w, stand.point()-h, 2, 2);stand.blit(pData-beginX, pData-beginY);pData-screen.flip();SDL_Delay(10);return 0;說到這里,我們不得不提及之前一直所忽略的一個問題:我們之前凡是涉及循環(huán)等待事件輪詢的程序總是占用100%的CPU,這并不是因為我們真正用到了100%的CPU性能,而是我們讓CPU陷入了“空等”(Busy Waiting)的尷尬境地。輪詢事件得到響應相對于循環(huán)等待來說,是發(fā)生得非常緩慢的事情,我們在循環(huán)中,哪怕是讓電腦休息0.01秒,事情都會發(fā)生本質性的改變:while ( gameOver = false )while ( SDL_PollEvent(&gameEvent) != 0 )if ( gameEvent.type = SDL_QUIT )gameOver = true;if ( gameEvent.type = SDL_KEYDOWN )if ( gameEvent.key.keysym.sym = SDLK_ESCAPE )gameOver = true;screen.flip();SDL_Delay(10);當我們重新運行新程序的時候,我們可以看到程序對CPU的占用從100%驟降到了0%!這當然并不意味著程序就用不上CPU了,而是說,這些運算對于我們的CPU來說,實在是小菜一碟了,或者從數據上說,處理這些運算的時間與0.01秒來比較,都幾乎可以忽略不計!2.3:GUI線程與worker線程我們的另外一項試驗是將事件輪詢放到動畫線程中,程序就不多寫了,大家可以自己試下。我直接說結論:動畫線程中無法響應事件輪詢。一般提倡的模式,是將GUI事件都寫在主線程中,而將純粹的運算才寫到由主線程創(chuàng)建的線程中,后者也就是所謂的worker線程。從另外一個概念看,只有主線程控制著“當前窗口”,其它線程也許在后臺,也許雖然也是在前臺但是并非是我們可見的,所以,輪詢事件找不到接口。對于拋出的線程與主線程之間的通訊,我們可以通過他們共享的數據來進行控制,所以,盡管事件輪詢不能直接影響worker線程,但是我們仍然是可以通過主線程進行間接影響的。SDL入門教程(十三):3、封裝多線程 作者:龍飛SDL創(chuàng)建多線程的函數SDL_CreateThread()所調用的是函數指針,這意味著我們不可以傳入(非靜態(tài))成員函數的指針。關于兩種函數指針我們之前已經討論過:函數指針與成員函數指針,我們可以有兩種方法能讓具有普通函數指針(函數指針以及靜態(tài)成員函數指針)的函數調用類的私有成員,一是友元函數,另外就是靜態(tài)成員函數。而能夠受到類私有保護的,只有靜態(tài)成員函數。所以,我們可以通過靜態(tài)成員函數調用一個對象數據的形式,實現對于創(chuàng)建多線程函數的封裝。另外,我們希望測試在主線程中讀寫線程數據的效果,所以添加了兩個方法show() 和reset(),多線程演示的類源代碼如下:#include #include SurfaceClass.hppclass AmnArgprivate:int beginX;int beginY;int endX;int endY;const ScreenSurface& screen;/static int amn(void* pThat);public:AmnArg(int begin_x, int begin_y, int end_x, int end_y, const ScreenSurface& _screen);SDL_Thread* createThrd();void show() const;void reset();其中SurfaceClass.hpp請參考:/lf426/archive/2008/04/14/47038.html實現函數如下:#include amn.hppAmnArg:AmnArg(int begin_x, int begin_y, int end_x, int end_y, const ScreenSurface& _screen):beginX(begin_x), beginY(begin_y), endX(end_x), endY(end_y), screen(_screen)SDL_Thread* AmnArg:createThrd()return SDL_CreateThread(amn, (void*)this);void AmnArg:show() conststd:cout Now x at: beginX screen);stand.colorKey();PictureSurface bg(./images/background.png, pData-screen);const int SPEED_CTRL = 300;int speedX = (pData-endX - pData-beginX) / SPEED_CTRL;int speedY = (pData-endY - pData-beginY) / SPEED_CTRL;for ( int i = 0; i beginX += speedX;pData-beginY += speedY;bg.blit(pData-beginX, pData-beginY, pData-beginX, pData-beginY, stand.point()-w, stand.point()-h, 2, 2);stand.blit(pData-beginX, pData-beginY);pData-screen.flip();SDL_Delay(10);return 0;最后,我們修改了主演示程序,并測試了show()和reset()的效果。我們可以看到,直接修改線程數據的reset()的結果也是不可預知的,所以,我們似乎更應該通過改變線程“流”的效果,而不是直接對數據進行修改。這個我們以后再討論了。#include SurfaceClass.hpp#include amn.hppint game(int argc ,char* argv);int main(int argc ,char* argv)int mainRtn = 0;try mainRtn = game(argc, argv);catch ( const ErrorInfo& info ) info.show();return -1;catch ( const char* s ) std:cerr s std:endl;return -1;return mainRtn;int game(int argc ,char* argv)/Create a SDL screen.const int SCREEN_WIDTH = 640;const int SCREE
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 《全國數據資源入表年度發(fā)展報告(2024)》
- 解除勞動合同通知書的標準(2025年版)
- 林改耕合同范本
- 中日本外貿合同范本
- 員工集體脫崗合同范本
- 《后羿射日》課件
- 2025我國合同法新規(guī)
- 2025電子產品買賣合同范例
- 2025年農村新建住宅買賣合同
- 2025企業(yè)借款合同范本系列
- 2023年高考新課標Ⅰ卷語文試題分析及2024年高考語文備考策略 課件36張
- 2020-2021學年上海市楊浦區(qū)七年級(下)期中語文試卷
- 曲阜師范大學畢業(yè)論文答辯通用ppt模板
- 建筑概論第二版-
- 倉庫貨物堆碼技術培訓PPT課件倉庫貨物擺放與堆碼規(guī)范
- 2023內部控制信息系統建設方案設計
- 1認識人壽保險課件
- 爆破安全管理知識培訓
- 盤扣式腳手架高支模計算書
- NBT 47013.2承壓設備無損檢測射線檢測講解
- 新課標中小學生課外閱讀推薦書目(教育部推薦)
評論
0/150
提交評論