GFX with SDL Lesson 1: Getting started with SDL(翻譯)

GFX with SDL
Lesson 1: Getting started with SDL

作者:Marius Andra
翻譯:heroboy


歡迎來到“GFX with SDL”的第一篇教程。

Dev-C++中使用SDL

首先需要下載sdlDevCPP-1.2.4.zip(點擊名稱下載)。把zip文件解壓到Dev C++的目錄中,請確定includelib文件夾(zip文件中)中的文件解壓到Dev C++中的includelib文件夾中。我計算機上的Dev-C++文件夾是c:/Dev-C++。所以解壓以後,在c:/Dev-C++/lib會有libSDL.alibSDL.lalibSDLmain.aSDL.dll,在c:/Dev-C++/include/SDL中會有一些 .h文件。

 

現在,在Dev-C++中新建一個控制檯工程。打開“工程屬性”對話框(在“工程”菜單中)。在參數中,點擊“加入庫或者對象”按鈕,選擇libSDL.aSDLmain.aligmingw32.a 三個文件。點擊“確定”。

 

最後,在SDL項目中使用printf(…)或輸出到stdout.txt中,而非屏幕。


Microsoft Visual C++ 6.0中使用SDL

要在MSVC6中使用SDL要下載SDL-devel-1.2.4-VC6.zip(請訪問www.libsdl.org下載更新版本的SDL)。在這個壓縮文件中有兩個重要的文件夾-includelib,把lib文件夾中的文件複製到MSVC6lib文件夾中(在我的機器上是C:/Program Files/Microsoft Visual Studio/VC98/Lib),在MSVC6include文件夾中新建一個SDL文件夾(在我的機器上是C:/Program Files/Microsoft Visual Studio/VC98/Include/SDL),把壓縮文件中include文件夾中的.h文件複製到新建的文件夾中。

 

現在,在VC++中新建一個工程。選擇“WIN32 Application”和“'an empty project”。現在要爲工程創建一個cpp文件,單擊File-new並選擇 c++ source file”,名字輸入“main.cpp”。現在打開工程選項對話框(菜單project->settings)。點擊“LINK”欄,添加 sdl.lib sdlmain.lib”到連接文件中(Object/library modules)。最後,點擊“C/C++”欄,在下拉菜單中選擇“Code Generation”。然後在“Use run-time library”下拉框中選擇“Multithreaded DLL”。

 

SDL.dll

 


SDL.dll
SDL中一個重要的文件(它在Dev-C++MSVC6SDL zip文件裏)。如果你要運行SDL程序,你必須把SDL.dll複製到c:/windows/system (win 95, 98, ME) c:/windows/system32 (on windows NT, 2000 and XP)。或者SDL.dll和你的程序在同一個文件夾。


Getting started with SDL

你已經設置好了一切。你要象下面一樣在你的程序中包含 SDL/SDL.h 文件:

#include <SDL/SDL.h>

通過SDL_Init()函數初始化SDLSDL_Init的返回值下於0表示出錯。它接受一個參數,初始化的內容。SDL_INIT_VIDEO初始化視頻,SDL_INIT_AUDIO初始化音頻。要初始化音頻和視頻,使用SDL_INIT_VIDEO|SDL_INIT_AUDIO。可以初始化更多如下的項目(要同時初始化,請使用 | 分割他們):

SDL_INIT_TIMER

SDL_INIT_AUDIO

SDL_INIT_VIDEO

SDL_INIT_CDROM

SDL_INIT_JOYSTICK

SDL_INIT_NOPARACHUTE

SDL_INIT_EVENTTHREAD

SDL_INIT_EVERYTHING

因此,初始化視頻和音頻的代碼是:

if( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) <0 )

{

  printf("Unable to init SDL: %s/n", SDL_GetError());

  return 1;

}

如果出錯了,SDL_GetError()函數返回一個字符串來描述這個錯誤。

但退出程序時,你必須調用SDL_Quit()。如果沒有調用的話,可能會出現一些異常的現象。告訴編譯器你要在退出時調用SDL_Quit,代碼如下:

atexit(SDL_Quit);

那樣就不需要在main函數中的每個return前加上SDL_Quit()了。

SDL中需要很多的surface。一切都是surface。你可以在surface上繪圖,也可以把一個surface畫到另一個surface上。在SDL中,屏幕也是一個surface。在我們的程序中surface是一個指向SDL_Surface結構的指針。如下得到屏幕surface

SDL_Surface *screen;

相信你在玩有些遊戲的時候會要你選擇屏幕分辨率。如果沒有的話,請玩更多的遊戲。如果你要使用surface屏幕(surface screen)(記住,screen只是一個指向SDL_Surface結構的指針),在上面繪圖(你在監視器上可以看到畫了什麼),那麼使用SDL_SetVideoMode()函數。

screen = SDL_SetVideoMode(640, 480, 32,

                             SDL_HWSURFACE|SDL_DOUBLEBUF);

前三個參數分別是屏幕的 寬度,高度和像素位數bits per pixel)。如果像素位數爲0SDL會爲你自動選擇最合適的位數。第四個參數是一些特殊的標記。如果要在屏幕上繪圖的話,必須有SDL_HWSURFACE(或SDL_SWSURFACE)。下列是可用的選項:


SDL_SWSURFACE
在系統內存中創建surface
SDL_HWSURFACE
在顯示內存中創建surface
SDL_ASYNCBLIT
允許異步刷新的surface。這會降低在單CPU上的位拷貝(blitting)的速度,但在SMP系統上速度會有提升。
SDL_ANYFORMAT -
一般地,如果給與的像素位數(bits-per-pixelbpp)不可用的話,SDL回使用影子surfaceshadow surface)模擬。SDL_ANYFORMAT會阻止這麼做,而忽略參數中的像素位數。
SDL_HWPALETTE
給予SDL 獨立的調色板訪問(exclusive palette access)。沒有這個選項,你不能通過SDL_SetColorsSDL_SetPalette來獲取顏色。
SDL_DOUBLEBUF
允許硬件雙緩衝。只有和SDL_HWSURFACE一起使用纔有效。調用SDL_Flip來翻轉(flip)緩衝並刷新屏幕。所有的繪圖不會立即顯示在屏幕上。如果不允許雙緩衝的話,SDL_Flip函數相當於對整個屏幕調用SDL_UpdateRect
SDL_FULLSCREEN – SDL
會嘗試使用全屏模式。如果硬件不支持當前的分辨率的話,會使用一個更高的分辨率和黑色的背景。
SDL_OPENGL
創建OpenGL渲染上下文(rendering context)。你應該預先使用SDL_GL_SetAttribute設置OpenGL視頻屬性。
SDL_OPENGLBLIT -
創建OpenGL渲染上下文(rendering context),但使用普通的位拷貝(blitting)操作。屏幕surface2D)會有一個alpha通道,並且必須使用SDL_UpdateRects來更新屏幕surface
SDL_RESIZABLE
創建可縮放的窗口。當縮放的時候,會產生SDL_VIDEORESIZE事件,此時可通過SDL_SetVideoMode再次改變屏幕surface大小。
SDL_NOFRAME -
如果可行的話,SDL會創建無框架無標題欄的窗口。全屏模式自動包含此選項。

我的建議是:使用 SDL_HWSURFACE|SDL_DOUBLEBUF。如果出錯的話,使用SDL_SWSURFACE.

SDL_SetVideoMode
返回指向SDL_Surface的指針,出錯的話返回NULL。使用如下代碼檢測出錯:

if ( screen == NULL )

{

  printf("Unable to set 640x480 video: %s/n", SDL_GetError());

  return 1;

}

至此已經初始化了SDL,可以開始繪圖了。在開始繪圖前,我要告訴你一些SDL的數據類型。他們是:

Uint8 – 相當於unsigned char

Uint16 – 16(2字節)無符號整形

Uint32 - 32(4字節) 無符號整形

Uint64 - 64(8字節) 無符號整形

Sint8 - 相當於 signed char

Sint16 - 16(2字節) 有符號整形

Sint32 - 32(4字節) 有符號整形

Sint64 - 64(8字節) 有符號整形

另外,無論何時初始化出錯的時候,你不必完全退出。例如,SDL_INIT_VIDEO初始化成功,而SDL_INIT_AUDIO失敗的話,你仍然可以繼續運行程序,只是沒有聲音。爲了檢測音頻是否初始化成功,使用SDL_WasInit()函數。代碼如下:

Uint32 init = SDL_WasInit(SDL_INIT_AUDIO);

if(init & SDL_INIT_AUDIO)

{

  sound = 1;  // Audio init sucessful, use sound

} else {

  sound = 0;  // Audio init unsucessful, don't use sound

}

你可以在如下代碼中插入上述代碼

if( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) <0 )

{

  printf("Unable to init SDL: %s/n", SDL_GetError());

  return 1;

}

爲簡單起見,我不在教程中插入此代碼。

繪製像素乍看是很簡單的,但如果你看了此函數的話其實並不簡單。我使用的這個像素繪製函數,取自SDL introwww.libsdl.org上的)。如下:

