游戏中汉字显示的实现与技巧1.docx

上传人:b****5 文档编号:29378288 上传时间:2023-07-22 格式:DOCX 页数:19 大小:28.30KB
下载 相关 举报
游戏中汉字显示的实现与技巧1.docx_第1页
第1页 / 共19页
游戏中汉字显示的实现与技巧1.docx_第2页
第2页 / 共19页
游戏中汉字显示的实现与技巧1.docx_第3页
第3页 / 共19页
游戏中汉字显示的实现与技巧1.docx_第4页
第4页 / 共19页
游戏中汉字显示的实现与技巧1.docx_第5页
第5页 / 共19页
点击查看更多>>
下载资源
资源描述

游戏中汉字显示的实现与技巧1.docx

《游戏中汉字显示的实现与技巧1.docx》由会员分享,可在线阅读,更多相关《游戏中汉字显示的实现与技巧1.docx(19页珍藏版)》请在冰豆网上搜索。

游戏中汉字显示的实现与技巧1.docx

游戏中汉字显示的实现与技巧1

游戏中汉字显示的实现与技巧

炎龙工作室xx肝

版本:

v1.0

最后更新日期:

2002-3-30

绪言

在游戏中,因为我们是中国人麻,通常都需要显示汉字,比方说交待剧情。

而对于文字的显示,英文的显示要较其简单得多,因为只有26个字母,就算再加一些标点、符号什么的,用一张位图,就可以足以显示所有的单词了,而相关实现技巧,也比较轻松。

而中文的显示方法,要复杂得许多。

记得原来在DOS下,汉字的显示都是读的UCDOS的点阵字库,而点阵字库的读取方法,在UCDOSSDK中都有源代码可以参考。

但是自从Windows操作系统开始,我们开始了解到一种更好的字库,它就是TTF。

注:

以下我所指的开发环境,除非明确说明,默认的平台是VC6.0+DirectX8.1,使用D3D来加速2D。

然后使用的STL是用的SGI实现的那一套STL。

点阵字库

包括现在,有很多游戏都还是使用的点阵字库。

因为操作起来比较方便,加上这方面的经验已经积累了好几年了。

通常如果只是一种字体就可以满足需要的话,它会是一个比较好、快的解决办法。

但是它有3个缺点:

1.如果放大显示,不做处理的话,显示出来的汉字,是很难看的。

2.像是UCDOS所提供的点阵字库,只有24点阵的有几种字体,如:

宋体、黑体、揩体„,而16点阵的好象就只有宋体一种。

3.点阵字库,通常是有版权的,尤其是第三方制作的汉字库(如:

xx)。

在这样的情况下,当我们写好这样的一个显示函数,就算是解决了如:

放大、快速显示等问题的话,可供选择的字体还是太过于局限了。

所以,在字体的要求比较强的情况下,点阵字库并不是一个好的解决方法,他不够灵活。

尽管我们对于它的操作是如此得熟练,可以写出优美的代码来展示我们的编程技巧。

TTF

TTF是TrueTypeFont的简称。

在Windows\Fonts目录下面,我们可以看到许多后缀为ttf的文件,它就是接下来我们接下来所要谈到的。

这样,我们就可以解决点阵汉字的一些问题。

通过TTF,我们在字体的质量和字库的数量上获得了暂时性的胜利。

字库的读取和显示

先前谈到点阵字库,只需要很简单的一些操作,就可以显示出想要的汉字。

下面我给出一个读取hzk16的函数,它需要一个Surface以供显示用:

#include

#include

#include

//读取16x16

