window.open:深入WINDOW字型



文档介绍:
   在探讨更深入剧情处理以前我们必须拥有输出文字能力这次我们目标是撷取WINDOW系统资源并且和DirectX全萤幕游戏相结合你将会学习到如何有效快速地将字型套用到你游戏上面

目录:
   掌握正确方向
   WINDOW字型
   取得字型资讯
   将字型设定给DC
   规画秀字方式
   整合
   末语

文档内容:

□ 掌握正确方向

  记得我曾经说过在DOS下开发游戏和WINDOW下差别是很大曾经令人苦恼问题在WINDOW下都有更方便解决方式最明显例子就是音乐播放以及我们这次讨论字型使用在很多较老旧游戏书籍里面都会教你如何秀中文比较常见思路方法就是利用倚天中文字型档并且还需要利用文字索引窍门技巧以节省记忆体空间切都过去了等等你会看到我们在WINDOW下面实作方式这边所谓「正确方向」是指有效率解决方式当然早期作法仍然可以适用于WINDOW环境只是要额外付出许多代价就是了

□ WINDOW字型

  在中文WINDOW里面安装完毕就会有许多字型可以套用其中中文字型至少有明体和标楷体这些字型属于系统资源部份任何应用程式皆可大方地使用该如何使用呢?写过WIN32 APP人大抵上都知道WINDOW API中个字型对话方块可以很方便某个字型资料不过我不打算使用这个对话方块函示实际上自己动手更有弹性也不会浪费多少时间以下开始实作

□ 取得字型资讯

  个应用程式如果没有指定字型则使用系统内定字型如果要使用标楷体字型必须先取得此种字型资讯当然还可以指定字型大小等等打开字型对话方块你会发觉字型种类很多但是我们需要只有中文字型而已所以我们忽略其他资讯专注于我们需要部份这里我要强调如果只强调秀出中文你大可不必管他字型如何来(毕竟我们WINDOW环境本身就是中文)使用内定字型即可如果你还要强调系统字型美观就需要选择种比较容易搭配字型更进步如果要让使用者在游戏内自由选择系统任种字型则需要列举出所有可用字型并且将这些资讯收集起来提供程式使用

我们先介绍如何列举出所有系统字型首先介绍这个函示:

EnumFontFamilies(
HDC hdc, // handle to device control
LPCTSTR lpszFamily, // poer to family-name
FONTENUMPROC lpEnumFontFamProc, // poer to callback function
LPARAM lParam // address of application-supplied data
); //取自VC线上介绍说明

  这个函示需要参数共有 4个个参数是绘图使用设备代码般应用程式我们会使用GetDC来取得他设备代码而在DirectX里面我们必须使用IDirectDrawSurface3::GetDC由这个函示取得DC才能保证和GDI函示相容第 2个参数设定为NULL则会取得所有字型包括固定宽度字型和向量字型第 3个参数是个CALLBACK函示指标原型固定系统会自动呼叫这个函示而实作此函示我们正好可以将系统字型撷取下来第 4个参数用不到设为NULL即可

至于FONTENUMPROC原型是这样子:

CALLBACK EnumFontFamProc(
ENUMLOGFONT FAR* lpelf, // poer to logical-font data
NEWTEXTMETRIC FAR* lpntm,

// poer to physical-font data
FontType, // type of font
LPARAM lParam // address of application-d data
); // 取字VC 线上介绍说明

这个函示 4个参数由系统传给我们里面包含我们所需要切资讯现在我们就看看实际上该如何使用底下撷取自CFONT类别实作内容:

//此成员函示呼叫以后会开始取得系统字型

void CFont::QueryFont
{
lpFrontBuffer->GetDC(&hdc); //取得前景DC
EnumFontFamilies(hdc, (LPCTSTR)NULL,(FONTENUMPROC) EnumFamCallBack,(LPARAM)NULL);
lpFrontBuffer->ReleaseDC(hdc); //释放DC
}

CFont::QueryFont仅设定好资料真正接收字型资料部份在CALLBACK函示而CALLBACK函示我们应该如何实作呢?底下便是:

首先我们配置 3个结构以存放「细明体」「新细明体」和「标楷体」字型资料 LOGFONT logfont[3];

这个结构可以存放字型细部资料其内容相当繁杂且不是所有资料都派上用场更详细资料可以在VC线上介绍说明取得需要你可以配置更多空间以存放各式各样字型资料这边我只示范 3种常用字型接著我们看下该如何在CALLBACK函示里面接收这些资料:

