SDL遊戲教程第一課 課程基礎

翻譯聲明:
    本系列教程來自
Dev Hub,一切解釋權歸原作者。我只是出自個人愛好,才翻譯了本系列教程。因爲本人也是個初學者,而且英語水平有限,錯誤難免,望各路高手指正。

本課原文地址:http://www.sdltutorials.com/sdl-tutorial-basics/

這些課程面向那些有一定C++經驗,或是其他編程語言的人。如果你跟不上代碼本身,而不是概念性問題(遊戲相關的),那我建議你還是先讀讀我們關於解釋C++編程語言的課程。那雖然不是掌握全部C++所必須的,但卻對以後會有點幫助。

在本系列教程裏,我們將使用CodeBlocks作爲我們的IDE,gcc和mingw作爲編譯器。如果你想用其他IDE和編譯器,那隨便你,但如果你對連接庫沒什麼經驗,或許不是很好搞。如果你要下載CodeBlocks,你可以免費從此獲得http://www.codeblocks.org (下載那個包含mingw包的)。強烈建議你使用穩定版,除非你想花費更多的時間和夜以繼日的修改。

本系列教程會緊密圍繞SDL(Simple DirectMedia Layer),一個2D跨平臺圖形庫。此庫可以讓我們在屏幕上繪製夢幻般的圖像,和所有有趣的東西來創建我們的遊戲。你需要到http://www.libsdl.org下載這個庫,一定要下載“開發庫”下的Mingw32的tar文件和“運行庫”下的“Win32”的壓縮包。如果你在用Visual Studio,可以下載相應的版本替代Minw32文件。一旦下載完成,建議你把壓縮包裏的.dll問及愛你放到你的system32目錄下。這是運行一個SDL應用程序所需要的。

現在打開tar文件(就是在“開發庫”下面列出的那個),然後解壓到一個目錄(比如,C:/SDL)。現在,打開CodeBlocks,需要改變一些設置。點擊菜單欄上的“設置”,然後點擊“查找目錄”選項頁。你需要添加C:/SDL/include,到“編譯器”選項頁,並把C:/SDL/lib添加到“連接器”選項頁(把C:/SDL改成你解壓文件所在的那個目錄)。完成以後,點擊Okay。

開始一個新的“空白”工程,隨便你叫它什麼。保存到某個地方。點擊“工程”,然後點擊“屬性”會彈出一個對話框;在右下角點擊“工程建立選項...”按鈕。點擊“連接器設置”,添加如下內容到“鏈接庫”下的列表:

mingw32
SDLmain
SDL

順序很重要,一定要保持上面這個次序。如果你搞不明白我們現在做什麼,我們僅僅是在把代碼“連接”在一塊兒,或者,換句話說,我們在把SDL代碼融入自己的代碼中。通過使用include文件進行編譯,lib文件進行連接。一旦完成,你的代碼就會組成一個整體以生成一個應用程序。

點擊兩次OK,然後就完成了所有設置!

讓我們創建兩個新文件CApp.h和CApp.cpp;他們將擔任我們程序的核心部分。首先,打開CApp.h,然後添加如下代碼,從此,我們的課程才真正開始:

現在,打開CApp.h,添加如下代碼:
  1. #include "CApp.h"
  2. CApp::CApp() {
  3. }
  4. int CApp::OnExecute() {
  5.     return 0;
  6. }
  7. int main(int argc, char* argv[]) {
  8.     CApp theApp;
  9.     return theApp.OnExecute();
  10. }
這個CApp類爲我們的整個程序做好基礎。我們先到一邊看看一個遊戲如何進行典型的設置。幾乎所有的遊戲都是由5個處理遊戲過程的函數組成的。這些過程差不多都是這樣:

初始化
處理所有的數據加載,可以是紋理貼圖、地圖、或NPC什麼的。

消息
處理所有的輸入消息,來自鼠標、鍵盤、遊戲杆、或其他什麼設備。

循環
處理所有的數據更新,比如,NPC的移動、減少你的血條、或其他什麼。

渲染
處理所有的場景渲染,它並不操作數據,因爲那應該是由循環函數完成的。

清理
簡單的清理掉所有加載的資源,然後聲明一個遊戲的正確退出。

瞭解遊戲就是一個巨大的循環,這點很重要。在這個循環內我們找到消息,更新數據,然後渲染圖片。所以,最基本的結構差不多如下:

Initialize();

while(true) {
    Events();
    Loop();
    Render();
}

Cleanup();


每個循環間隔,我們修改一些數據,然後進行相應的渲染。消息是附加的,只不過是用戶操作數據的一種方式。在某種意義上,消息並不是製作一個遊戲所必須的,但是當我們希望用戶去操作數據的時候(比如向左移動一個NPC)卻需要它。