voidDispHZ16(intx,inty,BYTE*Str,LPDIRECTDRAWSURFACEsurf){constintMask[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};

FILE*HzkFp;

WORDi,j,k=0,m;

WORDHzNum;

WORDQuHao;

WORDWeiHao;

longoffset;

BYTEdotBuffer[32];

HzkFp=fopen("HZK16","rb");

HzNum=strlen((constchar*)Str)/2;

DDSURFACEDESCddsd;

LPWORDlpSurface;

HRESULTddrval;

ddsd.dwSize=sizeof(ddsd);

while((ddrval=surf->Lock(NULL,&ddsd,0,NULL))==DDERR_WASSTILLDRAWING);

if(ddrval==DD_OK)

lpSurface=(LPWORD)ddsd.lpSurface;

for(i=0;i

WeiHao=Str[i*2+1]-160;

offset=((QuHao-1)*94+(WeiHao-1))*32;

fseek(HzkFp,offset,SEEK_SET);

fread(dotBuffer,32,1,HzkFp);

for(j=0;j<16;j++)

for(k=0;k<2;k++)

for(m=0;m<8;m++)

if(dotBuffer[j*2+k]&Mask[m]){lpSurface[ddsd.lPitch*(y+j+1)+x+k*8+m]=0x000;}

x+=16;}surf->Unlock(NULL);

fclose(HzkFp);}其实原理很简单:

1.打开字库

2.计算字符串长度(这个函数只支持中文),并且LockSurface

3.依次计算出每个汉字所对应的区码和位码(汉字的第1个字节是区码,第2个字节就是位码),然后通过公式计算出这个汉字在字库中的偏移量:

offset=((QuHao-1)*94+(WeiHao-1))*32;

4.读出一个32个字节的点阵

5.绘制到Surfacexx

以上只是16*16点阵字库的显示方法,24*24的读取方法与之类似,大家可以参照相关资料来书写出自己的代码。

如何显示TTF字库呢,有很多种手段,下面我按从简单到复杂的的顺序依次介绍:

1.使用WindowsAPI,也就是大家所熟悉的TextOut。

通过它,还需要一个HDC(设备句柄),我们就可以随意地在屏幕任何地方显示出文字了。

2.在http:

oTrueTypefonts(andcollections)

oType1fonts

oCID-keyedType1fonts

oCFFfonts

oOpenTypefonts(bothTrueTypeandCFFvariants)

oSFNT-basedbitmapfonts

oX11PCFfonts

oWindowsFNTfonts

3.自己研究TTF的格式,然后自己来操作。

晕.......╮╮

\█/倒!

●虽然我们想要把每一件事情都做好,但是也不是每一件事情都要亲历亲为。

如果你非要这样,也行^__^,但是过不了多久,你就会陷入泥沼,到时候你会发现自己的热情正在慢慢被磨灭,什么叫做抓狂,相信你很快就会知道^_^。

在有多种选择可以取舍的情况下,我们需要考虑一下,对比一下各种解决方法的优劣。

在DirectDraw时代,我们都不自觉地喜欢上了GetDC,因为„„多方便啊。

可是现在已经到了DirectX8.1时代了(我要使劲地摇那些还沉醉于DirectX7中,为如何在使用alpha时提升那可怜的

1、2个fps的朋友们:

醒醒,该起床了!

),HDC已经被M$列为禁用品。

怎么办呢?

是的,你可能已经想到了,我们还一直保存着窗口的hWnd呢,可以通过它来得到hdc,从而调用那些需要hdc的API,可是,这样做是更为愚蠢的,这样对你是没有一点好处的,不信,你就试试吧。

有一句话,请牢记:

要想你的游戏有更快的速度的话,请不要再去碰HDC了。

我们非常清楚hdc是一个超慢的解决办法,它无法在我们的高速游戏中满60分及格。

下面来看看FreeType,它更像是一个Service。

它的解决方法是,先通过一系列的初始化和设置,告诉FreeType字体的名字和大小等,然后它会动态地申请一个Graphic,再把我们要显示的字画到这个Graphic上,你还可以把它保存为tga格式。

不过我们最终所想要的不是这个,所以可能我们还需要从这个Graphic上逐点读取或者用CopyRect,然后再画到我们的画面上。

其实它已经是很方便的了,可是需要你去学习如何配置和使用它,这是很花时间的一件事情,而且它最大的优点是可以跨平台,我们需要它吗?

如果有一个更为简单的办法,像是如果Textout不是那么慢的话,就好了„„

在这里,顺便谈一下另2个字体显示类:

ID3DXFont和CD3DFONT。

可能早就有人会说怎么在上面的列表中没有它们?

原因我会在下面慢慢地说明:

ID3DXFont,它存在于D3DX库中,一个现成的字体类,不过对于它的处理方法„„我实在不敢恭维,就引用一位大师所说的话来表达我的看法吧:

在内部实现中,ID3DXFont:

