【SDL游戏编程入门第二卷】第一个SDL窗口

一、前言

之前我们已经设置了 SDL,是时候制作一个在屏幕上呈现四边形的 SDL 图形应用程序了。

二、第一个 SDL 窗口

我们在上一卷的基础上编写,我会对重要的地方做出适当的解释。

// 使用 SDL 和 iostream
#include <SDL.h>
#include <iostream>

// 链接库
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2main.lib")

// 屏幕尺寸常量
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

在源文件顶部,我们包含 SDL.h,因为我们需要 SDL 函数和数据类型,还将包含 iostream 用来打印日志信息到控制台。

这里我在源代码中链接了库,你也可以在项目的属性中设置,这里我选择了我更容易看到的地方,但如果是一个比较大的工程,我可能会在这两者中考虑一会。

最后声明窗口的宽度和高度

接下来是 main 函数部分

/* 此源代码版权归 AnnihilateSword (2022-*)所有,未经书面许可不得转载。*/

// 使用 SDL 和 iostream
#include <SDL.h>
#include <iostream>

// 链接库
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2main.lib")

// 屏幕尺寸常量
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

int main(int argc, char* argv[])  // 必须要填写此参数,不然会出现链接错误
{
	// 定义 SDL 窗口
	SDL_Window* window = nullptr;
	// 初始化 SDL
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		std::cout << "[Error]: SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
	}
	else
	{
		// 创建窗口
		SDL_Window* window = SDL_CreateWindow("HelloSDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
		if (window == nullptr)
		{
			std::cout << "[Error]: Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
		}
		else
		{
			// 获取窗口所包含的表面
			SDL_Surface* screenSurface = SDL_GetWindowSurface(window);

			// 将表面填充为蓝色
			SDL_FillRect(screenSurface, nullptr, SDL_MapRGB(screenSurface->format, 51, 76, 204));

			// 更新表面
			SDL_UpdateWindowSurface(window);

			// 窗口循环
			SDL_Event e;
			bool quit = false;
			while (quit == false)
			{
				while (SDL_PollEvent(&e))
				{
					if (e.type == SDL_QUIT)
						quit = true;
				}
			}
		}
	}
	// 销毁窗口
	SDL_DestroyWindow(window);
	// 退出 SDL
	SDL_Quit();
	return 0;
}

1. 代码解释

a. 窗口创建部分

这里要注意的是:我们要让入口函数(这里是 main)的参数是一个整数,后跟一个 char* 数组,返回值是一个整数。任何其他 main 函数的类型将导致对 main 的未定义引用。SDL 需要这种类型的主设备,以便与多个平台兼容。

声明窗口和屏幕表面后,我们初始化 SDL。如果不先初始化 SDL,则无法调用任何 SDL 函数。因为我们所关心的只是使用 SDL 的视频 子系统,我们只会传递它 SDL_INIT_VIDEO 标志。

SDL_Init() 函数用来初始化 SDL

参考官方文档:
成功返回 0,失败返回负;可以使用 SDL_GetError() 获取更多信息。
Returns 0 on success or a negative error code on failure; call SDL_GetError() for more information.

说到 API,最重要的就是文档,许多库官方都有提供文档(只能说大部分,因为我确实遇到过没有文档的...)

SDL参考文档

SDL_GetError 是一个非常有用的功能。每当出现问题时,您都需要知道原因。SDL_GetError 会通知您 SDL 函数内部是否发生任何错误。

接下来就是创建窗口了,SDL_CreateWindow 函数的参数需要提供窗口的位置、宽高、标志

这里代码很简单,我们创建了一个标题是 "HelloSDL",位置让 SDL 决定所以填写 SDL_WINDOWPOS_UNDEFINED,宽高是我们声明的 640x480,最后指定 SDL_WINDOW_SHOW 让窗口正常显示,可以用 OR 运算指定多个标志。

b. 窗口表面设置

如果窗口创建成功,我们还希望获取窗口的表面,以便我们可以绘制到它。

获取窗口的表面使用 SDL_GetWindowSurface

然后我们用 SDL_FillRect 函数使用特定颜色快速填充矩形。SDL_MapRGB 的第一个参数是描述像素格式的 SDL_PixelFormat 结构,我们直接使用窗口表面的格式。

关于渲染,需要了解的重要一点是,仅仅因为我们在屏幕表面绘制了某些内容并不意味着你会看到它。完成所需的所有绘图后,我们还需要更新窗口,使其显示您绘制的所有内容。

所以还需调用 SDL_UpdateWindowSurface 更新窗口表面

c. 窗口循环

如果我们现在运行程序,窗口会一闪而过,一般我们都会用一个循环让程序持续运行,直到我们想要关闭,游戏不就是这样吗。

这里使用了 SDL_PollEvent 轮询处理事件,之后会放在单独的模块介绍事件系统,我不想在这里花大量篇幅介绍,让我们保持简单

参考文档
如果有可获取的事件返回 1,如果没有返回 0
Returns 1 if there is a pending event or 0 if there are none available.

d. 销毁窗口

最后,在我们结束循环后,我们将销毁窗口以释放其内存,并且退出 SDL

2. 运行结果

三、Remarks

这一节花了较长篇幅来讲解 API,不过我推荐大家多看官方文档,我这里解释的 API 大部分其实都是查阅官方文档的解释,之后我会缩短单一解释 API 的篇幅(因为文档上都能找到)

如果是比较重要的或是难以理解的地方,我会写在文章中,所以,在之后的篇章中,我打算尽量减少不必要的解释,我认为这样能让我们更轻松。

不知道你们的想法是怎样,可以给我评论或留言。



本节内容就到这里了,下卷会继续分享 SDL 的基本使用

The End.

posted @ 2022-12-07 16:36  AnnihilateSword  阅读(112)  评论(0编辑  收藏  举报