舉個例子就會更清楚了。比如我們有一個騎士,這個遊戲的英雄。我們想做的僅僅是讓他兜圈子。如果我按下左,他就向左走。我們需要在一個循環中解決如何實現它。首先,我們知道要檢查消息(鍵盤消息)。因爲消息是操作數據的一種手段,我們還知道還需要一些某種類型的變量要修改。於是,我們可以使用這些變量在屏幕上合適的位置來渲染我們的騎士。我們需要:

if(Key == LEFT) X–;
if(Key == RIGHT) X++;
if(Key == UP) Y–;
if(Key == DOWN) Y++;//… somewhere else in our code …

RenderImage(KnightImage, X, Y);


它能運行,是因爲每次循環都檢查key是LEFT,RIGHT,等,若如此,我們增加或者減小一個變量。所以,如果我們的遊戲以30幀每秒的速度運行並且我們按下LEFT,那麼這傢伙每秒就會向左移動30個像素。如果你不明白遊戲循環到底是怎麼回事,待會你就明白了。遊戲需要他們來正確運行。

回到我們的概念代碼(那5個函數),我們可以添加一些附加文件到我們的工程:

CApp_OnInit.cpp
CApp_OnEvent.cpp
CApp_OnLoop.cpp
CApp_OnRender.cpp
CApp_OnCleanup.cpp

回到CApp.h,然後添加如下函數和變量:
  1. #ifndef _CAPP_H_
  2. #define _CAPP_H_
  3. #include <SDL.h>
  4. class CApp {
  5.     private:
  6.         bool    Running;
  7.     public:
  8.         CApp();
  9.         int OnExecute();
  10.     public:
  11.         bool OnInit();
  12.         void OnEvent(SDL_Event* Event);
  13.         void OnLoop();
  14.         void OnRender();
  15.         void OnCleanup();
  16. };
  17. #endif
創建他們自己的函數,完成剛創建的每一個文件:
  1. #include "CApp.h"
  2. bool CApp::OnInit() {
  3.     return true;
  4. }
  5. #include "CApp.h"
  6. void CApp::OnEvent(SDL_Event* Event) {
  7. }
  8. #include "CApp.h"
  9. void CApp::OnLoop() {
  10. }
  11. #include "CApp.h"
  12. void CApp::OnRender() {
  13. }
  14. #include "CApp.h"
  15. void CApp::OnCleanup() {
  16. }
現在,讓我們回到我們的CApp.cpp代碼把所有這些函數鏈接在一起:
  1. #include "CApp.h"
  2. CApp::CApp() {
  3.     Running = true;
  4. }
  5. int CApp::OnExecute() {
  6.     if(OnInit() == false) {
  7.         return -1;
  8.     }
  9.     SDL_Event Event;
  10.     while(Running) {
  11.         while(SDL_PollEvent(&Event)) {
  12.             OnEvent(&Event);
  13.         }
  14.         OnLoop();
  15.         OnRender();
  16.     }
  17.     OnCleanup();
  18.     return 0;
  19. }
  20. int main(int argc, char* argv[]) {
  21.     CApp theApp;
  22.     return theApp.OnExecute();
  23. }

你會發現一些新的變量,然而,讓我們看看先有什麼事發生。首先,我們試着初始化我們的遊戲,若失敗我們返回-1(一個錯誤代碼),於是,關閉遊戲。如果一切正常,那就繼續我們的遊戲循環。在遊戲循環裏,我們使用SDL_PollEvent()來檢查消息,並把他們逐一傳遞給OnEvent()。一旦完成消息,我們到OnLoop()來操作數據,然後渲染我們的遊戲。我們無休止地重複它。若用戶退出遊戲,我們到OnCleanup()來清理所有的資源。就這麼簡單。

現在,讓我們看看SDL_Event和SDL_PollEvent()。第一個是一個包含了消息信息的結構體。第二個是一個抓取任意在隊列裏等待消息的函數。這個隊列裏有很多消息,這就是爲什麼我們要循環遍歷他們。那麼,舉個例子,假如用戶在OnRender()函數執行期間,按下A並且移動了鼠標。SDL就會檢測到它,然後放置兩個消息到隊列中去,一個是按鍵的,一個是鼠標移動的。我們可以通過SDL_PollEvent()從隊列裏抓取一個消息,然後把它傳遞給OnEvent()來做相應的處理。一旦隊列裏沒任何消息了,SDL_PollEvent()就會返回false,然後退出消息隊列循環。

另一個增加的變量,Running,是私有的。表示我們退出了遊戲循環與否。如果它被設置爲false,程序就會結束,然後退出程序。那麼,舉個例子,如果用戶按下了Escape鍵,我們就把它設置爲false,退出遊戲。