:

DrawText()函数确实做了我上面讨论的工作,先建立一张GDI兼容的位图,把文本绘制到位图上,而后把位图拷贝到纹理贴图上去,最后把纹理渲染到屏幕上。

这样你就聚齐了所有的龟速的原始GDI函数,还包括了一大堆的额外开销—最终,这个函数比原来GDI的DrawTextEx()函数要慢上超过六倍„„

CD3DFONT,是由M$在D3D的框架代码中提供。

不过它只能显示英文,有很多朋友通过自己定制和修改这个类,来实现自己的中文显示。

不过效果都不是很好。

其实原理,跟ID3DXFont的方法差不多,不过处理方法要聪明了一点。

分析与思考

那么我们应该怎么办呢?

通常我们会幻想,如果可以像处理英文那样,把所有的汉字都保存在一张位图里,该有多好。

这样,显示的速度就不是问题了,直接可以CopyRect上去。

可是,这样可能吗?

首先,必须每一种字体都要生成这样的一个巨型位图。

而且据说在GB2312中,一共有6000多个汉字,就算是用16*16,ohmygod,这个位图该有多大啊(据说会有2.5M^__^)!

而且在DirectX8.1中,对于Texture(显示的最小单位,就好象是原来DirectSurface的概念一样。

说过多少遍了,不要再用DirectX8以前的东西了。

不要试着去回忆那些美好的过去,我很明白,要你一下子放弃原来多年所获得的成就,是一件很痛苦的事情,但是包袱太重,是会影响进步的。

就像是我们的国家„„扯远了),不同的显卡,支持的最大容量也是不同的。

比方说早期的Voodoo,只支持256*256大小的Texture。

而在我的显卡(Geforce2MX200)上测试,支持最大2048*2048大小的Texture。

对于这样的硬件不确定性,我们只能取其最小值,也就是256*256。

汉字虽然很多,但是常用的汉字,其实也就只有那么几百个。

像这样的字:

鬯、鞴,你一辈子会看到多少次呢?

如果可以做一个类似于Cache的东西,保存着常用的那些个汉字,在需要显示的的时候,先在Cache中查找,如果有的话,就马上画上去;如果没有,就从字库中提取到Cache中。

这样的话,在使用Texture来保存汉字的位图信息的同时,对于每个汉字,我们还要定义一个结构,然后用一个东西把它串起来,综合它们2个,也就实现了我们所要的Cache了。

刚开始,我所定义的结构是这样的:

