第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;
}