目前,你的編譯應該一切順利,但是,你可能注意到你還是不能退出。你或許還得到任務管理器那裏去終止掉這個程序。

現在一切搞定,我們就開始創建一個繪製遊戲的窗口。
跳到CApp.h,然後增加一個SDL表面變量到代碼:
  1. #ifndef _CAPP_H_
  2. #define _CAPP_H_
  3. #include <SDL.h>
  4. class CApp {
  5.     private:
  6.         bool            Running;
  7.         SDL_Surface*    Surf_Display;
  8.     public:
  9.         CApp();
  10.         int OnExecute();
  11.     public:
  12.         bool OnInit();
  13.         void OnEvent(SDL_Event* Event);
  14.         void OnLoop();
  15.         void OnRender();
  16.         void OnCleanup();
  17. };
  18. #endif

我感覺現在是時候後好好解釋一下什麼是一個SDL表面了。一個SDL表面是任何你可以在其上進行繪製的東西。假如我們有一張白紙、一支鉛筆、和一些貼紙;這張紙就可以被稱作是我們的顯示“表面”,我們可以在它上面畫東西,把貼紙放上面或別的什麼東西。我們的貼紙也是表面;我們可以在貼紙上繪畫,然後把它貼到別的貼紙上去。那麼,Surf_Display就是我們的“一張白紙”。我們要在它上面繪製我們所有的東西。

現在,讓我們回到CApp_OnInit來真正的創建這張表面:
  1. #include "CApp.h"
  2. bool CApp::OnInit() {
  3.     if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
  4.         return false;
  5.     }
  6.     if((Surf_Display = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) {
  7.         return false;
  8.     }
  9.     return true;
  10. }

首先要做的一件事就是啓動SDL本身,然後我們才能訪問它的函數。我們告訴SDL去初始化它所有的一切;有其他參數你可以傳遞,但是現在搞明白他們並不是很重要。下一個函數就是SDL_SetVideoMode()。他就是創建我們的窗口和表面的那個傢伙。他有4個參數:窗口寬度,窗口高度,窗口位深度(建議16或32),然後是顯示標誌。顯示標誌相當多,但是上面顯示的那個現在用就正好。第一個標誌位告訴SDL使用硬件內存來存儲我們的圖片等,第二個標誌位告訴SDL使用雙緩存(如果你不想閃屏,這個很重要)。還有一個標誌位,或許你會感興趣,那就是SDL_FULLSCREEN,它可以使你的窗口全屏化顯示。

現在我們的顯示也搞定了,然我們稍微做些清理,以保證一切工作順暢。打開CApp_OnCleanup.cpp,然後添加如下:
  1. #include "CApp.h"
  2. void CApp::OnCleanup() {
  3.     SDL_FreeSurface(Surf_Display);
  4.     SDL_Quit();
  5. }
我們要做的第一件事,就是釋放掉這個顯示錶面,基本就是釋放一些內存空間。之後,我們就退出SDL。你要注意下,這裏也是你釋放其他表面的地方(或許在Surf_Display之前)。這保證了你所有的代碼都集中到它的功能實現上。

爲了保持整潔,我們也把Surf_Display指針在構造函數裏設置爲NULL。
打開CApp.cpp,添加如下:
  1. CApp::CApp() {
  2.     Surf_Display = NULL;
  3.     Running = true;
  4. }
試試編譯你的代碼,看看能不能運行。你會得到一個彈出的空窗口。
你會發現,你還是不能關掉它,還得去“勞駕“任務管理器。

現在,我們已經搞定一個窗口了,我們要做的就是找一種方式去關閉它。
打開CApp_OnEvent.cpp文件,添加如下:
  1. #include "CApp.h"
  2. void CApp::OnEvent(SDL_Event* Event) {
  3.     if(Event->type == SDL_QUIT) {
  4.         Running = false;
  5.     }
  6. }
這個SDL消息結構體被肢解到types了。這些types可以包括按鍵、鼠標移動;我們要做得僅僅是在此檢查消息類型。上面我們找到的那個類型是關閉窗口需要的(比如,當用戶點擊X按鈕)。如果那種消息發生,我們就設置Running爲false,然後終止程序。就這麼簡單。我們會在後續課程當中再仔細琢磨消息。

現在,一切搞定了,並且是一個日後可以再用的優秀結構。或許把此工程改造成一個CodeBlock裏的”SDL template“是個不錯的想法。我不想回頭去做了,只是隨意Google一下。

如果你想知道我們在此提到的代碼發生了其他什麼事,跳到下一節課去學更多的SDL表面吧。

SDL 課程基礎 —— 教程文件:
Win32:Zip,Rar
Linux:Tar(感謝Gaten)
發佈了8 篇原創文章 · 獲贊 11 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章