第四课: SDL拓展库
博客专区 > geange 的博客 > 博客详情
第四课: SDL拓展库
geange 发表于1个月前
第四课: SDL拓展库
  • 发表于 1个月前
  • 阅读 6
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 十分钟定制你的第一个小程序>>>   

Lesson 3: SDL拓展库

到目前为止, 我们只使用 bmp 图像, 因为它们是基础 SDL 库支持的唯一类型, 但使用 bmp 图像并不太好。幸运的是, 有一组 sdl 扩展库为 sdl 添加了有用的功能, 例如通过 SDL_image 支持各种类型的图像。其他可用的库:SDL_ttf提供了字体渲染支持,SDL_net提供低级别网络支持,还有提供多声道音频播放的SDL_mixer。

安装拓展库

在本课中, 我们只需要 SDL_image, 但所有扩展库的安装过程都是相同的。从 "拓展库的项目" 页面下载系统的开发库, 并按照下面的说明在系统上设置 SDL_image。

  • Windows: (MinGW or Visual Studio): Merge the extension library’s files into your existing SDL2 directory. You’ll also need to copy the SDL2_image, zlib and any image format dlls (such as libpng) over to your executable directory so that they’re available at runtime

  • Linux: 从您的软件包管理器中安装, 或者下载源代码并使用 CMake 进行构建。

  • Mac: Download the .dmg from the site and follow the Readme.

要使用库, 您需要更新包含和生成系统以链接到新的扩展库。

设置平铺块属性

在本课中, 我们将了解如何使用 SDL_image 加载图像, 以及如何在绘制纹理时进行缩放, 并实现一种根据窗口大小和平铺大小来放置平铺块的方法。要做到这一点, 我们需要定义一个平铺块的大小常数, 我们将添加到我们的窗口常数。

const int SCREEN_WIDTH  = 640;
const int SCREEN_HEIGHT = 480;
//We'll just be using square tiles for now
const int TILE_SIZE = 40;

使用SDL_image加载纹理

SDL_image 让我们可以加载多种类型的图像, 并允许我们使用IMG_LoadTexture将图像直接加载到一个SDL_Texture上。有了这个功能, 几乎所有的 loadTexture 代码都可以被替换, 现在我们调用 IMG_LoadTexture 来加载纹理, 检查错误并返回。我们仍然可以使用 logSDLError打印来自SDL_image库返回的错误,因为 IMG_GetError 函数只是定义了SDL_GetError。

/**
* Loads an image into a texture on the rendering device
* @param file The image file to load
* @param ren The renderer to load the texture onto
* @return the loaded texture, or nullptr if something went wrong.
*/
SDL_Texture* loadTexture(const std::string &file, SDL_Renderer *ren){
	SDL_Texture *texture = IMG_LoadTexture(ren, file.c_str());
	if (texture == nullptr){
		logSDLError(std::cout, "LoadTexture");
	}
	return texture;
}

指定呈现的宽度和高度

使用SDL2,我们可以通过指定目标矩阵的高宽(不同于纹理的高宽)缩放纹理。然而,这也是经常被用于没有缩放的纹理的绘制,这将是很麻烦的事情,如果我们不想绘制缩放的纹理必须要制定高宽。为了解决这个问题, 我们将创建两个版本的 renderTexture。一个将可以设置宽度和高度与其他参数, 而另一个版本将镜像我们的原始, 将使用纹理的宽度和高度的目标。

要设置绘图的纹理宽度和高度, 我们只需将所需的宽度和高度写入目标矩形的宽度和高度字段, 而不是从纹理中获取它们。

/**
* Draw an SDL_Texture to an SDL_Renderer at position x, y, with some desired
* width and height
* @param tex The source texture we want to draw
* @param ren The renderer we want to draw to
* @param x The x coordinate to draw to
* @param y The y coordinate to draw to
* @param w The width of the texture to draw
* @param h The height of the texture to draw
*/
void renderTexture(SDL_Texture *tex, SDL_Renderer *ren, int x, int y, int w, int h){
	//Setup the destination rectangle to be at the position we want
	SDL_Rect dst;
	dst.x = x;
	dst.y = y;
	dst.w = w;
	dst.h = h;
	SDL_RenderCopy(ren, tex, NULL, &dst);
}

我们还将创建一个函数, 它提供了在不进行任何缩放的情况下绘制纹理的旧功能。这个函数只会得到纹理的宽度和高度, 然后调用我们的新 renderTexture 函数。

/**
* Draw an SDL_Texture to an SDL_Renderer at position x, y, preserving
* the texture's width and height
* @param tex The source texture we want to draw
* @param ren The renderer we want to draw to
* @param x The x coordinate to draw to
* @param y The y coordinate to draw to
*/
void renderTexture(SDL_Texture *tex, SDL_Renderer *ren, int x, int y){
	int w, h;
	SDL_QueryTexture(tex, NULL, NULL, &w, &h);
	renderTexture(tex, ren, x, y, w, h);
}