structChar{

charhz[3];//保存汉字

intfrequency;//使用频率

RECTrect;//这个字对应位图的区域

BoolisUsing;//是否使用}对于汉字和英文,我在这里大概地讲一下原理:

汉字是由2个字节保存,而英文只需要1个。

而判断一个字是否是汉字,只需判断第1个byte是否>128(在原来的GB2312中,汉字的2个字节都是>128的。

而新的GBK字库,汉字的第2个字节不一定>128,我想这是扩大了字库容量的原因。

我的意思是说,如果给一个字符串你,随机给其中一个位置,然后我问你这个位置是什么?

你的回答只能是:

1英文2汉字的首字节3汉字的尾字节。

而这个问题的解法,为了稳妥起见,你必须从字符串的开始判断起)。

也就是说在char[3]中,如果保存的是汉字,则char[0]保存汉字第1个字节,char[1]保存汉字第2个字节,第3个存放’\0’;如果是英文的话,则只用到char[0],其它的全部为’\0’。

接下来,对于使用char[3]来保存汉字,是否真的很合适呢?

因为如果把它当作一个字符串来看的话,在查找时就需要使用strcmp来比较字符串了,这样一定是会影响速度的。

如果不把它看作字符串(字符串的最后一个字节需要以’\0’结尾),只用char[2]的话,我们可以只是简单地调用宏MAKEWORD,把2个byte压成1个WORD。

当把文字作为一个WORD来看的时候,这样查找比较时可以用WORD内建的==操作,这样要比调用strcmp函数要快得多。

intfrequency用来标志每个WORD的使用频率。

设想,如果一个字已经存在于Cache中,以后每对它调用一次,就让frequency++。

这样做还有一个用意是,是否可以在一个合适的时候,以frequency为参照来对这整个Cache排个序,把常用的字放在前面。

那么在显示时,可以先在Cache中查找所要显示的字是否已经存在于Cache中,如果有则直接显示,没有的话才需要采取某种手段将字加入到Cache中。

一些常用的字(像:

我、的、着、了、过„„),使得显示的速度将会大大提高。

其实上面说了半天的Cache,它具体是什么呢?

其实就是指的最小绘制单位,在DirectX7里是Surface,而在DirectX8中就是Texture。

使用它来存放显示过的汉字,这样,就不用每次都从字库中读取或是调用如TextOut这类GDI超慢的函数了。

因为每次在绘制一个文字之前,都会先在这个Cache中找,有的话就直接画上去,没有才会调用TextOut操作。

而这样做的原因,我们先设想一下:

游戏一般会控制为30fps或是60fps的速度不停地刷新,如果在GameLoop中有任何的代码是龟速级的话,这样就会导致fps的最大数的降低,也就意味着在保证30fps或60fps的同时,能绘制到屏幕上的物体的数量减少了。

这就是我们为什么要使用Texture来作为Cache的实现的原因。

再一个,文字在屏幕上显示时一般会保持一段时间,这个时间可能是1秒-3秒,我们的游戏也就会相应地更新60fps或180fps,这是因为人们需要阅读它们。

或者是一些如标题这样的文字,它们总是不会更新的,或是更新得很慢。

我们完全可以在第一时间,比方说我们的画面有60fps,在第1个fps时,我们得知要显示文字”唐”,然后先在Cache中找,结果很糟:

没有找到!

这时马上用TextOut写到Texture上(现在还是属于第1个fps的时间范围内),而接下来的59个fps(甚至更多),都不用再调TextOut了,而是直接从我们的Cache:

Texture上Copy到屏幕上,速度得到了保障。

谈到GDI的函数,为了实现设备无关性,它们的速度都很慢。

其实它们也不像说得那么慢,如果不是每一帧都要调用它们,也算是蛮快的^_^。

那么这个RECTrect,就代表着这个文字所对应在Texture上的区域位置。

使用什么东西来把这n个Char串起来呢,一般会想到的是链表,原因无非有2个:

1随时有新的字加进来,而内存是不连续的2它几乎没有容量的限制(除非是内存用完了)。

不过链表的访问速度是很慢的,如果使用像数组这样的东西就好了。

仔细想想,在这里,我们用来存储的Cache,最大也就是256*256(理由上面说了),所以大小应该会是固定的。

我们只需要在数组中的给每一个汉字加上一个标志,说明这个位置的使用情况。

那么就使用数组吧,这样的话,访问的速度要更快一些,直接首地址+偏移量就够了,不必像链表,在查找时需要逐node访问。

当然,我绝不会想到用newChar来申请这个数组。

因为这样做实在没有必要,请不要过于迷信自己的能力,在STL中已经有vector了,为什么还要自己写呢?

^_^最后的一个bool成员变量isUsing,也就是上面所说,用来标志使用情况的。

实际的操作

上面考虑了那么多,我认为都是实际操作之前所应该有的。

先谈谈如何显示吧,因为在DirectX8.1中已经将DirectDraw和Direct3D融合为DirectGraphics了。

所以无法像原来那样了„„„„哦,实在有太多东西要讲了,我还是推荐几篇文章给你吧^_^:

http:

http:

http:

接下来,我会假设你已经具备了在DirectX8.1中绘图的基本概念了,所以在你继续往下阅读之前,请务必先仔细阅读以上推荐的文章。

前面提到,需要一个vector来对应Texture上各个位置文字的信息,上面已经创建了一个结构Char,则这个vector的定义为:

vector_vBuf;//记录缓冲中现有的文字情况首先,由于可以利用硬件的放大缩小机能,所以字体的大小精度要求不是很高,只需要支持16*16和24*24大小的字体就可以了。

我们需要一个这样的初始化函数:

boolCFont:

:

/*-------------------------------------------------------------

LPDIRECT3DDEVICE8pd3dDevice---D3DDevice设备

charszFontName[]---字体名(如:

xx)

intnSize---字体大小,只支持16和24

intnLevel---纹理的大小级别

-------------------------------------------------------------*/

Init(LPDIRECT3DDEVICE8pd3dDevice,charszFontName[],intnSize,intnLevel)。

在DirectX8.1中,由SetTexture(„)所贴的图的大小,也就是Texture的大小,是有大小限制的,长和宽都必须是2^n,而且位图越大,所花费的显存越大,这样留给其他显示用的显存就少了。

所以,必须根据需求的不同,来自定Texture(也就是Cache)的大小。

因为汉字点阵大小的原因,所以从实用角度而言(比方说只是显示fps或是短小的标题),开辟一个64*64大小的Texture,才能满足最低情况下的需要(这时如果选择16点阵的话可以存放16个汉字,24点阵可以存放7个,依次类推„„)。

根据设置,创建Texture:

_TextureSize=32<

_TextSize=nSize;//文字大小

_TextureSize=32<

_RowNum=_TextureSize/_TextSize;//计算一行可以容纳多少个文字_Max=_RowNum*_RowNum;//计算缓冲最大值

创建字体,还是需要使用Win32API。

也就是先创建一个HDC:

_hDc=CreateCompatibleDC(NULL);

然后创建一个BITMAP和一个FONT,将它们与HDC关联起来。

LOGFONTLogFont;

ZeroMemory(&LogFont,sizeof(LogFont));

LogFont.lfHeight=-_TextSize;

LogFont.lfWidth=0;

LogFont.lfEscapement=0;

LogFont.lfOrientation=0;

LogFont.lfWeight=FW_BOLD;

LogFont.lfItalic=FALSE;

LogFont.lfUnderline=FALSE;

LogFont.lfStrikeOut=FALSE;

LogFont.lfCharSet=DEFAULT_CHARSET;

LogFont.lfOutPrecision=OUT_DEFAULT_PRECIS;

LogFont.lfClipPrecision=CLIP_DEFAULT_PRECIS;

LogFont.lfQuality=DEFAULT_QUALITY;

LogFont.lfPitchAndFamily=DEFAULT_PITCH;

lstrcpy(LogFont.lfFaceName,szFontName);

_hFont=CreateFontIndirect(&LogFont);

if(NULL==_hFont){DeleteDC(_hDc);

returnfalse;}(只需要创建一个字体大小的BITMAP即可)

BITMAPINFObmi;

ZeroMemory(&bmi.bmiHeader,sizeof(BITMAPINFOHEADER));

bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);

