SDL2的学习之路<二>加载图片

在SDL2窗口中加载图片

上节记录了如何利用SDL2库去创建一个黑洞洞的窗口,游戏主体是显示各种图片以及对图片的移动,旋转,缩放等操作,所以这节来记录如何去加载显示图片。

显示星星

准备好一张图片,png,bmp,jpeg等等都可以,sdl2的扩展库SDL_image支持非常多的图片格式,具体可以参考官方文档或者SDL_image.h文件。
在这里插入图片描述
这里提供的是一张带有黑色背景的jpg格式星星图片,不喜欢的可以自己去替换,正好我们目前创建的是黑色背景的窗口,放进去后看着还是毫无违和感的。
这是我做的效果
加载以及显示图片的三个关键函数是:
IMG_LoadTexture:加载图片,两个参数,第一个是上节创建的渲染结构SDL_Render*;第二个参数就是图片资源的路径,可以是绝对路径也可以是相对路径
SDL_QueryTexture:查询纹理信息,主要用来查询宽度和高度,显示以及切割图片的时候会用。(这个就没有sfml方便,多一步操作)
SDL_RenderCopy:拷贝纹理到渲染器(双缓冲机制,先拷贝数据,然后一次性绘制,避免闪烁)。这里的第二个参数是刚才加载出来的纹理指针,第三个参数是图片裁剪采用到的位置尺寸信息(就是只渲染用到图片的从指定位置开始的一个多长多宽的矩形,一般做游戏都会用到,将多张图片集合放在一张图片上,能够加快加载速度),不裁剪的时候传NULL就可以,第四个参数就是渲染到哪个位置,可以在这个时候更改显示的宽高,这样就可以自由设置图片显示的大小(像我提供的星星图片太大,128个像素,显示出来不好看,我就会设置成最大宽高都是68个像素,可以自由设置)。
这里先只介绍这个接口,后面渲染图片,需要旋转,镜像等操作时,会用到更复杂点的接口,后面再介绍。
上代码:

//...这里接上节代码,不重复了
SDL_Renderer* render = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED);
//加载图片
SDL_Texture* tex = IMG_LoadTexture(render, "star.jpg");
if (tex == nullptr) return -1;
//查询图片尺寸(SDL大部分接口是返回0为成功,具体在使用时看接口申明)
int texw = 0, texh = 0;
if (0 != SDL_QueryTexture(tex, NULL, NULL, &texw, &texh)) 
	return -1;
//...这里接上节开始渲染部分,中间省略
//更新完可以渲染界面了
SDL_RenderClear(render);
SDL_Rect des = { 100, 100, texw, texh };
SDL_RenderCopy(render, tex, NULL, &des);

编译运行,好了,星星被创造显示出来了,关键代码就这么多,看起来还是很简单是不是。but…这只是最简单的显示一张图片。既然是游戏,当然不能只是这么静静的显示一张图片,不急,一步步来,先让星星闪起来。

让星星忽大忽小的闪烁

//星星坐标
SDL_Point stars[] = {
		{ 100, 100 },
		{ 90 , 247 },
		{ 156 , 25 },
		{ 298 , 325 }
	};

//半衰期,最大尺寸
const int halfLife = 8, maxSize = 48;
//当前缩放
Uint8 zoom = 0;
//上次更新时间
Uint32 tickLast = 0;

先随意选择几个点来显示星星。
简单的逻辑就是随着时间的迁移,星星的尺寸慢慢变小,到我们设定的最小值时再慢慢变大,到最大值时再慢慢变小,当然你也可以自定义自己的条件。

//...这里接上节在更新的地方
//这里可以开始更新事件
//onUpdate();
auto tick = SDL_GetTicks();
// 每100ms变化一次大小
if (SDL_TICKS_PASSED(tick, tickLast + 100)) {
	zoom++;
	zoom %= halfLife * 2;
	tickLast = tick;
}

// 计算星星当前大小
SDL_Rect des;
int z = halfLife - abs(halfLife - zoom);
des.w = maxSize - z * 2;
des.h = des.w;		
// 循环显示每个星星
for (int i = 0; i < sizeof(stars) / sizeof(SDL_Point); i++) {			
	des.x = stars[i].x - des.w / 2;
	des.y = stars[i].y - des.h / 2;
	engine.displayTexture(tex, des);
	SDL_RenderCopy(render, tex, NULL, &des);
}

这样就能看到星星在随着时间的迁移而忽大忽小了,是不是有了点感觉?

完整代码

分段代码可能大家看的不习惯,下面贴上完整代码,只是这代码跟上面有点区别,我把渲染相关的集合到一个类里面去了,然后就是这也只是自己的demo,加了一个Star类去更新星星,在鼠标点击的时候在鼠标点击位置生成一个星星,有几个参数都是待完善的功能,感兴趣的可以看看,大神可以跳走也可以指导下。
目前都放在一个cpp,是为了方便复制过来,方便大家看代码,后面如果demo写的多会分类整理,放到git仓库(如果大家感兴趣且需要)

