---
# System prepended metadata

title: 'SDL2: 基礎架構'
tags: [SDL]

---

# 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中輸入的指令，但用不到也得寫。

---

了解基礎的運作模式以後，就可以開始學習創造與運用各種功能了!