第四课: SDL拓展库

Lesson 3: SDL拓展库

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

安装拓展库

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

<!-- In this lesson we’ll just need SDL_image but the installation process for all the extension libraries is the same. Download the development library for your system from the project page and follow the instructions below to get SDL_image set up on your system. -->

  • 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课打开一个窗口和渲染器一样。

<!-- We then open a window and renderer the same as we did in lesson 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();

结束

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

转载于:https://my.oschina.net/geange/blog/1533736

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值