# SDL2: 基礎架構
###### tags: `SDL`
## 前言
恭喜你已經度過SDL2的第一個(也是最大的一個)難關: 安裝。現在我將先介紹SDL2是怎麼建構的? 我將在這裡講解一些基本的名詞解釋和概念。這裡的知識會比較瑣碎和無趣,如果你已經對SDL有一些概念可以先跳過一下。
## 結構
**SDL2是基於C語言編寫的**。不是C++。任何有上過C/C++課程的人都應該知道兩件事:
- C是函數導向,C++ 是物件導向,C++相容C,C沒有物件。
- 去他媽的指標(pointer)。
好那麼你已經了解SDL2的結構了。SDL2裡面的所有東西都是struct+pointer實現的。這造成它非常難以理解,很多函數的實現是搭在另一個函數之上,而且**不是成員函數,是一堆名字超長的函數**,所以想了解它的全貌,省省吧,你的程式課只有2~3學分。
那我們該如何使用它們呢? 幸好SDL教學大佬: LazyFoo幫我們解決很多問題。他的範例程式碼裡會把物件們都包成Class,方便各位使用。以下教學有時候也會幫忙建立幾個常用Class。
除了struct+pointer以外,SDL還提供很多旗標(flags),大部分是Uint32的enum(這部份看不懂就算了)。對於不知道旗標是什麼的人,簡單來說就是一種參數,用以傳入函數,但是被包裝成有名字而不是0、1、2這種方式傳入。
## SDL的錯誤訊息
程式是30% coding+70% debug。所以必須了解SDL的錯誤訊息機制。欲取得SDL的錯誤訊息,請呼叫:
```cpp
SDL_GetError();
```
函數回傳**最近一次發生的錯誤,而不是一個Stack (LIFO)的結構。**所以要在每個可能出錯的環節及時印出! 此外,函數回傳**C風格的字串(const char*)。**所以**請不要用cout去印**,使用方式可以看下一小節的程式。
## SDL的座標系統
實際上,SDL2使用的座標系統與其他電腦程式相似,若你有使用過Photoshop、Illustrator等軟體,應該不會對座標系統太陌生。
定義: **原點是螢幕的左上角,橫向向右是x軸正向,縱向向下是y軸正向。單位皆為像素(px)。**
:::info
❓ 像素是螢幕的基本長度單位。從顯示設定中可以找出電腦的解析度,解析度1920x1080代表電腦橫向被切成1920等分,即1920像素。要注意顯示比例若不是100%,則實際比例的計算方式是: **實際解析度 = 解析度/顯示比例。**
:::
## SDL的命名規律
以下的字首常見於SDL的函數,可以方便你記憶一些常用函數的功能和變數型態。
- Create: 物件的創造,傳入所需參數回傳物件指標。
- Destroy: 物件的消滅,傳入物件指標,void。
- Get: 取得某值,傳入物件指標回傳指定值。
- Set: 設定某值,傳入物件指標和參數,回傳0為正常,負數為錯誤,或是void函數。
- Is/Was/Has: 確認有無…,傳入物件指標,回傳bool。
## SDL的眉角
1. **所有的東西都需要初始化!** 有一個函數負責初始化,我們不要管它怎麼運作,只要記得將這行放在所有SDL之前。
```cpp
//Initialize SDL
if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 )
{
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
}
```
各個library還有自己的初始化語法,我統一整理在這裡。使用時,若有未安裝的程式庫,記得把它的初始化從這裡刪掉,不然會有”呼叫未知函數”的問題。
:::warning
❓ 出現”…” was not declared in this scope就可能是呼叫了未知函數。
:::
</aside>
```cpp
void InitializeSDL(){
//Initialize SDL
if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 )
{
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
}
//Initialize SDL_ttf
if( TTF_Init() == -1 )
{
printf( "SDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError() );
}
//Initialize SDL_image
int imgFlags = IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF | IMG_INIT_WEBP | IMG_INIT_JXL | IMG_INIT_AVIF;
if( !( IMG_Init( imgFlags ) & imgFlags ))
{
printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
}
//Initialize SDL_mixer
int mixFlags = MIX_INIT_FLAC | MIX_INIT_MOD | MIX_INIT_MP3 | MIX_INIT_OGG | MIX_INIT_MID | MIX_INIT_OPUS | MIX_INIT_WAVPACK;
if( !( Mix_Init( mixFlags ) & mixFlags ))
{
printf("SDL_mix could not initialize! SDL_mixer Error: %s\n", Mix_GetError());
}
//open audio devices
if( Mix_OpenAudio( 44100, MIX_DEFAULT_FORMAT, 2, 2048 ) < 0 )
{
printf( "SDL_mixer could not initialize! SDL_mixer Error: %s\n", Mix_GetError() );
}
//Initialize SDL_net
if( SDLNet_Init() == -1 )
{
printf( "SDL_net could not initialize! SDL_net Error: %s\n", SDLNet_GetError() );
}
return;
}
```
2. 因為SDL是基於pointer去配置記憶體。**每個物件的創造都需要有對應的消滅(memory deallocate)**。使用上要注意document所寫的對應函數,否則會有很嚴重的memory leak。
:::warning
❓ 程式的記憶體在執行時不斷上升,很可能是因為某些用後即丟的物件沒有被消滅。
:::
3. SDL的main()一定要用這個方式寫:
```cpp
int main(int argc, char *argv[]){
return 0;
}
```
其他方式都不行!! 它的意義是可以接受console中輸入的指令,但用不到也得寫。
---
了解基礎的運作模式以後,就可以開始學習創造與運用各種功能了!