注意:你不必完全理解它,只要使用就可以了

void DrawPixel(SDL_Surface *screen, int x, int y,

                                    Uint8 R, Uint8 G, Uint8 B)

{

  Uint32 color = SDL_MapRGB(screen->format, R, G, B);

  switch (screen->format->BytesPerPixel)

  {

    case 1: // Assuming 8-bpp

      {

        Uint8 *bufp;

        bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;

        *bufp = color;

      }

      break;

    case 2: // Probably 15-bpp or 16-bpp

      {

        Uint16 *bufp;

        bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;

        *bufp = color;

      }

      break;

    case 3: // Slow 24-bpp mode, usually not used

      {

        Uint8 *bufp;

        bufp = (Uint8 *)screen->pixels + y*screen->pitch + x * 3;

        if(SDL_BYTEORDER == SDL_LIL_ENDIAN)

        {

          bufp[0] = color;

          bufp[1] = color >> 8;

          bufp[2] = color >> 16;

        } else {

          bufp[2] = color;

          bufp[1] = color >> 8;

          bufp[0] = color >> 16;

        }

      }

      break;

    case 4: // Probably 32-bpp

      {

        Uint32 *bufp;

        bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;

        *bufp = color;

      }

      break;

  }

}

傳遞一個你想繪圖的surfacex軸,y軸座標和RGB顏色就可以了。


另外需要兩個重要的函數。一些顯卡,需要在繪圖前鎖定它。SDL_MUSTLOCK(SDL_Surface *screen)用來確定是否需要鎖定屏幕。SDL_LockSurface(SDL_Surface *screen)SDL_UnlockSurface(SDL_Surface *screen)用來鎖定和解鎖。函數代碼如下:

void Slock(SDL_Surface *screen)

{

  if ( SDL_MUSTLOCK(screen) )

  {

    if ( SDL_LockSurface(screen) < 0 )

    {

      return;

    }

  }

}

 

void Sulock(SDL_Surface *screen)

{

  if ( SDL_MUSTLOCK(screen) )

  {

    SDL_UnlockSurface(screen);

  }

}

調用 Slock(screen)鎖定屏幕, Sulock(screen)解鎖。


現在,代碼應該如下:

#include <stdio.h>

#include <stdlib.h>

 

#include <SDL/SDL.h>

 

// The functions are not shown to save space

void DrawPixel(SDL_Surface *screen, int x, int y,

                                    Uint8 R, Uint8 G, Uint8 B);

void Slock(SDL_Surface *screen);

void Sulock(SDL_Surface *screen);

 

 

int main(int argc, char *argv[])

{

 

  if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )

  {

    printf("Unable to init SDL: %s/n", SDL_GetError());

    exit(1);

  }

  atexit(SDL_Quit);

 

  SDL_Surface *screen;

  screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF);

  if ( screen == NULL )

  {

    printf("Unable to set 640x480 video: %s/n", SDL_GetError());

    exit(1);

  }

 

  // DRAWING GOES HERE

 

  return 0;

}

運行一下。你會發現,一個空窗口閃了一下。加入SDL_FULLSCREEN標記,會看見一個黑屏。現在,讓我們繪圖。


我們來做一個簡單的繪圖:我們把所有的東西都繪製在一個緩衝上,再把緩衝畫到屏幕上。這比直接在屏幕上繪製像素簡單,速度快,而且沒有閃爍。


讓我們在做一個彩色的屏幕(如你看見的截圖)。我們通過一個循環把所有座標繪製上去。在循環前添加鎖定屏幕的函數,在循環後添加解鎖的函數。Drawpixel函數在屏幕surface(緩衝)上繪製彩色的像素(每個像素顏色不同),然後使用SDL_Flip把緩衝(screen surface)繪製到實際的計算機屏幕上。

Slock(screen);

for(int x=0;x<640;x++)

{

  for(int y=0;y<480;y++)

  {

    DrawPixel(screen, x,y,y/2,y/2,x/3);

  }

}

Sulock(screen);

SDL_Flip(screen);

注意:實際上一直往屏幕上繪製像素是很慢的。通常只有在需要時,才繪製需要的某一部分。更多的請看以後的教程。

// DRAWING GOES HERE 替換成上述代碼,並允許程序。你會看見一個彩色的窗口,但只存在很短的時間。爲了存在時間長一點,添加一個循環:

for(i=0;i<100;i++)

{

  Slock(screen);

  for(int x=0;x<640;x++)

  {

    for(int y=0;y<480;y++)

    {

      DrawPixel(screen, x,y,y/2,y/2,x/3);

    }

  }

  Sulock(screen);

  SDL_Flip(screen);

}

