`
public0821
  • 浏览: 238046 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

SDL游戏开发教程05(显示文字和其他格式的图片)

阅读更多

     本节将介绍如何加载其他格式的图片,同时,介绍如何显示文字。效果图如下
 

 
 其中背景图片是我从网上下载的一张JPG图片,已经将它上传在了附件中。

 

 

显示其它格式图片:

 

要显示png、jpg、gif等格式的图片,我们需要下载SDL_image库

http://www.libsdl.org/projects/SDL_image/



 
 下载上图中标记出来的文件,然后解压。

1、将解压后include目录下的SDL_image.h文件拷贝到C:\MinGW\include\SDL目录下

2、将解压后include目录下的SDL_image.lib文件拷贝到C:\MinGW\lib目录下

3、将解压后include目录下的所有dll文件拷贝到C:\MinGW\bin目录下。

注意:我在这样做的时候出现了一个问题,当程序写好之后,在Eclipse下面运行报找不到驱动程序的错误,但自己手动进入到生成的EXE目录中双击运行程序没有问题。后来我将include目录下的所有dll文件拷贝到C:\WINDOWS\system32就好了,如果你也遇到和我一样的问题,可以参考这种解决方法。至于造成这个问题的原因,暂时还不清楚。

 

经过上面的准备,我们可以开始写代码了,其实加载图片的代码很简单。

SDLSurfacePtr SDLVideo::LoadImage(std::string fileName)
{
	SDL_Surface *surface = IMG_Load(fileName.c_str());
	if(NULL == surface)
	{
		throw SDLException(std::string("IMG_Load加载图片时发生错误:") + SDL_GetError());
	}
	return SDLSurfacePtr(new SDLSurface(surface));
}

 通过比较原来加载BMP格式图片的代码

SDLSurfacePtr	SDLVideo::LoadBMP(std::string file)
{
	SDL_Surface *surface = SDL_LoadBMP(file.c_str());
	if(NULL == surface)
	{
		throw SDLException(std::string("SDL_LoadBMP加载BMP图片时发生错误:") + SDL_GetError());
	}

	return SDLSurfacePtr(new SDLSurface(surface));
}

     其实就一个地方不同,一个用的是IMG_load函数,一个是SDL_LoadBMP函数。这里将加载图片的函数放到了SDLVideo类中。如果需要加载其他格式的图片调用LoadImage函数就可以了,同时LoadImage也可以加载BMP格式的图片,所以在这里LoadBMP函数基本上没什么用处了。

 

    我们下一步要做的就是封装一个专门加载图片的类SDLImageManager,用来对加载的图片进行管理,这里主要是对图片进行缓冲,避免频繁的访问硬盘,提高程序效率。头文件:

#ifndef RESOURCEMANAGER_H_
#define RESOURCEMANAGER_H_

#include "SDLSurface.h"
#include <map>
class SDLImageManager
{
	friend class SDL;
private:
	SDLImageManager();
public:
	virtual ~SDLImageManager();
public:
	//获取图片
	SDLSurfacePtr loadImage(std::string fileName);

	//获取图片,并将图片中的某种颜色去掉,使去掉的那部分颜色为透明,这样就不会覆盖掉底层图片的颜色
	//详细介绍见http://lazyfoo.net/SDL_tutorials/lesson05/index.php
	SDLSurfacePtr loadImageWithoutColor(std::string fileName, Uint8 r, Uint8 g, Uint8 b );
private:
	std::map<std::string, SDLSurfacePtr>	buffer;

};

#endif /* RESOURCEMANAGER_H_ */
 

其中loadImage用来获取一张图片,成员变量buffer用来缓冲已经加载的图片。loadImageWithoutColor的功能主要是去除图片的底色,避免底色覆盖掉底层图片的颜色,这样解释可能不好懂,我也没有什么更好的办法,大家可以去http://lazyfoo.net/SDL_tutorials/lesson05/index.php,上面有详细的介绍。

    源文件:

#include "SDLImageManager.h"
#include "SDLCore.h"
#include <boost/lexical_cast.hpp>

SDLImageManager::SDLImageManager()
{

}

SDLImageManager::~SDLImageManager()
{

}

SDLSurfacePtr SDLImageManager::loadImage(std::string fileName)
{
	//先到缓冲区去找
	std::map<std::string, SDLSurfacePtr>::iterator it = buffer.find(fileName);
	//如果找到就返回
	if(it != buffer.end())
	{
		return it->second;
	}


	//否则从硬盘加载图片
	SDLSurfacePtr loadedImage = SDL::video()->LoadImage( fileName.c_str() );

	//将文件格式调整成程序需要的格式
	SDLSurfacePtr image = SDL::video()->DisplayFormat( loadedImage );

	//放入缓冲区
	buffer.insert(std::make_pair(fileName, image));

	return image;
}

SDLSurfacePtr SDLImageManager::loadImageWithoutColor(std::string fileName, Uint8 r, Uint8 g, Uint8 b )
{
	/**
	 * 根据文件名和要去除的颜色生成一个唯一的键,
	 * 在缓冲区类同文件名,但一个是原图,一个是去除某种颜色的图可能共存,
	 * 所以这里用fileName作为键值有可能覆盖掉缓冲区中的原图。
	 * 存储每种颜色分量的空间是一个字节,所以最大数据不会超过255,这里简单的乘上1000就可以满足要求了。
	 * 比如这里传入的参数是aaa.jpg, 255, 255, 255;那么得到的结果就是aaa.jpg255255255。
	 * 这里boost::lexical_cast是boost里面一个常用的函数,用来做数据之间的转换。
	 */
	std::string key = fileName
		+ boost::lexical_cast<std::string>(r*1000*1000)
		+ boost::lexical_cast<std::string>(g*1000)
		+ boost::lexical_cast<std::string>(b);
	std::map<std::string, SDLSurfacePtr>::iterator it = buffer.find(key);
	if(it != buffer.end())
	{
		return it->second;
	}

	SDLSurfacePtr loadedImage = SDL::video()->LoadImage( fileName.c_str() );
	SDLSurfacePtr image = SDL::video()->DisplayFormat( loadedImage );
	Uint32 colorKey = SDL::video()->MapRGB(image->value()->format, r, g, b);
	SDL::video()->SetColorKey(image, SDL_SRCCOLORKEY, colorKey);
	buffer.insert(std::make_pair(key, image));

	return image;
}

 加载图片的相关准备工作已经完成。

 

 

显示文字:

要显示true type字体,我们需要下载SDL_ttf库。http://www.libsdl.org/projects/SDL_ttf/
 

 
   然后按照上面介绍SDL_image的方法,将下载下来的文件放到相应的目录下。这边不再重述。

为了管理字体,我们同上面一样也新建了一个SDLFontManager类

#ifndef SDLFONTMANAGER_H_
#define SDLFONTMANAGER_H_

#include "SDLFont.h"
class SDLFontManager
{
	friend class SDL;
private:
	SDLFontManager();
private:
	/**
	 * 初始化TTF环境
	 */
	void Init();
public:
	virtual ~SDLFontManager();
public:
	/**
	 * 打开字体
	 * name		字体文件路径
	 * size		待打开字体的大小
	 */
	SDLFontPtr OpenFont(std::string name, int size);
};

#endif /* SDLFONTMANAGER_H_ */
 
#include "SDLFontManager.h"

SDLFontManager::SDLFontManager()
{
	// TODO Auto-generated constructor stub

}

SDLFontManager::~SDLFontManager()
{
	TTF_Quit();//退出TTF环境
}

void SDLFontManager::Init()
{
	if(TTF_WasInit())
	{
		return;
	}

	if(-1 == TTF_Init())
	{
		throw SDLException(std::string("TTF_Init初始化TTF库时发生错误:") + SDL_GetError());
	}
}

SDLFontPtr SDLFontManager::OpenFont(std::string name, int size)
{
	TTF_Font *font = TTF_OpenFont(name.c_str(), size);
	if(NULL == font)
	{
		throw SDLException(std::string("TTF_OpenFont打开字体时发生错误:") + SDL_GetError());
	}
	return SDLFontPtr(new SDLFont(font));
}

 上面的代码很简单,初始化TTF环境 -> 打开字体 ->退出TTF环境

其中SDLFontPtr的定义为

typedef boost::shared_ptr<SDLFont> SDLFontPtr;

 由于字体打开后需要关闭,所以这里用了智能指针,在SDLFontPtr不再被使用的时候,它会调用SDLFont的delete函数。SDLFont的定义

#ifndef SDLFONT_H_
#define SDLFONT_H_
#include <SDL/SDL_ttf.h>
#include "boost/shared_ptr.hpp"
#include "SDLSurface.h"
#include <string>
#include "SDLException.h"
class SDLFont
{
	friend class SDLFontManager;
private:
	SDLFont(TTF_Font *font);
public:
	virtual ~SDLFont();
public:
	/**
	 * 渲染字体
	 * message	文字内容
	 * color	文字颜色
	 */
	SDLSurfacePtr RenderTextSolid(std::string message, SDL_Color color);
	SDLSurfacePtr RenderUNICODESolid(std::string message, SDL_Color color);
	SDLSurfacePtr RenderUNICODEBlended(std::string message, SDL_Color color);
	SDLSurfacePtr RenderUNICODEShaded(std::string message, SDL_Color fg, SDL_Color bg);

	/**
	 * 获取和设置字体的样式
	 */
	int  GetFontStyle();
	void SetFontStyle(int style);//参见SDL/SDL_ttf.h中的宏定义
private:
	/*
	 * 获取message中包含的Unicode字符个数
	 * 如果message="你好abc", 则返回5
	 * 如果message="你好啊", 则返回3
	 */
	int getUnicodeCharCountByGb2312(std::string message);
	/**
	 * 将GB2312编码转换成UNICODE编码
	 */
	Uint16 * getUnicodeByGb2312(std::string message);
private:
	TTF_Font *font;
};
typedef boost::shared_ptr<SDLFont> SDLFontPtr;
#endif /* SDLFONT_H_ */
#include "SDLFont.h"
#include <iostream>
SDLFont::SDLFont(TTF_Font *font)
{
	this->font = font;
}

SDLFont::~SDLFont()
{
	if (font != NULL)
	{
		TTF_CloseFont(font);
	}
}

SDLSurfacePtr SDLFont::RenderTextSolid(std::string message, SDL_Color color)
{
	SDL_Surface* image = TTF_RenderText_Solid(font, message.c_str(), color);
	if (image == NULL)
	{
		throw SDLException(std::string("TTF_RenderText_Solid渲染字体时发生错误:")
				+ SDL_GetError());
	}
	return SDLSurfacePtr(new SDLSurface(image));
}

SDLSurfacePtr SDLFont::RenderUNICODESolid(std::string message, SDL_Color color)
{
	Uint16 * text = getUnicodeByGb2312(message);
	SDL_Surface* image = TTF_RenderUNICODE_Solid(font, text, color);
	delete[] text;
	if (image == NULL)
	{
		throw SDLException(std::string("TTF_RenderUNICODE_Solid渲染字体时发生错误:")
				+ SDL_GetError());
	}
	return SDLSurfacePtr(new SDLSurface(image));
}


int SDLFont::getUnicodeCharCountByGb2312(std::string message)
{
	int		number = 0;
	for (unsigned int i = 0; i < message.length(); i++)
	{
		//判断最高位是不是0,如果是0表示是一个ASCII码,否则是一个汉字
		//一个汉字占用两个字符,所以变量i需要++
		if ((message.at(i) & 0x80) != 0)
		{
			i++;
		}
		number++;
	}

	return number;
}

Uint16 * SDLFont::getUnicodeByGb2312(std::string message)
{
		/**
		 * 获取字符串中包含的UNICODE字符数量,主要是为了后面转换时分配内存
		 * 这里其实不用精确具体的个数,只要保证chNum大于实际的个数就行了
		 * 因为一个汉字占用两个字节,所以这里用int chNum = message.length()也可以
		 * 如果message中不含汉字,则message.length()==getUnicodeCharCountByGb2312(message)
		 * 如果message含有汉字,则message.length()>getUnicodeCharCountByGb2312(message)
		 */
		int chNum = getUnicodeCharCountByGb2312(message);
		//int chNum = message.length();

		wchar_t * wmessage=new wchar_t[chNum];
		char *chset = setlocale(LC_CTYPE,"chs");//设置当前字符集,返回字符集的描述
		if(chset == NULL)//如果返回NULL,表示该环境中没有这种字符集
		{
			throw SDLException("setlocale设置当前环境为简体中文时发生错误,可能是系统不支持简体中文:");
		}

		//将GB2312转换成UNICODE字符集
		//返回实际转换的UNICODE字符个数
		int wchNum = mbstowcs(wmessage,message.c_str(),chNum);
		if(wchNum == -1)//如果里面包含了非法的GB2312字符编码
		{
			throw SDLException(std::string("mbstowcs中文字符转UNICODE时发生错误:"));
		}
		setlocale(LC_CTYPE,"");//设置回原来的字符集

		Uint16 *text = new Uint16[wchNum+1];
		text[wchNum] = 0;
		for (int i = 0; i < wchNum; i++)
		{
			text[i] = wmessage[i];
		}
		delete[] wmessage;

		return text;
}

SDLSurfacePtr SDLFont::RenderUNICODEBlended(std::string message, SDL_Color color)
{
	Uint16 * text = getUnicodeByGb2312(message);
	SDL_Surface* image = TTF_RenderUNICODE_Blended(font, text, color);
	delete[] text;
	if (image == NULL)
	{
		throw SDLException(std::string("TTF_RenderUNICODE_Blended渲染字体时发生错误:")
				+ SDL_GetError());
	}
	return SDLSurfacePtr(new SDLSurface(image));
}
SDLSurfacePtr SDLFont::RenderUNICODEShaded(std::string message, SDL_Color fg, SDL_Color bg)
{
	Uint16 * text = getUnicodeByGb2312(message);
	SDL_Surface* image = TTF_RenderUNICODE_Shaded(font, text, fg, bg);
	delete[] text;
	if (image == NULL)
	{
		throw SDLException(std::string("TTF_RenderUNICODE_Blended渲染字体时发生错误:")
				+ SDL_GetError());
	}
	return SDLSurfacePtr(new SDLSurface(image));
}

int  SDLFont::GetFontStyle()
{
	return TTF_GetFontStyle(font);
}

void SDLFont::SetFontStyle(int style)
{
	TTF_SetFontStyle(font, style);
}
 

    SDLFont对TTF库中的函数进行了封装,并且增加了对gb2312格式编码的处理。这里使用的规范是封装SDL库的成员函数开头字母大写,其余的成员函数开头字母小写 ,后面的章节中将继续采用该规范。

    在程序中,像std::string a="你好"这样的代码,编译器会根据系统的默认编码方式对"你好"这个字符串进行存储,我们现在用的系统都是简体中文,所以"你好"在内存中的编码方式一般为简体中文的方式,简体中文的编码方式有多种,这里给函数取的名字和写的注释都是GB2312,但实际的编码方式可能不是这个。只是GB2312比较常见,不管是GB2312还是GB其它的编码格式,mbstowcs都会将它转换成UNICODE编码格式。

    这里用mbstowcs进行编码转换,并不代表只有这一种方式,也不是因为它是最好的,而是由于它使用起来比较简单。网上有很过关于编码转换的讨论,也有一些开源的项目,其中比较出名的是libiconv。我们在这里进行编码转换单纯是为了显示中文,并且是以系统中有简体中文环境为前提,怎样使我们的程序能在其他语言环境中运行,需要对程序做国际化支持的考虑。这里推荐Gettext项目。

 

    上面怎么加载图片和显示文字的准备工作都已经做好,下一步是怎么使用这些类进行显示。代码如下

#include "Lesson02.h"

Lesson02::Lesson02()
{
	// TODO Auto-generated constructor stub

}

Lesson02::~Lesson02()
{
	// TODO Auto-generated destructor stub
}

void Lesson02::onRender()
{
	//先绘制背景图片
	SDL::video()->BlitSurface(image, NULL, screen, NULL);

	//将文字显示在screen的中央
	SDL_Rect rect;
	rect.x = screen->value()->w/2 - message->value()->w/2;
	rect.y = screen->value()->h/2 - message->value()->h/2;
	SDL::video()->BlitSurface(message, NULL, screen, &rect);
}
void Lesson02::onInit()
{
	//加载背景图片
	image = SDL::imageManager()->loadImage("E:\\code_picture\\464d64dd7512752d5982dd84.jpg");
	//创建字体
	SDLFontPtr font = SDL::fontManager()->OpenFont("E:\\code_picture\\wqy-zenhei.ttc", 20);
	//设置字体样式
	font->SetFontStyle(TTF_STYLE_UNDERLINE | TTF_STYLE_ITALIC);

	//渲染文字,这里的两种颜色一个是前景色,一个是背景色
	//渲染文字后返回的其实就是一张图片,和loadImage加载的图片没什么两样
	message = font->RenderUNICODEShaded("大家好(Hello, Everybody)"
			, SDL::assistant()->makeColor(0, 0, 0)
			, SDL::assistant()->makeColor(255, 0, 0));

	//大家可以试试下面注释掉的这两行代码,看看他们的不同效果
	/*message = font->RenderUNICODESolid("大家好(Hello, Everybody)", SDL::assistant()->makeColor(255, 0, 0));
	message = font->RenderUNICODEBlended("大家好(Hello, Everybody)", SDL::assistant()->makeColor(255, 0, 0));
*/

}

     由于我们前期的准备工作比较充分,所以到具体使用的时候代码就很简单,这里不再做解释。其中wqy-zenhei.ttc是文泉驿字体,网上有相关介绍,你也可以使用C:\WINDOWS\Fonts目录下随便一个可以支持中文的字体,比如simhei.ttf。

 

最后,需要添加连接选项,增加SDL_image和SDL_ttf,如下图:

 

最后重新编译整个工程即可。这里贴出来并不是全部代码,而是一些比较重要的代码,完整的代码见附件。

  • 大小: 71.4 KB
  • 大小: 9 KB
  • 大小: 9.6 KB
  • 大小: 48.9 KB
  • 大小: 687.5 KB
分享到:
评论

相关推荐

    SDL 游戏开发 经典教程

    【标签】"SDL":这是教程的核心内容,即Simple DirectMedia Layer,一个开源的库,用于处理游戏开发中的图形、音频、输入和其他多媒体元素。SDL支持多种操作系统,如Windows、Linux和macOS,使其成为跨平台游戏开发...

    SDL显示图片及文字

    在游戏开发或者图形用户界面编程中,图片和文字的显示是非常基本且重要的功能。在本示例中,我们将探讨如何使用Simple DirectMedia Layer(SDL)库在Visual Studio 2005环境下显示图片和文字。SDL是一个跨平台的...

    SDL游戏开发经典教程03源代码

    SDL(Simple DirectMedia Layer)是一个跨平台的多媒体库,它为开发人员提供了处理图形、音频和输入设备的能力,尤其适合游戏开发。这个“SDL游戏开发经典教程03源代码”是针对初学者的一个优秀资源,它帮助理解如何...

    lazyfoo SDL游戏入门教程 紫冀魔王 翻译版

    紫翼魔王 翻译版》是一份专为初学者设计的游戏开发教程,由知名游戏开发资源提供者lazyfoo的原版教程翻译而来,紫翼魔王负责了中文版的翻译工作,使得国内的学习者能够更加方便地理解和掌握SDL(Simple DirectMedia ...

    SDL游戏开发教程11(扫雷游戏实现)

    【标题】 SDL游戏开发教程11(扫雷游戏实现) 在本教程中,我们将探讨如何使用Simple DirectMedia Layer (SDL)库来实现经典的扫雷游戏。SDL是一个跨平台的开发库,专门用于处理图形、音频和输入设备,是游戏开发中...

    SDL游戏开发教程04(C++封装SDL)

    总结来说,"SDL游戏开发教程04(C++封装SDL)"着重讲解了如何在C++中以面向对象的方式整合和管理SDL库,以实现更高效、模块化的游戏开发。通过封装,不仅可以提升代码质量,还能降低开发难度,便于团队协作和项目的...

    SDL游戏开发教程09(代码迁移到VC8)

    【标题】 SDL游戏开发教程09(代码迁移到VC8) 在游戏开发领域,Simple DirectMedia Layer (SDL) 是一个广泛使用的开源库,它提供了一种跨平台的方式来处理图形、音频、输入设备等。本教程将指导你如何将已有的SDL...

    SDL游戏开发教程06(利用BOOST库实现简单的文件日志功能)

    在本教程中,我们将深入探讨如何使用Boost库在SDL游戏开发中实现文件日志功能。Boost库是一个流行的C++库集合,它提供了许多实用的工具和功能,包括日期时间处理、文件系统操作以及系统接口等。在游戏开发中,日志...

    SDL游戏开发经典教程01源代码

    【标题】 SDL游戏开发经典教程01源代码 在游戏开发领域,Simple DirectMedia Layer (SDL) 是一个广泛使用的开源跨平台开发库,它为创建多媒体应用和游戏提供了丰富的功能。"SDL游戏开发经典教程01源代码"是针对初学...

    SDL游戏开发教程10(场景管理器)

    在本篇“SDL游戏开发教程10(场景管理器)”中,我们将深入探讨如何在游戏开发中有效地管理和切换不同的游戏场景。SDL(Simple DirectMedia Layer)是一个跨平台的库,用于处理图形、音频和输入设备,是创建2D游戏的...

    SDL添加了文字显示

    标题提到的“SDL添加了文字显示”是指利用SDL的一个扩展库——SDL_ttf,实现了在SDL应用程序中动态显示Unicode文字的功能。SDL_ttf库是SDL的扩展,专门用于处理TrueType字体,使得开发者能够在程序中方便地渲染文本...

    SDL游戏开发教程07(图像的旋转和缩放)

    在本教程中,我们将深入探讨SDL(Simple DirectMedia Layer)游戏开发中的图像处理技术,特别是图像的旋转和缩放。SDL是一个跨平台的多媒体库,主要用于开发图形、音频和输入等游戏相关的功能。在这个教程中,我们将...

    SDL开发的小游戏

    **SDL(Simple DirectMedia Layer)** 是一个跨平台的开源库,主要用于开发图形、音频和输入设备相关的应用程序,尤其适合创建游戏。SDL库提供了一套底层API,让开发者能够便捷地处理多媒体元素,并且支持多平台,...

    SDL.rar_SDL知名游戏_sdl教程_sdl游戏

    通过学习"SDL.rar_SDL知名游戏_sdl教程_sdl游戏"提供的资料,尤其是"SDL教程.docx"文档,你将逐步掌握SDL的基本用法,为开发自己的游戏奠定坚实的基础。随着对SDL的熟练运用,你甚至可以参与制作出具有专业水准的...

    SDL1.2中文教程

    本教程主要针对的是Simple DirectMedia Layer(SDL)1.2版本,这是一个跨平台的多媒体库,广泛应用于游戏开发、图形界面设计和其他需要处理图形、音频和输入设备的软件开发中。SDL提供了一个相对低级别的接口,让...

    sdl游戏开发中文文档

    ### SDL游戏开发中文文档知识点概览 #### 一、SDL简介 - **名称与定义**:SDL即Simple DirectMedia Layer(简易直控媒体层),是一个跨平台的多媒体库,主要用于直接控制多媒体硬件设备,如音频、键盘、鼠标、游戏...

    SDL2.0教程

    它的最新版本是SDL 2.0,提供了许多增强的功能和改进,使得游戏开发者和其他多媒体应用开发者能够更方便地在不同的操作系统上构建和移植应用程序,包括Windows、Linux、Mac OS X以及移动平台如Android和iOS。...

    SDL入门教程中文(最好的SDL入门教程,自己手工整理)

    ### SDL入门教程中文知识点概述 ...本教程通过详细介绍SDL的基本概念、特点及其应用领域,为读者提供了一个全面的入门指南,旨在帮助他们快速建立起游戏开发的基础知识,并激发其进一步探索的兴趣。

Global site tag (gtag.js) - Google Analytics