banner
Learn,review,improve.

SDL入门

Scroll down

我的SDL3之旅(面向C语言初学者):DAY1认识基本结构和创建窗口

在上一篇文章中我们已经配置好了SDL3环境现在我们可以来写SDL3的代码了。
如果没有配置请转到环境配置

开发环境:Virtual Studio 2022

基本框架

值得一提的是SDL3与SDL2不同,其舍弃了传统的main函数的入口模式,转而使用四个基本函数来处理程序的生命周期。下面我们来认识一下。

初始化函数

1
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])

这个函数会在程序开始执行时运行一次,如果学过OOP的编程语言可以把这个类比成类的构造函数其只会执行一次。

参数解释

void** appstate首先是一个二级指针,其指向的指针是一个结构体指针,其的作用主要是管理程序的相关状态。里面可以管理如**窗口(window)、渲染器(renderer)**一类的状态信息。

int argc, char* argv[]这些是C/C++的传统命令行参数。

事件响应函数

1
SDL_AppResult SDL_AppEvent(void* appstate,SDL_Event* event)

这个函数会在有事件(如鼠标点击、键盘输入、QUIT退出)时调用。

参数解释

void* appstate是一个指针传入APP的状态管理参数
SDL_Event* event 负责传入APP当前响应的事件

帧渲染函数(主函数)

1
SDL_AppResult SDL_AppIterate(void* appstate)

这个函数会在每一帧运行一次,有点类似传统的while死循环方法。
里面会处理你程序运行时的各种事件,当然也包括图形渲染。

后处理函数

1
void SDL_AppQuit(void* appstate,SDL_AppResult result)

这个函数会在程序生命结束时调用,SDL会帮我们清理渲染和屏幕。同样的学过OOP编程语言的可以拿析构函数类比

参数解释

SDL_AppResult result这个会传入程序的运行结构,如SUCCESS或者FALIURE

创建窗口

前置条件

  • 安装了SDL3环境
  • 具备基本的C/C++知识

知识补充

  • 屏幕坐标系

屏幕坐标系是建立在屏幕上的二维坐标系,以像素为单位。屏幕的左上角为原点,水平方向为X轴,向右为正;垂直方向为Y轴,向下为正。

  • 计算机是如何渲染视频的

我们看到的视频实际上有由一系列连续的图像形成的,我们把每一张图像叫做帧(fame)

书写代码

首先定义宏来启用新式写法

1
#define SDL_MAIN_USE_CALLBACKS 1

现在引入头文件

1
2
#include<SDL3/SDL.h>
#include<SDL3/SDL_main.h>

创建窗口也遵循传统的 ‘声明-初始-使用’ 的基本形式。
现在定义两个全局变量

1
2
static SDL_Window* window = NULL;
static SDL_Renderer* renderer = NULL;

现在我们可以开始写第一个函数了

1
2
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* agrv[]) {
}

首先我们需要调用
SDL_SetAppMetadata( )函数来初始化程序的名称等。

SDL_SetAppMetadata拥有三个参数(程序名称,版本信息,标识符)用于设置APP的元数据

这里使用官方的Demo名称

1
SDL_SetAppMetadata("Example Renderer Clear", "1.0", "com.example.renderer-clear");

下面来初始化窗口和渲染器

1
2
3
4
5
6
7
8
9
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL :%s", SDL_GetError());
return SDL_APP_FAILURE;
}
/*此处Windowsflag参数被设置为0 表示默认设置*/
if (!SDL_CreateWindowAndRenderer("examples/renderer/clear", 640, 480, 0,&window ,&renderer)) {
SDL_Log("Couldn't create window/renderer %s", SDL_GetError());
return SDL_APP_FAILURE;
}

这里使用了SDL_Init()SDL_CreateWindowAndRenderer()其实我们可以顾名思义一个是用来初始化权限的一个是用来创建窗口和渲染器的。
SDL_GetError()会返回字符串内容是错误信息。
SDL_Log()是SDL内置的日志打印函数。

这里需要提一下SDL_Init()函数是使用|与运算符来申请多个权限的比如

1
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO);

下面是完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*这个函数会在开始时执行一次*/
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* agrv[]) {

/*设置元数据*/
SDL_SetAppMetadata("Example Renderer Clear", "1.0", "com.example.renderer-clear");

if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL :%s", SDL_GetError());
return SDL_APP_FAILURE;
}
/*此处Windowsflag参数被设置为0 表示默认设置*/
if (!SDL_CreateWindowAndRenderer("examples/renderer/clear", 640, 480, 0,&window ,&renderer)) {
SDL_Log("Couldn't create window/renderer %s", SDL_GetError());
return SDL_APP_FAILURE;
}

return SDL_APP_CONTINUE;
}

下面的函数很简单先略过

1
2
3
4
5
6
7
8
/*这个函数会在有事件(如:鼠标、键盘输入)时调用*/
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {

if (event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS; /*向操作系统传递程序成功执行*/
}
return SDL_APP_CONTINUE;
}

下面开始主函数SDL_AppResult SDL_AppIterate(void* appstate)

1
2
3
4
5
6
7
8
9
10
11
12
/*下面设置渲染器*/
SDL_AppResult SDL_AppIterate(void* appstate){
SDL_SetRenderDrawColorFloat(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE_FLOAT);

/*下面clear上一次渲染的内容*/
SDL_RenderClear(renderer);

/*现在把新的渲染器放到屏幕上*/
SDL_RenderPresent(renderer);

return SDL_APP_CONTINUE;
}

现在解释一下SDL_SetRenderDrawColorFloat()函数其接受渲染器和颜色(RGB)来设置渲染器颜色。SDL_ALPHA_OPAQUE_FLOAT是一个官方宏,表示不透明且是浮点型的参数。

最后结尾

1
2
3
void SDL_AppQuit(void* appstate,SDL_AppResult result) {
/*pass*/
}

写在后面

Q&A

为什么第一个SDL_AppResult SDL_AppInit(void** appstate, int argc, char* agrv[])appstate参数是二级指针?

因为这个函数是初始化函数,其目的是为了储存开发创建的状态对象
其他函数只会修改这个对象的内容而不是这个对象所以是一级指针

为什么没有使用appstate参数?

官方说法是appstate会更方便开发者管理,尤其对于大项目,但是我使用着十分麻烦,而且我们就只是创建一个窗口,所以没有使用。

你说话好啰嗦啊?

你真是个小天才(开玩笑)
其实主要面向的是刚学完C语言(不严谨就是会一点)的想图形化的同学写的。

感受

本人技术也不是很好,但还是想说两句。
学习这个SDL3其实最好的教程已经放在官方的Demo里面了,所以剩下的就是啃接口文档了。

如有不妥,敬请指教

其他文章
请输入关键词进行搜索