循環100次,然後退出。但還有更好的方法:

我們把繪圖代碼放入一個函數:

void DrawScene(SDL_Surface *screen)

{

  Slock(screen);

  for(int x=0;x<640;x++)

  {

    for(int y=0;y<480;y++)

    {

      DrawPixel(screen, x,y,y/2,y/2,x/3);

    }

  }

  Sulock(screen);

  SDL_Flip(screen);

}

main()函數中,我們創建一個遊戲循環。遊戲循環是一個循環,直至退出。我們的遊戲循環是一個while循環,當done等於0時循環。

int done=0;

 

while(done == 0)

{

  // CODE

}

在遊戲循環中,我們檢測是否ESC鍵或窗口上的X按鈕被按下了。如果按下了,則令done等於1,那麼循環就會結束。

一切的SDL事件使用SDL_Event結構表示。我們需要一個SDL_Event變量來檢測時間:

SDL_Event event;

我們不停的獲取事件(直至沒有事件發生):

while ( SDL_PollEvent(&event) )

{

 

}

在每個 while(...) {...}中, SDL_Event 會包含事件的信息。然後我們確定事件的類型。

if ( event.type == SDL_QUIT )  {  done = 1;  }

 

if ( event.type == SDL_KEYDOWN )

{

  // CODE

}

如果我們得到了退出的事件(關閉按鈕被按下),我們令done等於1。如果一個按鍵被按下,我們在確定哪個鍵被按下:

if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }

所有的鍵盤上的按鍵名字都以SDLK_開頭。查看 SDL_keysym.h文件來得到更多的SDLK_ 鍵名字。事件檢測之後:

DrawScene(screen);

好了,下列是全部的代碼:

#include <stdio.h>

#include <stdlib.h>

 

#include <SDL/SDL.h>

 

void Slock(SDL_Surface *screen)

{

  if ( SDL_MUSTLOCK(screen) )

  {

    if ( SDL_LockSurface(screen) < 0 )

    {

      return;

    }

  }

}

 

void Sulock(SDL_Surface *screen)

{

  if ( SDL_MUSTLOCK(screen) )

  {

    SDL_UnlockSurface(screen);

  }

}

 

void DrawPixel(SDL_Surface *screen, int x, int y,

                                    Uint8 R, Uint8 G, Uint8 B)

{

  Uint32 color = SDL_MapRGB(screen->format, R, G, B);

  switch (screen->format->BytesPerPixel)

  {

    case 1: // Assuming 8-bpp

      {

        Uint8 *bufp;

        bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;

        *bufp = color;

      }

      break;

    case 2: // Probably 15-bpp or 16-bpp

      {

        Uint16 *bufp;

        bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;

        *bufp = color;

      }

      break;

    case 3: // Slow 24-bpp mode, usually not used

      {

        Uint8 *bufp;

        bufp = (Uint8 *)screen->pixels + y*screen->pitch + x * 3;

        if(SDL_BYTEORDER == SDL_LIL_ENDIAN)

        {

          bufp[0] = color;

          bufp[1] = color >> 8;

          bufp[2] = color >> 16;

        } else {

          bufp[2] = color;

          bufp[1] = color >> 8;

          bufp[0] = color >> 16;

        }

      }

      break;

    case 4: // Probably 32-bpp

      {

        Uint32 *bufp;

        bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;

        *bufp = color;

      }

      break;

  }

}

 

void DrawScene(SDL_Surface *screen)

{

  Slock(screen);

  for(int x=0;x<640;x++)

  {

    for(int y=0;y<480;y++)

    {

      DrawPixel(screen, x,y,y/2,y/2,x/3);

    }

  }

  Sulock(screen);

  SDL_Flip(screen);

}

 

int main(int argc, char *argv[])

{

 

  if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )

  {

    printf("Unable to init SDL: %s/n", SDL_GetError());

    exit(1);

  }

  atexit(SDL_Quit);

 

  SDL_Surface *screen;

  screen=SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF);

  if ( screen == NULL )

  {

    printf("Unable to set 640x480 video: %s/n", SDL_GetError());

    exit(1);

  }

  int done=0;

 

  while(done == 0)

  {

    SDL_Event event;

 

    while ( SDL_PollEvent(&event) )

    {

      if ( event.type == SDL_QUIT )  {  done = 1;  }

 

      if ( event.type == SDL_KEYDOWN )

      {

        if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }

      }

    }

 

    DrawScene(screen);

  }

 

  return 0;

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章