初始化SDL_image(可选)

当第一次加载映像时, SDL_image 将自动初始化必要的映像加载子系统, 但是这将导致加载映像时出现一些延迟, 因为 SDL_image 将必须首先执行其初始化设置。如果您想先初始化 SDL_image, 以避免在第一次加载图像类型时出现延迟, 您可以使用 IMG_Init。IMG_Init 将返回所有当前初始化的映像加载器的位掩码, 因此我们将执行一个和我们设置的标志, 以查看初始化是否成功。在这里, 我们只初始化 PNG 加载器。初始化 SDL 后, 应进行此初始化。

if ((IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) != IMG_INIT_PNG){
	logSDLError(std::cout, "IMG_Init");
	SDL_Quit();
	return 1;
}

然后, 我们像在2课打开一个窗口和渲染器一样。

加载纹理

因为这个课程是演示加载 png, 我们将使用新的图像为我们的背景和前景。我们还将通过使用具有透明背景的前景图像在平铺背景上绘制, 来演示 PNG 透明度的。

平铺背景

前景图

图像的加载跟loadTexture功能完全一样。请确保更新文件路径以匹配您的项目结构。

const std::string resPath = getResourcePath("Lesson3");
SDL_Texture *background = loadTexture(resPath + "background.png", renderer);
SDL_Texture *image = loadTexture(resPath + "image.png", renderer);
//Make sure they both loaded ok
if (background == nullptr || image == nullptr){
	cleanup(background, image, renderer, window);
	IMG_Quit();
	SDL_Quit();
	return 1;
}

平铺背景操作

由于我们的图像是比窗口要小, 现在我们需要很多超过4个相同背景图覆盖整个屏幕,确定图像的位置是一个真正的痛苦。我们将使用数学计算图片的摆放的位置,用来填充屏幕。

我们可以通过将 SCREEN_WIDTH 除以 TILE_SIZE 来确定每行需要多少块图片。同样,对于每列多少个,我们也可以类似操作。因为我们将填补一个正方形的瓷砖面积瓷砖的总数将 tiles_per_row * tiles_per_col。我们可以使用单个for循环来遍历所有的图块, 或者嵌套for循环以在每行中为每行填充一行。我选择了一个单一的循环。

在循环中, 我们计算图块右上角坐标的x和y, 以确定它应该放置在哪里。由于我们在该方法中按行绘制行, 因此 x 索引将重复每一行, 而 y 索引将在每行填充后递增, 我们向下移动到下一行。因此, 我们可以使用绝对平铺索引成计算 x 索引, 每行平铺数: x = tile_idx% tiles_per_row。

例如, 如果我们绘制的是一个2x2 的瓷砖网格, 我们希望平铺0具有与平铺2相同的 x 索引, 因为: 0% 2 = = 0 和 2% 2 = = 0。

在放置了整行平铺后, y 索引应增加, 因此每个 tiles_per_row 平铺。由于我们使用整数, 我们可以利用整数截断, 并计算为 y = tile_idx/tiles_per_row。因此, 在我们的2x2 网格示例: 0 行将有瓷砖0和 1: 0/2 = = 0 和 1/2 = = 0, 而行1将有瓷砖2和 3: 2/2 = = 1 和 3/2 = = 1,从而获得正确的y指数。

剩下要做的就是将索引转换为图块的像素坐标, 这是通过将 x 和 y 指数乘以平铺块大小来完成的, 而我们的平铺块循环完成了!

注意: 所有这些渲染代码将放在我们的主循环中, 类似于第1课。

//Determine how many tiles we'll need to fill the screen
int xTiles = SCREEN_WIDTH / TILE_SIZE;
int yTiles = SCREEN_HEIGHT / TILE_SIZE;

//Draw the tiles by calculating their positions
for (int i = 0; i < xTiles * yTiles; ++i){
	int x = i % xTiles;
	int y = i / xTiles;
	renderTexture(background, renderer, x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE,
		TILE_SIZE);
}

绘制前景

我们的前景图象以前一样绘制, 位于屏幕中心。

int iW, iH;
SDL_QueryTexture(image, NULL, NULL, &iW, &iH);
int x = SCREEN_WIDTH / 2 - iW / 2;
int y = SCREEN_HEIGHT / 2 - iH / 2;
renderTexture(image, renderer, x, y);

然后, 我们将呈现渲染器, 并等待几秒钟后退出, 就像我们在2课做的一样。

SDL_RenderPresent(renderer);
SDL_Delay(2000);

清理内存

清理是相同的, 在2课与一条添加线退出 SDL_image 通过调用 IMG_Quit。

cleanup(background, image, renderer, window);
IMG_Quit();
SDL_Quit();

结束

如果一切顺利, 你应该看到类似下面的窗口。

共有 人打赏支持
粉丝 0
博文 18
码字总数 9453
×
geange
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: