第1課:你好世界
在本課中,我們將學習如何打開一個窗口,創建一個渲染上下文,並繪製一個我們已經加載到屏幕上的圖像。獲取BMP,我們將在下面繪製並保存它在你的項目中的某個地方,讓我們開始吧!
啓動SDL
爲了使用SDL,我們首先需要初始化我們想要使用的各種SDL子系統。這是通過 SDL_Init 完成的,它使用一組標誌或一起指定要初始化的子系統。現在我們只需要視頻子系統,但我們將添加更多的標誌,因爲我們需要更多的功能。注意,當視頻系統本身沒有顯式請求而文件I/O和線程系統默認被初始化時,事件處理系統被自動初始化。如果一切順利,SDL_Init
將返回0,如果不是,我們將打印出錯誤並退出。
if (SDL_Init(SDL_INIT_VIDEO) != 0){
std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
return 1;
}
創建窗口
我們需要一個窗口來顯示我們的渲染,我們可以創建一個帶有SDL_CreateWindow的渲染窗口,它獲取窗口的標題、創建它的x和y位置、窗口寬度和高度以及一些設置窗口屬性的標誌,並返回一個SDL_.*。如果創建窗口時出現任何錯誤,此指針將爲空。如果確實發生錯誤,我們需要在退出程序之前清理SDL。
SDL_Window *win = SDL_CreateWindow("Hello World!", 100, 100, 640, 480, SDL_WINDOW_SHOWN);
if (win == nullptr){
std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
創建渲染器
現在我們可以創建一個渲染器來使用SDL_CreateRenderer繪製到窗口。 此函數使用窗口將渲染器與要使用的渲染驅動程序的索引相關聯(或-1以選擇滿足我們要求的第一個)以及用於指定我們想要的渲染器類型的各種標誌。 這裏我們要求啓用了vsync的硬件加速渲染器。 我們將返回一個SDL_Renderer *,如果出現問題,它將爲NULL。 如果確實發生了錯誤,我們需要清理我們之前創建的任何內容,並在退出程序之前退出SDL。
SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (ren == nullptr){
SDL_DestroyWindow(win);
std::cout << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
加載位圖圖像
要渲染BMP圖像,我們需要將其加載到內存中,然後加載到我們正在使用的渲染平臺上(在本例中爲GPU)。 我們可以使用SDL_LoadBMP加載圖像,它會返回一個SDL_Surface *然後我們可以將其上傳到渲染器能夠使用的SDL_Texture。
SDL_LoadBMP獲取我們圖像的文件路徑,您應該更改它以匹配您的項目結構,並在出現問題時返回SDL_Surface *或NULL。
std::string imagePath = getResourcePath("Lesson1") + "hello.bmp";
SDL_Surface *bmp = SDL_LoadBMP(imagePath.c_str());
if (bmp == nullptr){
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
std::cout << "SDL_LoadBMP Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
加載圖像後,我們現在可以使用SDL_CreateTextureFromSurface將其上傳到渲染器。 我們傳入渲染上下文以上傳到內存中的圖像(SDL_Surface)並獲取加載的紋理,如果出現問題,我們將返回NULL。 此時我們還完成了原始表面,所以我們現在就把它釋放出來。
SDL_Texture *tex = SDL_CreateTextureFromSurface(ren, bmp);
SDL_FreeSurface(bmp);
if (tex == nullptr){
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
std::cout << "SDL_CreateTextureFromSurface Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
繪製紋理
剩下要做的就是在屏幕上獲取我們的紋理! 首先,我們將清除渲染器,然後渲染紋理,然後顯示更新的屏幕以顯示結果。 由於我們想要渲染整個圖像並使其伸展以填充屏幕,因此我們將傳遞NULL作爲SDL_RenderCopy的源和目標矩形。 我們還希望保持窗口一段時間,以便我們可以在程序退出之前看到結果,因此我們將添加對SDL_Delay的調用。
我們將所有這些渲染代碼放在我們程序的主循環中,現在這將是一個簡單的for循環。 通過循環的每次迭代我們都會睡一秒鐘,所以我們可以增加或減少計數器,使我們的程序運行更長或更短的時間。 當我們進入事件處理時,我們將改爲跟蹤一個布爾值,該布爾值指示用戶是否想要退出我們的程序(例如,單擊窗口上的X)並在這種情況下退出循環。
//A sleepy rendering loop, wait for 3 seconds and render and present the screen each time
for (int i = 0; i < 3; ++i){
//First clear the renderer
SDL_RenderClear(ren);
//Draw the texture
SDL_RenderCopy(ren, tex, NULL, NULL);
//Update the screen
SDL_RenderPresent(ren);
//Take a quick break after all that hard work
SDL_Delay(1000);
}
打掃乾淨
在退出之前,我們必須通過各種SDL_DestroyX函數銷燬創建的所有對象並退出SDL。 錯誤處理注意事項:以前在程序中我們可能遇到錯誤並提前退出,在這種情況下,我們必須銷燬我們創建的任何SDL對象並退出SDL以在退出之前正確清理。 從課程中省略了錯誤處理的這一部分,因爲它們是如此小的例子,它有助於保持代碼更短,但在現實世界的程序中,絕對需要正確的錯誤處理和清理。
SDL_DestroyTexture(tex);
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
SDL_Quit();
課程結束
如果一切順利,你應該看到你加載的圖像在整個窗口上呈現,等待2秒然後退出。 如果您有任何問題,請確保已安裝SDL並正確配置項目,如第0課:設置SDL中所述,或在下面發佈問題。
完整代碼:
#include "SDL.h"
#include <iostream>
//#include <stdio.h>
int main(int argc, char* argv[]) {
SDL_Window* window = NULL; // Declare a pointer
SDL_Renderer* renderer = NULL; //渲染器
SDL_Surface* bmp = NULL;
// 初始化SDL
// int SDLCALL SDL_Init(Uint32 flags)
//SDL_INIT_TIMER:定時器
//SDL_INIT_AUDIO:音頻
//SDL_INIT_VIDEO:視頻
//SDL_INIT_JOYSTICK:搖桿
//SDL_INIT_HAPTIC:觸摸屏
//SDL_INIT_GAMECONTROLLER:遊戲控制器
//SDL_INIT_EVENTS:事件
//SDL_INIT_NOPARACHUTE:不捕獲關鍵信號(這個不理解)
//SDL_INIT_EVERYTHING:包含上述所有選項
if(SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
return 1;
}
// 使用以下設置創建應用程序窗口:
// SDL_Window * SDLCALL SDL_CreateWindow(const char *title,int x, int y, int w, int h, Uint32 flags);
//參數含義如下。
//title :窗口標題
// x :窗口位置x座標。也可以設置爲SDL_WINDOWPOS_CENTERED或SDL_WINDOWPOS_UNDEFINED。
// y :窗口位置y座標。同上。
// w :窗口的寬
// h :窗口的高
// flags :支持下列標識。包括了窗口的是否最大化、最小化,能否調整邊界等等屬性。
// SDL_WINDOW_FULLSCREEN 全屏窗口
// SDL_WINDOW_FULLSCREEN_DESKTOP 當前桌面分辨率下的全屏窗口
// SDL_WINDOW_OPENGL 使用OpenGL上下文的窗口
// SDL_WINDOW_VULKAN 與Vulkan實例一起使用的窗口
// SDL_WINDOW_HIDDEN 窗口不可見
// SDL_WINDOW_BORDERLESS 無窗裝飾
// SDL_WINDOW_RESIZABLE 窗口可以調整大小
// SDL_WINDOW_MINIMIZED 最小化窗口
// SDL_WINDOW_MAXIMIZED 最大化窗口
// SDL_WINDOW_INPUT_GRABBED 窗口抓住輸入焦點
// SDL_WINDOW_ALLOW_HIGHDPI 如果支持,窗口應該在高DPI模式下創建(> = SDL2.0.1)
// SDL_WINDOW_SHOWN 可見窗口
// SDL_WINDOW_POPUP_MENU 窗口應被視爲彈出式菜單(x11,>=SDL 2.0.5)
// SDL_WINDOW_TOOLTIP 窗口應被視爲工具提示(X11,> = SDL 2.0.5)
// SDL_WINDOW_UTILITY 窗口應視爲實用窗口(x11,>=SDL 2.0.5)
// SDL_WINDOW_SKIP_TASKBAR 窗口不應該添加到任務欄(X11,> = SDL 2.0.5)
// SDL_WINDOW_ALWAYS_ON_TOP 窗口應該總是高於其他(x11,>=SDL 2.0.5)
// SDL_WINDOW_MOUSE_CAPTURE 窗口已捕獲鼠標(與輸入的抓取無關,> = SDL 2.0.4)
// SDL_WINDOW_FOREIGN 不由SDL創建的窗口
// SDL_WINDOW_MOUSE_FOCUS 窗口有鼠標焦點
// SDL_WINDOW_INPUT_FOCUS 窗口有輸入焦點
// 返回創建完成的窗口的ID。如果創建失敗則返回0。
window = SDL_CreateWindow(
"開軟網絡科技有限公司", // window title
SDL_WINDOWPOS_UNDEFINED, // initial x position
SDL_WINDOWPOS_UNDEFINED, // initial y position
1365, // width, in pixels
768, // height, in pixels
SDL_WINDOW_SHOWN // flags - see below
);
// 檢查窗口是否成功創建
if (window == nullptr)
{
std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
// SDL中使用SDL_CreateRenderer()基於窗口創建渲染器。SDL_CreateRenderer()原型如下。
// SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags);
// 參數含義如下。
// window : 渲染的目標窗口。
// index :打算初始化的渲染設備的索引。設置“-1”則初始化默認的渲染設備。
// flags :支持以下值(位於SDL_RendererFlags定義中)
// SDL_RENDERER_SOFTWARE :使用軟件渲染
// SDL_RENDERER_ACCELERATED :使用硬件加速
// SDL_RENDERER_PRESENTVSYNC:和顯示器的刷新率同步
// SDL_RENDERER_TARGETTEXTURE :不太懂
// 返回創建完成的渲染器的ID。如果創建失敗則返回NULL。
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
// 檢查渲染器是否成功創建
if (renderer == nullptr)
{
SDL_DestroyWindow(window);
std::cout << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
//加載圖片
bmp = SDL_LoadBMP("images/kairuan.bmp");
if (bmp == nullptr)
{
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
std::cout << "SDL_LoadBMP Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
SDL_Texture *tex = SDL_CreateTextureFromSurface(renderer, bmp);
SDL_FreeSurface(bmp);
if (tex == nullptr)
{
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
std::cout << "SDL_CreateTextureFromSurface Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
// 窗口是打開的:可以在這裏輸入程序循環(see SDL_PollEvent())
//A sleepy rendering loop, wait for 3 seconds and render and present the screen each time
for (int i = 0; i < 3; ++i)
{
//First clear the renderer
SDL_RenderClear(renderer);
//Draw the texture
SDL_RenderCopy(renderer, tex, NULL, NULL);
//Update the screen
SDL_RenderPresent(renderer);
//Take a quick break after all that hard work
SDL_Delay(1000);
}
// 關閉並摧毀窗戶
SDL_DestroyTexture(tex);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
// 清理
SDL_Quit();
return 0;
}