#include "stdafx.h"
#include "SDL.h"
#include "SDL_image.h"
#include "SDL_ttf.h"
#include <string>
#include <unordered_map>
#include <vector>

#pragma comment(lib,"SDL2.lib")
#pragma comment(lib,"SDL2main.lib")
#pragma comment(lib,"SDL2_image.lib")
#pragma comment(lib,"SDL2_ttf.lib")
/*
********* Engine **************************************************************/
struct TextureInfo {
	SDL_Texture* texture = nullptr;
	int w, h;
};

class Engine {
public:
	bool onInit(std::string title, int w, int h);
	inline SDL_Renderer* getRender() { return m_render; }
	void shutdown();
	inline bool isQuit() { return m_quit; }
	inline void setFrame(int frame) { m_frame = frame; }

	void setClearColor(Uint8 r, Uint8 g, Uint8 b);
	void setClearColor(SDL_Color color);
	void startRender();
	void endRender();
	void displayTexture(TextureInfo* tex, SDL_Point point);
	void displayTexture(TextureInfo* tex, SDL_Rect rect);

	void drawRect(SDL_FRect rect, SDL_Color color = { 255, 255, 255, 255 });

	int pollEvent(SDL_Event& event);

	TextureInfo* getTextureInfo(std::string file);

private:
	SDL_Window* m_window = nullptr;
	SDL_Renderer*	m_render = nullptr;
	SDL_Color m_clearColor = { 0, 0, 0, 255 };
	bool m_quit = false;
	int m_frame = 60;
	Uint32 m_lastFrameTick = 0;

	std::unordered_map<std::string, TextureInfo> m_textureList;
};

bool Engine::onInit(std::string title, int w, int h)
{
	SDL_Init(SDL_INIT_EVERYTHING);
	m_window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		w, h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
	if (m_window == nullptr) return false;

	m_render = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED);
	if (m_render == nullptr) return false;

	return true;
}

void Engine::shutdown()
{
	for (auto it = m_textureList.begin(); it != m_textureList.end(); it++) {
		if (it->second.texture) {
			SDL_DestroyTexture(it->second.texture);
			it->second.texture = nullptr;
		}
	}
	m_textureList.clear();

	if (m_render) SDL_DestroyRenderer(m_render);
	if (m_window) SDL_DestroyWindow(m_window);
	SDL_Quit();
}

void Engine::setClearColor(Uint8 r, Uint8 g, Uint8 b)
{
	m_clearColor.r = r;
	m_clearColor.g = g;
	m_clearColor.b = b;
}

void Engine::setClearColor(SDL_Color color)
{
	m_clearColor.r = color.r;
	m_clearColor.g = color.g;
	m_clearColor.b = color.b;
}

void Engine::startRender()
{
	if (!m_render) return;
	SDL_SetRenderDrawColor(m_render, m_clearColor.r, m_clearColor.g, m_clearColor.b, m_clearColor.a);
	SDL_RenderClear(m_render);
}

void Engine::endRender()
{
	if (!m_render) return;
	SDL_RenderPresent(m_render);

	auto tick = SDL_GetTicks();
	int tickStep = tick - m_lastFrameTick;
	tickStep = 1000 / m_frame - tickStep;
	if (tickStep > 0)
		SDL_Delay(tickStep);
	m_lastFrameTick = tick;
}

void Engine::displayTexture(TextureInfo* tex, SDL_Point point)
{
	if (!m_render) return;
	SDL_Rect des = { point.x, point.y, tex->w, tex->h };
	SDL_RenderCopy(m_render, tex->texture, NULL, &des);
}

void Engine::displayTexture(TextureInfo* tex, SDL_Rect rect)
{
	if (!m_render) return;
	SDL_RenderCopy(m_render, tex->texture, NULL, &rect);
}

void Engine::drawRect(SDL_FRect rect, SDL_Color color)
{
	if (!m_render) return;
	Uint8 a, r, g, b;
	SDL_GetRenderDrawColor(m_render, &r, &g, &b, &a);
	SDL_SetRenderDrawColor(m_render, color.r, color.g, color.b, color.a);
	SDL_RenderDrawRectF(m_render, &rect);
	SDL_SetRenderDrawColor(m_render, r, g, b, a);
}

int Engine::pollEvent(SDL_Event & event)
{
	int ret = SDL_PollEvent(&event);
	if (event.type == SDL_QUIT)
		m_quit = true;
	return ret;
}

