作者:龙飞
最近几篇教程基本上都是参考着Lazy Foo的教程顺序来的。因为我也觉得他的顺序很实用。所不同的是,新的类型我都添加在了之前建立起来的surface类的基础之上。所以,如果你觉得单独看这些教程完全搞不明白,最好从头按照顺序来学习。另外,为了复习C++知识,也为了遵循C++的理念,我有意的将程序风格向C++靠拢。如果你更喜欢C风格,相信你在其他地方可以找到更适合你的教程。
1.1:一个小细节,SDL窗口的名称
因为涉及到文本的显示了,我们提一个一直以来忽略的问题——SDL建立起来的窗口的名字。因为我们所建立起来的Screen Surface是唯一和特殊的。所以窗口名字这个行为是可以绑定在这个唯一的Screen Surface对象上的。SDL中的相关函数是:
void SDL_WM_SetCaption(const char *title, const char *icon);
一般icon还暂时用不上,我们设置为空指针。我们修改一下Screen Surface的数据成员与构造函数。在数据成员里面添加一个windowName,并且修改构造函数
class ScreenSurface
{
private:
//![](http://www.cppblog.com/Images/dot.gif)
char* windowName;
public:
//![](http://www.cppblog.com/Images/dot.gif)
ScreenSurface(int w, int h, char* window_name = 0, int b = 0, Uint32 f = 0);
};
ScreenSurface::ScreenSurface():
width(640), height(480), bpp(32), flags(0), windowName(0)
{
if ( screenNum > 0 )
throw ErrorInfo("DONOT create more than ONE screen!");
if ( SDL_Init(SDL_INIT_VIDEO < 0 ) )
throw ErrorInfo(SDL_GetError());
pScreen = SDL_SetVideoMode(width, height, bpp, flags);
screenNum++;
}
ScreenSurface::ScreenSurface(int w, int h, char* window_name, int b, Uint32 f):
width(w), height(h), bpp(b), flags(f)
{
if ( screenNum > 0 )
throw ErrorInfo("DONOT create more than ONE screen!");
if ( SDL_Init(SDL_INIT_VIDEO < 0 ) )
throw ErrorInfo(SDL_GetError());
pScreen = SDL_SetVideoMode(width, height, bpp, flags);
screenNum++;
if ( window_name != 0 ) {
windowName = window_name;
SDL_WM_SetCaption(windowName, 0);
}
else
windowName = 0;
}
这样,我们在创建SceenSurface的时候,第三个参数如果指定,则可以用字符串表示窗口名称。
1.2:使用*.ttf文件
SDL使用*.ttf文件,仍然需要扩展库的支持。相关的下载和SDL_image的类似,大家可以参考前面的教程。下载地址如下:
http://www.libsdl.org/projects/SDL_ttf/
使用ttf扩展库的程序如下:
(1)装载扩展库:TTF_Init();
(2)打开字库:TTF_OpenFont(const char* ttf_fileName, int ttf_size);
(3)构建显示文本的surface:TTF_RenderText_Solid(TTF_Font* pFont, const char* message, SDL_Color textColor);
(4)显示(blit)文本surface;
(5)关闭字库:TTF_CloseFont();
(6)退出扩展库:TTF_Quit();
(7)释放显示文本的surface:SDL_FreeSurface();
我们考虑下这个TextSurface与之前的DisplaySurface之间的关系,希望通过类将二者有所联系。
1.3:构建TextSurface类
我们分析下TextSurface与DisplaySurface的关系:他们都依赖于一个ScreenSurface对象,至少具有两个一样的私有数据成员pSurface和pScreen;他们有一致的行为blit();他们的构造前提条件不同,析构做的“善后”也不一样。
我在水木社区的CPP版请教有这样关系的两个类应该是什么关系。有前辈指教说,一个类,用不同的flag加以区分。而我并不愿意多增加一个构造函数的参数,所以,我用构造函数的重载实现构造的不同;用继承类实现方法代码的重用;用继承类的析构函数为TextSurface类做额外的析构工作。
考虑到应在第一次建立TextSurface对象的时候装载ttf扩展库,并在最后一个对象使用完毕后关闭ttf扩展库,所以,在基类DisplaySurface中添加静态私有成员作为计数器,并添加相应的方法为派生类使用。这些方法,以及专门为派生类创建的基类构造函数,我们并不希望能被外部使用,所以,使用了关键字proteced。
class DisplaySurface
{
private:
//![](http://www.cppblog.com/Images/dot.gif)
//for TextSurafce
static int textNum;
TTF_Font* pFont;
public:
//![](http://www.cppblog.com/Images/dot.gif)
protected:
//for TextSurface
DisplaySurface(const std::string& msg_name, const std::string& message, const ScreenSurface& screen,
Uint8 r, Uint8 g, Uint8 b,
const std::string& ttf_fileName, int ttf_size);
int tellTextNum() const;
void reduceTextNum();
void deleteFontPoint();
};
pFont是TextSurface会用到的私有数据,构造基类的时候,直接设置成空指针就可以了。
保护成员的实现如下:
//for TextSurface
DisplaySurface::DisplaySurface(const std::string& msg_name, const std::string& message, const ScreenSurface& screen,
Uint8 r, Uint8 g , Uint8 b,
const std::string& ttf_fileName, int ttf_size):
fileName(msg_name)
{
if ( textNum == 0 )
if ( TTF_Init() < 0 )
throw ErrorInfo("TTF_Init() failed!");
SDL_Color textColor;
textColor.r = r;
textColor.g = g;
textColor.b = b;
pFont = TTF_OpenFont(ttf_fileName.c_str(), ttf_size);
if ( pFont == 0 )
throw ErrorInfo("TTF_OpenFont() failed!");
pSurface = TTF_RenderText_Solid(pFont, message.c_str(), textColor);
if ( pSurface == 0 )
throw ErrorInfo("TTF_RenderText_solid() failed!");
pScreen = screen.point();
textNum++;
}
int DisplaySurface::tellTextNum() const
{
return textNum;
}
void DisplaySurface::reduceTextNum()
{
textNum--;
}
void DisplaySurface::deleteFontPoint()
{
TTF_CloseFont(pFont);
}
有了这些数据成员和方法,我们可以构建TextSurface类了。
class TextSurface: public DisplaySurface
{
public:
TextSurface(const std::string& msg_name, const std::string& message, const ScreenSurface& screen,
Uint8 r = 0xFF, Uint8 g = 0xFF, Uint8 b = 0xFF,
const std::string& ttf_fileName = "lazy.ttf", int ttf_size = 28);
~TextSurface();
};
可以看到,我们仅仅增添了派生类的构造函数和析构函数,实现如下:
//class TextSurface
TextSurface::TextSurface(const std::string& msg_name, const std::string& message, const ScreenSurface& screen,
Uint8 r, Uint8 g, Uint8 b,
const std::string& ttf_fileName, int ttf_size):
DisplaySurface(msg_name, message, screen, r, g, b, ttf_fileName, ttf_size)
{}
TextSurface::~TextSurface()
{
deleteFontPoint();
reduceTextNum();
if ( tellTextNum() == 0 )
TTF_Quit();
}
我们在下节给出完整的代码以及一个用于演示的例子。
posted on 2008-03-24 20:22
lf426 阅读(3946)
评论(0) 编辑 收藏 引用 所属分类:
SDL入门教程