bmi.bmiHeader.biWidth=_TextSize;

bmi.bmiHeader.biHeight=-_TextSize;

bmi.bmiHeader.biPlanes=1;

bmi.bmiHeader.biBitCount=32;

bmi.bmiHeader.biCompression=BI_RGB;

(这里需要定义一个指针指向位图的数据:

DWORD*_pBits;//位图的数据指针)

_hBmp=CreateDIBSection(_hDc,&bmi,DIB_RGB_COLORS,

(void**)&_pBits,NULL,0);

if(NULL==_hBmp||NULL==_pBits){DeleteObject(_hFont);

DeleteDC(_hDc);

returnfalse;}//将hBmp和hFont加入到hDc

SelectObject(_hDc,_hBmp);

SelectObject(_hDc,_hFont);

接着设置背景色和文字色:

SetTextColor(_hDc,RGB(255,255,255));

SetBkColor(_hDc,0);

设置文字为上对齐:

SetTextAlign(_hDc,TA_TOP);

创建Texture所需要的顶点缓冲:

if(FAILED(_pd3dDevice->CreateVertexBuffer(_Max*6*sizeof(FONT2DVERTEX),

D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC,0,

D3DPOOL_DEFAULT,&_pVB))){DeleteObject(_hFont);

DeleteObject(_hBmp);

DeleteDC(_hDc);

returnfalse;}创建Texture

if(FAILED(_pd3dDevice->CreateTexture(_TextureSize,_TextureSize,1,0,D3DFMT_A4R4G4B4,D3DPOOL_MANAGED,&_pTexture))){DeleteObject(_hFont);

DeleteObject(_hBmp);

DeleteDC(_hDc);

SAFE_RELEASE(_pVB);

returnfalse;}设置渲染设备的渲染属性:

_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE,TRUE

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 育儿理论经验

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1