TextureInfo* Engine::getTextureInfo(std::string file)
{
	if (m_render == nullptr) return nullptr;

	auto itFind = m_textureList.find(file);
	if (itFind == m_textureList.end()) {
		SDL_Texture* tex = IMG_LoadTexture(m_render, file.c_str());
		if (tex == nullptr) return nullptr;
		int texw = 0, texh = 0;
		if (0 != SDL_QueryTexture(tex, NULL, NULL, &texw, &texh)) return nullptr;
		m_textureList[file].texture = tex;
		m_textureList[file].w = texw;
		m_textureList[file].h = texh;
		return &m_textureList[file];
	}

	return &itFind->second;
}

// 单例引擎,便于全局获取
class SingleEngine {
public:
	static Engine& getEngine();
};

Engine& SingleEngine::getEngine() {
	static Engine engine;
	return engine;
}
/********** Engine **************************************************************/

/********** Star **************************************************************/
class Star {
public:
	Star();
	void setPoint(int x, int y);
	void onUpdate();
	SDL_Rect getDesRect();
private:
	SDL_Point m_point;					//中心位置
	Uint8 m_maxSize = 68;				//最大尺寸
	Uint8 m_minSize = 12;				//最小尺寸
	Uint8 m_curSize = 0;				//当前尺寸
	Uint32 m_createTime = 0;			//创建时间
	Uint32 m_flashCycle = 3000;			//闪烁周期(最大到最小然后恢复到最大的时间,单位:ms)
	Uint8 m_flashCount = 0;				//闪烁次数
	Uint32 m_lastTick = 0;				//上次更新时间
	Uint8 m_interval = 0;				//更新间隔
	bool m_add = false;					//当前是否递增状态
};

Star::Star()
{
	m_createTime = SDL_GetTicks();
	m_lastTick = 0;
	m_curSize = m_maxSize;
}

void Star::setPoint(int x, int y)
{
	m_point.x = x;
	m_point.y = y;
}

void Star::onUpdate()
{
	auto tick = SDL_GetTicks();
	//每次衰减/递增2个像素
	const Uint8 unit = 2;
	if (m_interval == 0) {
		//需要的帧数
		Uint8 frame = (m_maxSize - m_minSize) * 2 / unit;
		//每帧的间隔
		m_interval = m_flashCycle / frame;
	}
	
	if (SDL_TICKS_PASSED(tick, m_lastTick + m_interval)) {
		if (m_curSize <= m_minSize) m_add = true;
		if (m_curSize >= m_maxSize) m_add = false;
		if (m_add) m_curSize += unit;
		else m_curSize -= unit;
		m_lastTick = tick;
	}
}

SDL_Rect Star::getDesRect()
{
	SDL_Rect rect;
	rect.w = m_curSize;
	rect.h = m_curSize;
	rect.x = m_point.x - m_curSize / 2;
	rect.y = m_point.y - m_curSize / 2;
	return rect;
}
/********** Star **************************************************************/

int main(int, char**)
{
	auto& engine = SingleEngine::getEngine();
	if (false == engine.onInit("", 800, 600))
		return -1;

	auto render = engine.getRender();
	auto tex = engine.getTextureInfo("images/star.jpg");
	if (nullptr == tex)
		return -1;

	//engine.setClearColor(255, 255, 255);
	engine.setFrame(60);
	SDL_Point stars[] = {
		{ 100, 100 },
		{ 90 , 247 },
		{ 156 , 25 },
		{ 298 , 325 }
	};

	std::vector<Star> starList;

	const int halfLife = 8, maxSize = 48;
	Uint8 zoom = 0;
	Uint32 tickLast = 0;

	SDL_Event event;
	while (!engine.isQuit())
	{
		while (engine.pollEvent(event))
		{
			if (event.type == SDL_KEYDOWN
				&& event.key.keysym.sym == SDLK_1)
				;
			else if (event.type == SDL_MOUSEBUTTONDOWN) {
				int mousex = 0, mousey = 0;
				Uint32 buttons = SDL_GetMouseState(&mousex, &mousey);
				Star star;
				star.setPoint(mousex, mousey);
				starList.push_back(star);
			}
		}

		// update
		for (auto& it : starList)
			it.onUpdate();

		// render
		engine.startRender();
		
		auto tick = SDL_GetTicks();
		if (SDL_TICKS_PASSED(tick, tickLast + 100)) {
			zoom++;
			zoom %= halfLife * 2;
			tickLast = tick;
		}

		for (int i = 0; i < sizeof(stars) / sizeof(SDL_Point); i++) {			
			SDL_Rect des;
			int z = halfLife - abs(halfLife - zoom);
			des.w = maxSize - z * 2;
			
			des.h = des.w;			
			des.x = stars[i].x - des.w / 2;
			des.y = stars[i].y - des.h / 2;
			engine.displayTexture(tex, des);
		}

		for (auto& it : starList)
			engine.displayTexture(tex, it.getDesRect());

		engine.drawRect(SDL_FRect{ 10, 10, 15, 10 });
		
		engine.endRender();
	}
	
	engine.shutdown();

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值