BOOL CALLBACK CFont::EnumFamCallBack(LPLOGFONT lplf,LPNEWTEXTMETRIC lpntm ,DWORD FontType,LPARAM aFontCount)
{
(strcmp(lplf->lfFaceName,\"细明体\")0) //仅找明体和标楷体
memcpy(&logfont[0],lplf,(LOGFONT)); //资料存放到logfont阵列
(strcmp(lplf->lfFaceName,\"新细明体\")0)
memcpy(&logfont[1],lplf,(LOGFONT));
(strcmp(lplf->lfFaceName,\"标楷体\")0)
memcpy(&logfont[2],lplf,(LOGFONT));

TRUE;
}

刚刚有说到这个CALLBACK函示 4个参数是系统传给我们其中LPLOGFONT包含了种字型资料我们藉由判断其名称来决定这个字型是不是我们需要如果是将他拷贝到我们预先配置好LOGFONT结构里面这个函示事实上会持续呼叫直到找完所有字型为止所以在这个过程中我们只接收 3种字型资料其他都忽略不处理当这个函示完成以后我们配置LOGFONT[3]这个阵列里面已经包含我们所需要资料了大事已经完成半了接著我们应该做什么事情呢?

□ 将字型设定给DC

取得字型资料以后实际上什么事情也没发生我们必须根据字型资料来产生个字型代码给API函示使用这个处理过程我将他包在成员函示CFont::SetFont(HDC hdc, FontType, width, height)里面实作内容如下:

void CFont::SetFont(HDC hdc, FontType, width, height)
{
switch(FontType)
{
MINGLIU:
logfont[0].lfHeight=height;
logfont[0].lfWidth=width;
hFont = CreateFontIndirect (&logfont[0]);
;

NEWMINGLIU:
logfont[1].lfHeight=height;
logfont[1].lfWidth=width;
hFont = CreateFontIndirect (&logfont[1]);
;

KAIU:
logfont[2].lfHeight=height;
logfont[2].lfWidth=width;
hFont = CreateFontIndirect (&logfont[2]);
;
}

SelectObject(hdc,hFont);
}

这个成员函示接收 4个参数个参数是欲设定字型DC第 2个参数决定要设定何种字型第 3第 4个参数决定字型大小多方便阿连字型大小都可以自由设定不过还是要适中才会好看所以实际呼叫时候我们是这样做:

SetFont(hdc,NEWMINGLIU,10,15); //将前景DC和字型相结合



为了美观起见我把 3种字型另外定义其名称:

# KAIU 5 //标楷体
# MINGLIU 6 //细明体
# NEWMINGLIU 7 //新细明体

所以上面SetFont我们是选择了新细明体并且决定其字型宽度10高度15当然宽度高度是可以任意变化决定好宽度高度以后接著使用CreateFontIndirect ;其传回值为字型代码最后利用SelectObject(hdc,hFont);把他真正设定给DC就可以了

感觉上这个过程绕来绕去都没有枪毙命感觉唔~~~我也这样认为所以我还是把到目前为止过程整理下吧:

1. 使用EnumFontFamilies列举字型
2. 在CALLBACK函示里面接收字型资讯
3. 使用字型时候将字型和DC结合
4. 目前为止大事完成2/3

□ 规画秀字方式

我用最简单方式来秀字看看要用什么函示呢?TextOut是也简单又大方亲切又可爱而且在任何地方秀字总免不了使用这个函示我们来看看他样子:

BOOL TextOut(
HDC hdc, // handle of device context
nXStart, // x-coordinate of starting position
nYStart, // y-coordinate of starting position
LPCTSTR lpString, // address of
cbString // number of characters in
); // 取自VC4.0线上介绍说明

可以指定座标和字串果然是为我们精心设计API不用如何对得起别人呢?示范下我要在座标(20,25)地方秀出段文字我这么做:

TextOut(hdc,20,25,\"相当稳用\",8);

果然没问题不过呢我们需要再包装让这个函示更人性化所以我又实作了个成员函示void CFont::ShowFont(char* )这个成员函示接收个指向任意长度字串指标并且按照我们预先设计好格式秀出来这个格式需要讨论我自己决定方式是这样子:  

1. 在萤幕座标 (46,120)地方开始秀字
2. 列以十 4个中文字为最长长度超过换列
3. 萤幕最多同时容纳 4列超过话清除字串从第列输出
4. .........(依照喜好自己定格式)

旦决定好以后根据这些规则我们实作出来内容是这样子:

void CFont::ShowFont(char* )
{
Line,Cycle;
i,j,k,m=0;
count=0,ShowedFont=0;//累计秀出

while([count]!=0)



count;//先取得这个字串长度

(count%2) //不足2 s则补足
count;

count/=2; //COUNT除以 2变成中文字个数

Line=count/14; // 每列14个中文字所以我们计算共需要几列

Cycle=Line/4; //每个画面最多4列所以我们计算需要几个画面

Cycle; //至少个画面

//可见页拷贝到隐藏页文字秀出以后恢复萤幕用

lpBackBuffer->Blt(NULL,lpFrontBuffer,NULL,DDBLT_WAIT,NULL);

SetFont(hdc,NEWMINGLIU,10,15);//将前景DC和字型相结合

for(k=0;k<Cycle;k)// 4行字为个回圈
{
lpFrontBuffer->GetDC(&hdc);//取得前景DC
SetBkMode(hdc,TRANSPARENT);//设定秀字背景为透明色
for(i=0;i<4;i)//行字为个回圈共 4行
{

for(j=0;j<14;j)//行字里面有14个中文字
{
//第次秀黑色
SetTextColor(hdc,RGB(0,0,0));
TextOut(hdc,46+j*17,120+i*15,&[m*28+j*2],2);

//第 2次左移个像点秀出另制造框线效果
SetTextColor(hdc,RGB(255,0,0));
TextOut(hdc,45+j*17,120+i*15,&[m*28+j*2],2);
Sleep(20);//稍微延迟控制速度
ShowedFont;

(ShowedFontcount)goto Finish;//秀完了吗?离开回圈
}//for j end

m;//指向下列给TextOut使用
}// for i end

Finish:

lpFrontBuffer->ReleaseDC(hdc);

ReleaseFont;

Sleep(200); //延迟

while(GetAsyncKeyState(VK_SPACE)>=0);//按空白键继续

lpFrontBuffer->Blt(NULL,lpBackBuffer,NULL,DDBLT_WAIT,NULL);

}//for k end

//还原萤幕画面
lpFrontBuffer->Blt(NULL,lpBackBuffer,NULL,DDBLT_WAIT,NULL);
Sleep(300);
;

} // function end

虽然加上注解了我还是稍微介绍说明首先函示会收到个不定长度字串个步骤我们先计算他长度此处长度单位是除以 2以后就变成中文字个数了接著利用国小数学我们计算出总共要几列几个画面才得以把这个字串秀完所以回圈里面就是在做这件事情最后跳出回圈方式我们判断已经秀出字是否跟传进来长度如果代表我们已经秀字完毕了goto跳出来就好了轻松

另外保存萤幕画面是有必要不过这边我作法相当直接在秀字时候我们是直接秀到前景绘图页(surface)所以TextOut呼叫完毕萤幕上马上会出现这些字串所以你知道了在我们秀字当时背景绘图页是闲置于是我们在秀字串以前把萤幕画面Blt拷贝到背景绘图页等到秀字完毕以后要恢复画面则反向操作从背景绘图页拷贝到前景绘图页即可初步结论是切较静态画面你直接画在前景surface即可不必担心会有闪烁现象



在秀字过程中个字我们都秀两次这是为什么呢?达成文字边框效果是也套字型我们利用区别颜色画上去效果不错其原理是这样子我随便举例介绍说明:

次我在座标(46,120)地方用黑色秀出个「中」字第 2次我在座标(45,120)地方用红色秀出个「中」字你可以看到这两个字偏移只有个x座标单位所以红色「中」字会覆盖掉黑色「中」字但是最右边黑色部份则不会覆盖到(我们偏移个x单位嘛)这简单过程我们好像获得了套新更漂亮字型这窍门技巧够酷

各位也可以看到函示里面Sleep部份主要是控制秀出字串速度你可以在字和字的间控制间隔速度如果没有稍微延迟整面字瞬间秀完当然最好思路方法是把Sleep函示参数(延迟时间)当成变数让使用者决定他想要速度

最后我要说在你使用IDirectDrawSurface3::GetDC获得DC以后记得要释放他不这么做其他绘图动作都会失败这跟我们平常使用GDIGetDC是有差别

□ 整合

到目前为止初步完成99%任务我们学到了如何利用系统字型资源并应用在我们程式上面整个字型类别是这样子:

CFont
{
public:
void QueryFont; //找中文字型存入 LOGFONT
void SetFont(HDC,,,);//设定字型以及大小
void ReleaseFont;//释放字型资源
void ShowFont(char *);//秀出格式化字串

private:
BOOL CALLBACK EnumFamCallBack(LPLOGFONT,
LPNEWTEXTMETRIC,DWORD,LPARAM);
HFONT hFont;//字型代码
};

是解释其过程并非要写个现成东西给你用所以还有很多事情需要靠你自己去完成比方说般人系统里面中文字型并不仅止于明体和标准楷体或许还有华康等其他字型我们或许应该列举出所有中文字型让程式更有弹性在字型大小方面是可以调整所以你也不必要担心在320x200画面字型是否会过大在800x600画面下字型是否太小操控权都在你手上在秀字同时加上个美丽背景图框也是不错选择

□ 末语

看到这里你大概已经知道WINDOW下处理方式跟DOS下有相当相当差别吧如果你掌握到正确处理方式并节省下额外时间那么看这篇文章就值回票价了

以处理字型为目标我们已经达成阶段性任务以处理剧情为目标我们只完成了开始5%剧情表现上大致上可以看成「说故事」不断把文字输入到脑海里面就形成了剧情当然也需要各种事件参和更有趣架构单线剧情和多线剧情都是相当富挑战性件工作我们是不是该开始规画了呢?规画前记得先洗把脸(可以不使用洗面皂)让思绪更加畅通





Tags:  findwindow window7 window window.open

延伸阅读

最新评论

发表评论