设为首页收藏本站官方微博

【汉化资料】游戏中汉字显示的实现与技巧

[复制链接]
查看: 1943|回复: 1
打印 上一主题 下一主题

【汉化资料】游戏中汉字显示的实现与技巧

跳转到指定楼层
楼主
发表于 2008-10-22 17:34 | 只看该作者 回帖奖励 |正序浏览 |阅读模式

【汉化资料】游戏中汉字显示的实现与技巧

讲了一些TTF的知识,也许对汉化有用7 j; }) |7 c. B0 c' o6 I

0 V' c) p$ E# A6 I3 R4 v) y
( F0 R+ f# }' a. u. r游戏中汉字显示的实现与技巧
! j- i7 w- g$ @- t3 i1 F1 Z作者:炎龙工作室 千里马肝/ ~9 C6 D' d+ L" |1 C9 I# I) w
版本:v1.0
9 B' H7 a9 ]( M2 n7 K最后更新日期:2002-3-30( Y$ V- q) r& A" r
绪言; Q) n/ K9 [9 p% b- r3 B
在游戏中,因为我们是中国人麻,通常都需要显示汉字,比方说交待剧情。而对于文字的显示,英文的显示要较其简单得多,因为只有26个字母,就算再加一些标点、符号什么的,用一张位图,就可以足以显示所有的单词了,而相关实现技巧,也比较轻松。
4 T+ O' r2 p" [' |% d  O而中文的显示方法,要复杂得许多。记得原来在DOS下,汉字的显示都是读的UCDOS的点阵字库,而点阵字库的读取方法,在UCDOS SDK中都有源代码可以参考。但是自从Windows操作系统开始,我们开始了解到一种更好的字库,它就是TTF。( i* M3 O0 w/ B. z4 q9 i& N& Q
注:以下我所指的开发环境,除非明确说明,默认的平台是VC6.0+DirectX8.1,使用D3D来加速2D。然后使用的STL是用的SGI实现的那一套STL。
, `+ u* T: e6 Z9 Q' M  _点阵字库
0 v5 g3 b7 M) [0 w! _包括现在,有很多游戏都还是使用的点阵字库。因为操作起来比较方便,加上这方面的经验已经积累了好几年了。通常如果只是一种字体就可以满足需要的话,它会是一个比较好、快的解决办法。但是它有3个缺点:) B$ A- \  f* p) j' o* y# _
1.    如果放大显示,不做处理的话,显示出来的汉字,是很难看的。
) j: }4 }0 O/ a& x2.    像是UCDOS所提供的点阵字库,只有24点阵的有几种字体,如:宋体、黑体、揩体…,而16点阵的好象就只有宋体一种。% q( ^  j/ U$ q+ [+ _4 c  \* v* }
3.    点阵字库,通常是有版权的,尤其是第三方制作的汉字库(如:方正)。6 b$ C# E  {2 O& s
在这样的情况下,当我们写好这样的一个显示函数,就算是解决了如:放大、快速显示等问题的话,可供选择的字体还是太过于局限了。所以,在字体的要求比较强的情况下,点阵字库并不是一个好的解决方法,他不够灵活。尽管我们对于它的操作是如此得熟练,可以写出优美的代码来展示我们的编程技巧。" `- q( R, }0 L; i* \7 m+ d# t
TTF' l; N. C$ E$ p" h* ?4 W
TTF是True Type Font的简称。在Windows\Fonts目录下面,我们可以看到许多后缀为ttf的文件,它就是接下来我们接下来所要谈到的。
. h3 P0 a% D( k9 kTTF是一种矢量字库。我们经常可以听到矢量这个词,像是FLASH中的矢量图形,在100*100分辨率下制作的flash,就算它放大为全屏,显示出的画面也不会出现马赛克。所谓矢量,其实说白了就是用点和线来描述图形,这样,在图形需要放大的时候,只要把所有这个图形的点和线放大相应的倍数就可以了。而且,在网站上有很多的TTF字库可以下载,或者你可以去买一些专门的字库光盘。然后在你发行你精心制作的游戏时,可以顺便捎上这些后缀为.ttf的文件就行了。包括Quake这样的惊世之作,也都是用的TTF字库。, o+ Z& `) `& y7 @6 L7 \" h& S
这样,我们就可以解决点阵汉字的一些问题。通过TTF,我们在字体的质量和字库的数量上获得了暂时性的胜利。8 I' R- c: j& H, k; d$ a! T5 E
字库的读取和显示
) v! g; ^( w- d7 E先前谈到点阵字库,只需要很简单的一些操作,就可以显示出想要的汉字。下面我给出一个读取hzk16的函数,它需要一个Surface以供显示用:& X8 O' Z5 ^5 c6 d9 H. b8 D% Z  Y
#include <io.h>
! `: s: W6 ~, J& q) Q, m#include <stdio.h>
- u; I0 t0 B4 c) |1 a# h#include <conio.h>- B& G; o5 z7 Z! S! V; G
) ^3 b- m/ M; K8 y* _1 b
// 读取16x166 j# y4 s5 _2 [5 h/ n0 z
void DispHZ16(int x, int y, BYTE *Str, LPDIRECTDRAWSURFACE surf)( M" ~3 j* Z# o- P
{: Z  ]! l5 P! K! H
    const int Mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
8 I: C8 k; A- O    FILE *HzkFp;
, ]* X; W6 \4 _  i    WORD i, j, k=0, m;
- N0 ?3 @2 S2 N- v) }# I$ `    WORD HzNum;; {9 A7 s: e. S- r2 s
    WORD QuHao;
, H4 ~- e- g! v' \0 F    WORD WeiHao;
. P% X; i+ e, n, B2 y" I9 }2 E    long offset;
# H9 h4 W; x2 p$ W1 Q6 ~+ e    BYTE dotBuffer[32];   
+ U& ^* I0 x: \. v3 x5 [  Q2 }  V" X5 ~0 D. S
    HzkFp = fopen("HZK16", "rb");
/ q! o, Q. \' S# U9 u   
1 [3 U) h; ^& C# S$ K    HzNum = strlen((const char *)Str)/2;
; Y8 z- |0 P0 @6 t/ ~% \
0 m! l9 H5 t0 C# c4 v5 I- ?! I" i    DDSURFACEDESC       ddsd;! g, m! W5 }/ r5 R! g% ^7 H7 [
    LPWORD              lpSurface;
& O6 \0 U: h0 @6 b0 j    HRESULT             ddrval;
6 M# M7 V) }; O( E3 S    8 B0 o2 i, o: d: _" F
    ddsd.dwSize = sizeof(ddsd);. ?$ x7 e' \* _& K+ k9 r& W
   
& s/ H! Y" @6 l3 S7 F& z1 z0 h- l    while((ddrval=surf->Lock(NULL, &ddsd, 0, NULL))==DDERR_WASSTILLDRAWING);
, ]: }: r3 P0 z: d1 P3 |7 ~& z    if(ddrval == DD_OK)* V+ v2 d8 F/ T9 ^# g! i% h2 K8 f$ r
        lpSurface = (LPWORD)ddsd.lpSurface;
/ a: M* F# w# Z8 |" @- f
4 o% D$ }3 v" M9 B$ E    for(i = 0; i<HzNum; i++)) y' a$ J2 b2 h% ?8 C0 F  S3 G
    {    5 b  j2 h: ?+ @3 f6 y
        QuHao = Str[i*2]-160;& B7 Q, X/ Q) y& U* \) P2 v5 |
        WeiHao = Str[i*2+1]-160;
6 K2 l9 R# u8 w5 J# O" @# P5 @' N" l9 x+ a- z. D
        offset = ((QuHao - 1) * 94 + (WeiHao-1))*32;0 \: @3 \3 \' {7 ^" ?+ N# y
7 J, n7 W- u. g' b) X
        fseek(HzkFp, offset, SEEK_SET);1 U4 \4 {* O; Y
        fread(dotBuffer, 32, 1, HzkFp);
! I! f3 K- r' R+ {' H' X, q& ]
7 O! L" y2 i; I& w3 l0 X        for(j=0;j<16;j++) ' D0 T* i: }- ~0 l5 f4 n1 C- Y7 s
            for(k=0;k<2;k++)
  ~+ I$ u8 l" U0 x" d: ~* B6 j( p                for(m=0;m<8;m++)
* U& i2 ?6 E0 m- ?                    if(dotBuffer[j*2+k] & Mask[m])
, A- O, J. F. {' w6 S& e: Z' H/ q                    {
- k0 g: h+ t2 I3 z                        lpSurface[ddsd.lPitch*(y+j+1) + x+k*8+m] = 0x000000;2 Y2 |, E* r9 L8 r8 w9 k) V* Q6 t
                    }
0 u2 x8 t* e6 X; ~9 z6 ]) H: H        x+=16;
+ E3 G* q' ?6 ?. C/ J! C    }
- x: y7 d( T$ k8 f
" S# F7 l  H2 T5 m* O. s3 k7 q    surf->Unlock(NULL);
4 s/ m* P" A9 |" j; ]% i7 m# K, l% X# y+ U9 F" l2 R
    fclose(HzkFp);$ Z5 T8 d$ b3 f0 S7 B
}
; X( ~/ n5 T8 B! d" l其实原理很简单:) x# B9 r( d6 d) A+ n
1.    打开字库3 ]/ V6 u5 J' J6 }0 ~
2.    计算字符串长度(这个函数只支持中文),并且Lock Surface
2 [; y6 S/ e7 @; Y; ^3.    依次计算出每个汉字所对应的区码和位码(汉字的第1个字节是区码,第2个字节就是位码),然后通过公式计算出这个汉字在字库中的偏移量:! ]4 ?. q7 Y+ o3 b, F
offset = ((QuHao - 1) * 94 + (WeiHao-1))*32;
9 f5 J7 C( O# B6 U- p4.    读出一个32个字节的点阵
, Z# G0 D+ X8 v- _) I5.    绘制到Surface上% @' Z7 @' n  G2 |1 z9 D) F4 j9 b
以上只是16*16点阵字库的显示方法,24*24的读取方法与之类似,大家可以参照相关资料来书写出自己的代码。- M  G% M7 G5 a. I+ p0 L# J2 p
* C( }* I+ k( I+ q
如何显示TTF字库呢,有很多种手段,下面我按从简单到复杂的的顺序依次介绍:4 n5 m2 {9 V8 r( n1 O7 b* ?
1.    使用Windows API,也就是大家所熟悉的TextOut。通过它,还需要一个HDC(设备句柄),我们就可以随意地在屏幕任何地方显示出文字了。
; @. n; p' ]: M6 |. V& y2.    在http://www.freetype.org,有一个FreeType的免费库,而且是OpenSource的。它目前有2个版本:1.0和2.0。其区别在于,1.0只能读取TTF格式的,而2.0支持更多的文件格式,在使用它之前请详细阅读所要遵循的Licence,以下是摘自FreeType2.0对字库的支持列表:- N  k$ [% F) Q$ Y1 f
o    TrueType fonts (and collections)
1 r' c& S( d# f" z  E% \, Co    Type 1 fonts # v) }5 C5 u  N
o    CID-keyed Type 1 fonts 4 M( o2 Q& G4 z- N
o    CFF fonts 3 f5 f2 A4 Q: N( j
o    OpenType fonts (both TrueType and CFF variants) ( {/ q" v$ ^- I6 P7 T5 @" }. [0 F
o    SFNT-based bitmap fonts ( G; ?' q& I# l
o    X11 PCF fonts 7 O& h4 r8 a, m0 ?
o    Windows FNT fonts
) R3 B' [/ {5 _& e3.    自己研究TTF的格式,然后自己来操作。
2 P* A# W$ W. x6 d% D) O; b5 Y" i2 C( G0 R& @0 D
....... ╮╮ + R7 l3 I5 `" O$ _& ~: y5 n
      \█/倒!
4 Z' P0 b3 T# ^0 M1 Z4 E4 h" s: y  N      ●
+ Z+ g  |- j7 Y# m" U% M虽然我们想要把每一件事情都做好,但是也不是每一件事情都要亲历亲为。如果你非要这样,也行^____^,但是过不了多久,你就会陷入泥沼,到时候你会发现自己的热情正在慢慢被磨灭,什么叫做抓狂,相信你很快就会知道^_^。
9 Q+ c0 G2 m6 `- K
' S  a* Z( X* d9 c: ~& a9 H在有多种选择可以取舍的情况下,我们需要考虑一下,对比一下各种解决方法的优劣。3 U0 M4 {0 v, \5 W* ^& n

/ C  c" ^. i2 Q8 c; Z3 x4 `在DirectDraw时代,我们都不自觉地喜欢上了GetDC,因为……多方便啊。可是现在已经到了DirectX8.1时代了(我要使劲地摇那些还沉醉于DirectX7中,为如何在使用alpha时提升那可怜的1、2个fps的朋友们:醒醒,该起床了!),HDC已经被M$列为禁用品。怎么办呢?是的,你可能已经想到了,我们还一直保存着窗口的hWnd呢,可以通过它来得到hdc,从而调用那些需要hdc的API,可是,这样做是更为愚蠢的,这样对你是没有一点好处的,不信,你就试试吧。有一句话,请牢记:要想你的游戏有更快的速度的话,请不要再去碰HDC了。8 R& K1 L; p; [0 `  p/ y) X) x- P
我们非常清楚hdc是一个超慢的解决办法,它无法在我们的高速游戏中满60分及格。下面来看看FreeType,它更像是一个Service。它的解决方法是,先通过一系列的初始化和设置,告诉FreeType字体的名字和大小等,然后它会动态地申请一个Graphic,再把我们要显示的字画到这个Graphic上,你还可以把它保存为tga格式。不过我们最终所想要的不是这个,所以可能我们还需要从这个Graphic上逐点读取或者用CopyRect,然后再画到我们的画面上。其实它已经是很方便的了,可是需要你去学习如何配置和使用它,这是很花时间的一件事情,而且它最大的优点是可以跨平台,我们需要它吗?如果有一个更为简单的办法,像是如果Textout不是那么慢的话,就好了……
8 P8 Q; O8 @4 r在这里,顺便谈一下另2个字体显示类:ID3DXFont和CD3DFONT。可能早就有人会说怎么在上面的列表中没有它们?原因我会在下面慢慢地说明:2 u# A  U' O" ~. q8 L( Y, W0 J6 S% k& a
ID3DXFont,它存在于D3DX库中,一个现成的字体类,不过对于它的处理方法……我实在不敢恭维,就引用一位大师所说的话来表达我的看法吧: 在内部实现中, ID3DXFont::DrawText()函数确实做了我上面讨论的工作,先建立一张GDI兼容的位图,把文本绘制到位图上,而后把位图拷贝到纹理贴图上去,最后把纹理渲染到屏幕上。这样你就聚齐了所有的龟速的原始GDI函数,还包括了一大堆的额外开销 — 最终,这个函数比原来GDI的DrawTextEx()函数要慢上超过六倍……
3 B* z" l1 f9 j0 p8 RCD3DFONT,是由M$在D3D的框架代码中提供。不过它只能显示英文,有很多朋友通过自己定制和修改这个类,来实现自己的中文显示。不过效果都不是很好。其实原理,跟ID3DXFont的方法差不多,不过处理方法要聪明了一点。$ c( f; |$ n: a3 T
分析与思考
& i4 _! n7 k& G3 U# M那么我们应该怎么办呢?通常我们会幻想,如果可以像处理英文那样,把所有的汉字都保存在一张位图里,该有多好。这样,显示的速度就不是问题了,直接可以CopyRect上去。可是,这样可能吗?首先,必须每一种字体都要生成这样的一个巨型位图。而且据说在GB2312中,一共有6000多个汉字,就算是用16*16,oh my god,这个位图该有多大啊(据说会有2.5M^__^)!!!而且在DirectX8.1中,对于Texture(显示的最小单位,就好象是原来DirectSurface的概念一样。说过多少遍了,不要再用DirectX8以前的东西了。不要试着去回忆那些美好的过去,我很明白,要你一下子放弃原来多年所获得的成就,是一件很痛苦的事情,但是包袱太重,是会影响进步的。就像是我们的国家……扯远了),不同的显卡,支持的最大容量也是不同的。比方说早期的Voodoo,只支持256*256大小的Texture。而在我的显卡(Geforce2 MX 200)上测试,支持最大2048*2048大小的Texture。对于这样的硬件不确定性,我们只能取其最小值,也就是256*256。6 Q3 M$ {1 V% F1 n8 B9 v+ F
汉字虽然很多,但是常用的汉字,其实也就只有那么几百个。像这样的字:鬯、鞴,你一辈子会看到多少次呢?如果可以做一个类似于Cache的东西,保存着常用的那些个汉字,在需要显示的的时候,先在Cache中查找,如果有的话,就马上画上去;如果没有,就从字库中提取到Cache中。这样的话,在使用Texture来保存汉字的位图信息的同时,对于每个汉字,我们还要定义一个结构,然后用一个东西把它串起来,综合它们2个,也就实现了我们所要的Cache了。刚开始,我所定义的结构是这样的:/ {" P+ e& B: |0 ?
struct Char{( r) h! @3 X' f* Y0 }! j/ ~: r
  char hz[3];   // 保存汉字
! f) Z7 j8 u9 a  int frequency;// 使用频率7 M. H: o! [( e* \  I
  RECT rect;    // 这个字对应位图的区域
# k- D1 N/ k0 r1 r- M" v  Bool isUsing; // 是否使用
2 J3 l7 q% C! Z}4 f5 a1 @3 ^# Z$ p
对于汉字和英文,我在这里大概地讲一下原理:汉字是由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’。
  q+ g( o# c/ q& ^5 A" S接下来,对于使用char[3]来保存汉字,是否真的很合适呢?因为如果把它当作一个字符串来看的话,在查找时就需要使用 strcmp 来比较字符串了,这样一定是会影响速度的。如果不把它看作字符串(字符串的最后一个字节需要以’\0’结尾),只用char[2]的话,我们可以只是简单地调用宏MAKEWORD,把2个byte压成1个WORD。当把文字作为一个WORD来看的时候,这样查找比较时可以用WORD内建的==操作,这样要比调用strcmp函数要快得多。2 m, t" M1 v: M/ n8 t: E
int frequency用来标志每个WORD的使用频率。设想,如果一个字已经存在于Cache中,以后每对它调用一次,就让frequency++。这样做还有一个用意是,是否可以在一个合适的时候,以frequency为参照来对这整个Cache排个序,把常用的字放在前面。那么在显示时,可以先在Cache中查找所要显示的字是否已经存在于Cache中,如果有则直接显示,没有的话才需要采取某种手段将字加入到Cache中。一些常用的字(像:我、的、着、了、过……),使得显示的速度将会大大提高。, N+ S% Q& |2 t2 m
其实上面说了半天的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的函数,为了实现设备无关性,它们的速度都很慢。其实它们也不像说得那么慢,如果不是每一帧都要调用它们,也算是蛮快的^_^。那么这个RECT rect,就代表着这个文字所对应在Texture上的区域位置。) l  D! n* ^& }( P% a, U1 ~2 r$ U
使用什么东西来把这n个Char串起来呢,一般会想到的是链表,原因无非有2个:1 随时有新的字加进来,而内存是不连续的 2 它几乎没有容量的限制(除非是内存用完了)。不过链表的访问速度是很慢的,如果使用像数组这样的东西就好了。仔细想想,在这里,我们用来存储的Cache,最大也就是256*256(理由上面说了),所以大小应该会是固定的。我们只需要在数组中的给每一个汉字加上一个标志,说明这个位置的使用情况。那么就使用数组吧,这样的话,访问的速度要更快一些,直接首地址+偏移量就够了,不必像链表,在查找时需要逐node访问。当然,我绝不会想到用new  Char来申请这个数组。因为这样做实在没有必要,请不要过于迷信自己的能力,在STL中已经有vector了,为什么还要自己写呢?^_^最后的一个bool成员变量isUsing,也就是上面所说,用来标志使用情况的。
3 c# V1 C1 m8 e! N" z0 C实际的操作) J% _4 U( P0 l: l+ Q7 V" W
上面考虑了那么多,我认为都是实际操作之前所应该有的。先谈谈如何显示吧,因为在DirectX8.1中已经将DirectDraw和Direct3D融合为DirectGraphics了。所以无法像原来那样了…………哦,实在有太多东西要讲了,我还是推荐几篇文章给你吧^_^:. c. r) \, A2 l; r$ V4 {2 P
http://vip.6to23.com/mays/develop/directx/200201/Geczy3Din2D.htm* p' p* m; ]  }8 j) d
http://vip.6to23.com/mays/develop/directx/200201/GESurface.htm! [: O# S8 m  w" _7 l
http://vip.6to23.com/mays/develop/directx/200112/2DGtoDX8.htm
" H# s+ ~% E+ R( r( S" p0 J- \http://vip.6to23.com/mays/develop/directx/200201/DX8adv2D.htm! F+ w+ c/ j2 w' o% N
接下来,我会假设你已经具备了在DirectX8.1中绘图的基本概念了,所以在你继续往下阅读之前,请务必先仔细阅读以上推荐的文章。, r: r0 k$ }3 C! I1 G$ \
前面提到,需要一个vector来对应Texture上各个位置文字的信息,上面已经创建了一个结构Char,则这个vector的定义为:# E% Z& x- E  J" p8 l
    vector <Char> _vBuf;                    // 记录缓冲中现有的文字情况
$ p2 W: O9 n- [5 S首先,由于可以利用硬件的放大缩小机能,所以字体的大小精度要求不是很高,只需要支持16*16和24*24大小的字体就可以了。我们需要一个这样的初始化函数:
% a+ s' H% i" @6 O! E7 |  rbool CFont:: ! g) ]/ p  r  \  ?! C
/*-------------------------------------------------------------. p6 a2 e8 J5 ?  ~
LPDIRECT3DDEVICE8 pd3dDevice  ---  D3DDevice设备
! v& q3 o/ @4 U- z7 W( W# Q& d5 Qchar szFontName[]              ---  字体名(如: 宋体)
4 U7 n2 g, q' j- hint nSize                      ---  字体大小, 只支持16和24
4 Y0 S( A+ F+ z* _% f  B: Q) Zint nLevel                      ---  纹理的大小级别
' k2 y2 [  W3 ~( Z-------------------------------------------------------------*/
) j2 G# W+ A3 d& b% g5 v4 L1 yInit( LPDIRECT3DDEVICE8 pd3dDevice, char szFontName[], int nSize, int nLevel )。
+ G& `" S. r6 O; V2 z: @( R
8 `8 z1 J0 u6 j# }% m; `/ I; ?在DirectX8.1中,由SetTexture(…)所贴的图的大小,也就是Texture的大小,是有大小限制的,长和宽都必须是2^n,而且位图越大,所花费的显存越大,这样留给其他显示用的显存就少了。所以,必须根据需求的不同,来自定Texture(也就是Cache)的大小。因为汉字点阵大小的原因,所以从实用角度而言(比方说只是显示fps或是短小的标题),开辟一个64*64大小的Texture,才能满足最低情况下的需要(这时如果选择16点阵的话可以存放16个汉字,24点阵可以存放7个,依次类推……)。% Y8 ^0 g/ b- \# z8 e" x! h- b
根据设置,创建Texture:1 k3 ?" k% C  A5 z* Q: m) D5 O8 q
    _TextureSize = 32 << nLevel;        // 纹理大小
2 x/ k8 T& P8 \9 N, Y1 S    _TextSize     = nSize;                // 文字大小; ^' b3 q& d4 [( c( M' g4 m
    _TextureSize = 32 << nLevel;        // 纹理大小
; k) t: r+ |: z; i) p    4 {+ r. O7 V' \! Z
    _RowNum = _TextureSize / _TextSize;    // 计算一行可以容纳多少个文字
* V& H/ o9 W; C9 q" Z    _Max = _RowNum * _RowNum;            // 计算缓冲最大值, c3 N" C. h; n6 V; Q4 h% u- D$ u
4 l% j# O! s% K$ P* P0 w
创建字体,还是需要使用Win32 API。也就是先创建一个HDC:/ Q' X0 Q7 v2 C! {7 g: ?/ y+ [
    _hDc = CreateCompatibleDC(NULL);, K/ x' M7 N) |' N3 t& x  _' g8 E& x

' J# v, E" J9 L! }然后创建一个BITMAP和一个FONT,将它们与HDC关联起来。  R7 K& l, I1 Y) j2 \
    LOGFONT LogFont;6 o$ @+ E1 H5 c( [
    ZeroMemory( &LogFont, sizeof(LogFont) );
5 P' R2 C" p/ k* `    LogFont.lfHeight            = -_TextSize;
  Y8 O1 P, j2 {( F$ Q    LogFont.lfWidth                = 0;( A% p# w- E/ [( b
    LogFont.lfEscapement        = 0;
% s/ ?+ @) c& a, D7 v0 B    LogFont.lfOrientation        = 0;
6 S3 J3 B+ k- u8 z' [7 @2 U    LogFont.lfWeight            = FW_BOLD;
& m/ r/ t. p# e$ R    LogFont.lfItalic            = FALSE;
; Q) S0 a: W) c  U  G: w6 J    LogFont.lfUnderline            = FALSE;
' S4 S/ I, t, G  u& P' j( r2 T    LogFont.lfStrikeOut            = FALSE;
+ V1 o# V. r3 ~7 J5 {: E3 o    LogFont.lfCharSet            = DEFAULT_CHARSET;
2 S6 @2 k4 v5 U% A! O0 o    LogFont.lfOutPrecision        = OUT_DEFAULT_PRECIS; ! Y9 v" s3 B$ A: D4 L8 L! f, F
    LogFont.lfClipPrecision        = CLIP_DEFAULT_PRECIS; ! u# d2 g) J9 x) t. z
    LogFont.lfQuality            = DEFAULT_QUALITY;
* U# Z  x5 U8 f0 S( c% s# G* x. j    LogFont.lfPitchAndFamily    = DEFAULT_PITCH;
# W+ [, T4 S' F) @: }3 Y    lstrcpy( LogFont.lfFaceName, szFontName );
) ^: S7 |- g4 P0 Z   
+ P  Q7 ]5 `$ @( k* M    _hFont = CreateFontIndirect( &LogFont );
4 m5 s0 o- Y" A9 p' x9 \( I    if ( NULL == _hFont )4 j9 d& L3 Y* a- z( O
    {# }. T% |$ I: t- x8 h
        DeleteDC( _hDc );7 X0 X: M0 Y, r
        return false;
4 N% @  P# l/ g. P( e$ d    }6 P4 E$ F+ k1 ~6 S
   
* ?9 U- C! i  E(只需要创建一个字体大小的BITMAP即可)6 R$ R2 q) W$ ], F" X0 o
    BITMAPINFO bmi;
, p  @' E, L" ~0 R$ i' z( \    ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
' Z# X: y4 N) D- B/ m    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);& r, o2 }' D" W
    bmi.bmiHeader.biWidth        = _TextSize;
9 T7 g5 |/ o% b( ~: A0 n4 a+ E# C+ d    bmi.bmiHeader.biHeight        = -_TextSize;+ V5 d3 U0 t/ }
    bmi.bmiHeader.biPlanes        = 1;/ E5 a* V4 Y; K4 H: p3 f" ]  `
    bmi.bmiHeader.biBitCount    = 32;* O$ x+ Z- y6 t0 h$ @* p( @
    bmi.bmiHeader.biCompression = BI_RGB;
/ C' A7 _- J/ p' A$ s0 ]   
, d. `4 z  v6 ]% c) E5 o7 t8 \0 B4 G(这里需要定义一个指针指向位图的数据:
* L0 T: g! A% F    DWORD *        _pBits;            // 位图的数据指针)2 K: X" g7 Z+ }' b; B- Q+ `. i

. ~! y5 N9 S( r5 G    _hBmp = CreateDIBSection( _hDc, &bmi, DIB_RGB_COLORS,
0 v( J& V7 }# c6 ^% g        (void **) &_pBits, NULL, 0 );9 {5 X5 _) P' V$ G) Q3 d- d
    if ( NULL == _hBmp || NULL == _pBits )- K. l# c7 H- V- J% Z: ^
    {8 ?, ~. o! p3 X; Y4 t
        DeleteObject( _hFont );5 `5 \1 s/ x+ b
        DeleteDC( _hDc );
% ?- g8 Q+ _! e% Z        return false;( ?0 T2 I' ?, O' z; I, ~( Q: ?
    }
( A# f$ A+ a2 `! t    & x5 p$ j. H) @9 t+ I0 N! l
    // 将hBmp和hFont加入到hDc5 Q2 I2 Z1 x; t% y& u+ O: o
    SelectObject( _hDc, _hBmp );
% j6 R% q: C) u- v. f. k    SelectObject( _hDc, _hFont );
+ E/ x, P+ S# P9 a' b. s
. r' X" F9 u* b5 |% g接着设置背景色和文字色:0 x# o7 J+ G) A$ o' `6 ~, ^  w
    SetTextColor( _hDc, RGB(255,255,255) );
" v, Y) F! P% \2 V" V9 g% }( i    SetBkColor( _hDc, 0 );# U/ L7 H  Q( p3 S3 y/ I0 H/ s& F
" ~2 p! J! @, I6 g6 Z0 h1 B# a
设置文字为上对齐:, B) x5 `# w  Z8 z& a, F$ @5 i
    SetTextAlign( _hDc, TA_TOP );5 X2 [7 C' C2 q

: e4 q' c. C* m0 n8 D0 a创建Texture所需要的顶点缓冲:
  ^3 w/ ?* Q8 A) u1 n* \    if ( FAILED( _pd3dDevice->CreateVertexBuffer( _Max * 6 * sizeof(FONT2DVERTEX),/ R7 q' N; q' F8 u; S
        D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,4 ^. N( W% H3 x$ H/ |
        D3DPOOL_DEFAULT, &_pVB ) ) )
0 L% I  G* D" w- [/ r6 }    {
) o$ E, O: ~% N        DeleteObject( _hFont );4 G, a& [0 {/ s+ S
        DeleteObject( _hBmp );1 E. X& n# c1 W* ~0 H- Q
        DeleteDC( _hDc );
( S& y7 o# k( F) W) {        return false;8 d  p; R  l! s, |0 G( x) }
    }% @- E3 g, V. h( N* y- d, p
   
7 w5 o: O3 [' ?: v% G1 R; U创建Texture
) j' q) V% p- [3 w5 r6 D7 o    if ( FAILED( _pd3dDevice->CreateTexture( _TextureSize, _TextureSize, 1, 0,
  n' a, G3 S4 _: V8 x! P- J        D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &_pTexture ) ) )
; z' f' l* T* ^4 Z, P7 J    {
+ K% I8 d  Y7 j6 W        DeleteObject( _hFont );: m, |4 E  |- Q
        DeleteObject( _hBmp );* `8 G8 z. F) i* o
        DeleteDC( _hDc );. e8 @6 T6 R; @
        SAFE_RELEASE(_pVB);
$ H( a9 l* Z1 F2 |' p. A! q# a8 d" C         return false;
% t; y% P$ E' v0 ^" w; E# |    }' K, t9 N1 x! i2 V) h$ U
, S6 p  p, L9 s( Y* ^, U
设置渲染设备的渲染属性:
/ `8 p; M2 y$ s1 F    _pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE,   TRUE );
% A" q0 ^1 Z+ \' ?. h    _pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );
. `6 }1 a7 ~& T$ n+ R7 K  x    _pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
* d' ^6 V3 n7 Y/ g, y3 z) i    _pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE,        TRUE );0 v0 b1 V' T& J
    _pd3dDevice->SetRenderState( D3DRS_ALPHAREF,            0x08 );# q; I8 N, A) O+ F% Z4 \" ?" |
    _pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC,            D3DCMP_GREATEREQUAL );
# H* K& ^9 {' A& v    _pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,    D3DTOP_MODULATE );/ f5 _/ z: \9 O
        
- Y& T0 B  T, i/ G4 S0 f    _pd3dDevice->SetTexture( 0, _pTexture );
4 q2 G. P5 B& A& y    _pd3dDevice->SetVertexShader( D3DFVF_FONT2DVERTEX );- ~4 B; E1 P+ w4 D
    _pd3dDevice->SetStreamSource( 0, _pVB, sizeof(FONT2DVERTEX) );5 l5 R% a7 X1 l. x& p

% {3 K* a, m: ?) B/ l/ D/ E设置缓冲的最大容量
9 }/ g# ~- y7 X; X9 {    _vBuf.resize( _Max );6 T+ e) _6 }- G, H0 {4 k

) n1 g' G& t- F) i0 |7 f* q8 B这样,初始化完成了。接下来是如何把一个汉字写到Texture中,以及如何进行管理。定义函数:
- P5 @: ^6 j/ S" D! F// 得到文字在纹理中的位置
7 ?- X4 K& p6 b0 Xvoid CFont::- N6 d1 O+ Y0 w/ Z$ }: q- O
/*-------------------------------------------------------------; F! a0 Y. f7 m9 e6 A7 _
char c1   ---  文字的第1个字节
; X: [, v' m/ d$ [2 {char c2   ---  文字的第2个字节0 g: M, A3 _  O9 L
int & tX  ---  写入纹理中的坐标x
" s) l6 f; M8 _, a* ^* P4 c, Zint & tY  ---  写入纹理中的坐标y
5 P+ W0 a9 i- d0 c1 o, @0 _-------------------------------------------------------------*/# K! i. l2 l( \+ l& ^" S$ U
Char2Texture( char c1, char c2, int & tX, int & tY )" ^- Z: V1 D: W* E9 _0 ?
{0 N" c( q( Y3 s& V, _
    WORD w = MAKEWORD(c1, c2);        // 把此字变为WORD
; X. P2 S3 D: m2 \  \3 f    vector<Char>::iterator it = find( _vBuf.begin(), _vBuf.end(), w );: z1 o0 `7 M& G2 _7 `" j* T# s
    if ( it == _vBuf.end() )        // 如果没找到
7 A2 X% m" o6 J" k- |# D    {
# |4 g# K% s9 l        it = find( _vBuf.begin(), _vBuf.end(), 0 ); // 查找空闲位置
9 U% k3 |) v; x/ K% }& U        if ( it == _vBuf.end() )    // 缓冲已满
/ ]; A& q7 X. b8 n4 x8 M& v        {
; |* M8 ?8 v5 t1 O1 ^+ L3 W            for(; it!=_vBuf.begin(); it-- )
# y4 a8 o% u- [) @! E+ c) {% r            {
/ I; c1 J5 R0 Y2 v2 Y& s                it->hz = 0;
* j: D! ]) e5 K% I3 z: t) u            }1 D  Z$ k8 d' b( R
//            Log.Output( "字体缓冲已满, 清空!" );2 s" O* {& u* w0 F- h
        }5 w% u2 l5 f9 K( K5 R' O1 U& d; ?. h
7 }, r( _$ Z. ~1 J7 R9 O
        // 计算当前空闲的Char在缓冲中是第几个
# \5 {7 O4 w( `/ b7 b4 K, {        int at = it-_vBuf.begin();( ], J8 q, h% [! D3 s

/ S; O+ w7 ]# g" p1 d        // 得到空闲位置的坐标9 S* |6 V6 G: l) C$ t2 x' E/ |7 _1 \
        tX = (at % _RowNum) * _TextSize;( }+ F! C9 Y6 ]" n
        tY = (at / _RowNum) * _TextSize;5 Z* B4 v- Z% Q9 \

# {; }# {+ f* j  O( ~        // 设置这个Char为使用中; ~2 L* Z% i4 d( ?7 s& }
        (*it).hz = w;5 o8 b2 Z/ b- F

" N. _* U* q; f2 s' Y        RECT rect = {0, 0, _TextSize, _TextSize};3 R$ o; @: Y  H% c8 N. z9 v
        char sz[3] = {c1, c2, '\0'};5 w/ ]# d- r! y! Z  t& m& u
        // 填充背景为黑色(透明色)4 x2 ^8 c; \$ A( }8 n2 h- l& t" k4 ]
        FillRect( _hDc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
& |. v# \7 P% B7 m$ N+ W        // 往hBitmap上写字
) m% M( D$ K# }2 E( @% H        ::TextOut( _hDc, 0, 0, sz, c1 & 0x80 ? 2 : 1 );
$ X) p) F0 u+ f        ( L! H% k; M/ ^
        // 锁定表面, 把汉字写入纹理, 白色的是字(可见), 黑色为背景(透明)
- E( H5 M3 @) r6 W2 F  `% z        D3DLOCKED_RECT d3dlr;! D; J6 `8 [& C: `8 w3 p' A, ?' y1 _
        _pTexture->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);- ~6 Q9 d' O5 i2 p+ g7 N
        BYTE * pDstRow = (BYTE*)( (WORD *)d3dlr.pBits + tY * _TextureSize + tX );
; R# x: l$ E( {2 t        
' f: C9 B1 ?, A0 }1 M- E/ f* l        for (DWORD y=0; y<_TextSize; y++)# L- y1 g% ?4 g5 G2 D
        {: N$ t7 o; J6 {+ `1 V; e+ Y% G9 n& z9 y
            WORD * pDst16 = (WORD*)pDstRow;
6 C! `8 n( |/ t) |8 X, I! ]% Y            for (DWORD x=0; x<_TextSize; x++)4 l) _7 g/ |; b
            {
. W' Q, d; i- Y4 b: S- H" Y                BYTE bAlpha = (BYTE)((_pBits[_TextSize * y + x] & 0xff) >> 4);
5 j; y( W: r' P. Z                if (bAlpha > 0)
; D9 Z4 [+ {+ q+ M. D                    *pDst16++ = (bAlpha << 12) | 0x0fff;: L6 Z0 l7 S' ~1 @8 l8 M( x
                else
4 Z9 X2 c' O; V' a0 r) K9 D" r6 n                    *pDst16++ = 0x0000;
' X2 P# o: m: y1 q! x$ e7 u: x            }
2 h% T5 w3 q7 d3 y2 b0 [            pDstRow += d3dlr.Pitch;0 L! Y% }! ^! c0 `2 \2 I" y
        }
7 D9 Y  ~& ]/ w: h9 w        _pTexture->UnlockRect( NULL );
/ N, D% Y7 @- f    }
8 H' d9 b9 a' ^! @  T    else
% {. h( `3 M' q+ t$ F    {
( t7 s+ l9 j$ r. y: W( E: q( b+ s; P* D        // 计算当前空闲的Char在缓冲中是第几个
" z; }7 K! F6 z- z# G0 b        int at = it-_vBuf.begin();
0 }! ~2 Q& r* f7 A4 ?7 R; i$ l7 A
        // 得到这个字的坐标& d$ Z6 Q& _2 i
        tX = (at % _RowNum) * _TextSize;
; v+ h3 S9 q9 D6 h; V" P  j( A; R        tY = (at / _RowNum) * _TextSize;. f) m1 _" ^! _  ]/ O8 c; F
    }+ P  A% b- Y1 L; b5 y4 Q# Q( |
}' |2 k; y1 w) Q1 t" z/ G* W5 O$ x
以上代码中的注释已经很清楚了,相信无须我多言。这里唯一需要声明的是:原来所定义的Char结构是这样的
8 K5 _) k5 S. {  J2 T: X% @struct Char{' }4 f2 s4 H! I( _4 W! K9 e
  char hz[3];   // 保存汉字
1 P+ j/ R" e, g. e- O, t  int frequency;// 使用频率# y2 E& K- }" _" C1 ~% |$ R
  RECT rect;    // 这个字对应位图的区域
7 D/ {" W2 s5 H" q, l  Bool isUsing; // 是否使用2 O$ ~6 q6 ]3 M; |5 O% q/ Q) J
}
- e3 t" T* h& H后来因为将char hz[3]合成为WORD,所以改为WORD hz。然后对于int frequency,这个词频应该如何表现,我一直没有想到很好的方法。frequency应该在何时++呢?是在每次被使用的时候吗?但是这样的话,上面说过,游戏是以60fps的速度在刷新,如果停上1分钟的话,变量很快就会溢出了。就算是使用像是DWORD或__int64这样的巨型变量保存,也是不安全的。除非能在某个合适的时候将frequency清零,但是这个“时候”是什么时候呢?或者设置一个最大值,如65535,但是这样也基本上没什么用途,很快,所有在vector中的Char中的frequency都会++成65535的。回忆一下最初,是因为想把常用字放到vector的前面,以便每次find操作可以最快返回结果的。而经过我的测试,即使不做这样的优化操作,速度也是很快的,毕竟Cache不是很大,加上vector是连续内存空间。所以可以放弃使用int frequency。# w4 L7 }' G% f: F3 a
然后对于RECT rect,因为没有了int frequency,意味着一旦将汉字写入到Texture,其位置就不会变动了。所以,很容易根据find函数操作后的iterator,直接计算出这个汉字所在Texture的位置。这样,RECT rect也不再必须。: N/ x# I, Y! V/ I6 U- |
而bool isUsing,它本身就是个鸡肋,要也可以,这样结构更加清晰。不过,直接通过观察WORD hz为0或非0,即可实现isUsing的作用了。* Q3 W/ N* C) Q" m* _& k
为什么要对结构Char这么精雕细琢呢?
& s% Z5 u) Q) F% D1.    既然没有必要的东西,就应该删除2 A  Q' C- b# H
2.    Char结构的大小越大,vector所要求的内存越大
+ y6 A* d# S" j3.    小的结构,find可以更快地查找出所结果
- k5 }. t' L7 L为什么find会正常工作呢?这里我要大概地讲一下find是如何查找出所需的位置的:它只是简单地使用while从vector的begin一直遍历到end,逐个判断,直到找到为止。find要求必须实现自己的operator ==(),进一步跟踪到find的源码中,发现也是这样。于是前面的结构Char变成了现在这样:
. Z+ e1 U( b) T5 r- _    struct Char{
& ~6 h- [: _2 s2 x7 U! d3 v  I        WORD    hz;                // 文字6 }- L2 D. k4 V
$ O3 D6 R, r( ~3 o! J8 C
        Char() : hz(0) {}
- c1 j+ v! y  B3 ]5 C; b9 Q+ W- S& Y+ K8 I- P
        // 用作查找文字) D4 a4 P3 L; j4 K- s& X
        inline bool operator == ( WORD h ) const
/ O7 R7 k8 L# ^5 ~        {
8 u7 F8 v7 q0 u. ]9 `9 G  l; j            return hz==h ? true : false;# u( n/ p3 L  C& ^
        }" j1 x& `& X$ M
    };2 j" d; r3 ]9 [- B; n
是不是很简单?^___^: l) d" `" J) w; y& f, o

: I  w( H0 {* s终于到了显示的函数了:# b4 s2 h4 ^) I9 }
// 得到文字在纹理中的位置3 {) c3 ^# k' C, W
bool CFont::) n- n! X+ i2 ^) Z) ~
/*-------------------------------------------------------------
# }- {0 d$ S$ R* o+ x/ zchar szText[]  ---  显示的字符串
# D3 _7 I! X9 o: O8 \; @! l: fint x           ---  屏幕坐标x
5 q$ x4 o, x7 o' o! O: F1 aint y           ---  屏幕坐标y8 a5 g: \% c5 ?# D
D3DCOLOR       ---  颜色及alpha值" a  [* R- {$ p8 W0 G
int nLen       ---  字符串长度
- G5 \* c4 @3 h+ O' [float fScale   ---  放大比例8 K! c( x5 K% l( K4 ^1 i# L( S
-------------------------------------------------------------*/
- v& b( q1 [5 ITextOut( char szText[], int x, int y, D3DCOLOR color, int nLen, float fScale )
$ }! ~8 [+ x5 x9 ?& e& s{
- @; e( m' L1 L3 [9 w" f4 X    Assert( szText!=NULL );
. T$ _/ s8 X5 F  @3 w2 a3 m- z# @- K
3 e' ]. H& E6 P0 o5 J$ U    float sx = x, sy = y,
0 c* r7 P- ~5 ]1 I! P& h          offset=0, w=0, h=0, tx1=0, ty1=0, tx2=0, ty2=0;, ]5 ~$ G: q3 m7 S  u4 a6 ]
    w = h = (float)_TextSize * fScale;4 I  C" H  N) d0 f: o

3 r! W; l2 W9 a/ b' X    char ch[3] = {0,0,0};
  M* ^0 f! ^! g2 I/ T& g7 I* f* M9 ]    FONT2DVERTEX * pVertices = NULL;
5 i6 M2 i" W+ L) u6 W2 R    UINT wNumTriangles = 0;
) P  W6 D0 R! B    _pVB->Lock(0, 0, (BYTE**)&pVertices, D3DLOCK_DISCARD);- D  x( |: M& Q! k' O* d9 d; W

$ y% d. T( b9 q: _" s    if ( -1 == nLen ||                // 默认值-1
1 C; M5 D- N0 X5 y4 p$ j         nLen > lstrlen( szText ) ) // 如果nLen大于字符串实际长度, 则nLen=实际长度
+ U8 J+ R9 ?2 X8 Y+ m3 _        nLen = lstrlen( szText );' Z7 l) _- H& G/ i8 l' f1 `7 L* t
    for (int n=0; n<nLen; n++ )1 f6 L$ Q3 X/ O( i6 u0 x
    {
- y+ O% z& ]- L9 }4 F        ch[0] = szText[n];' v9 w. [8 L* Y  v( P

: [3 S1 U& X: X& w0 u        if ( ch[0]=='\n' )9 f" ~+ V" ~3 M2 s: R" R; k2 ], H
        {) R' @0 \7 d2 ~  Q
            sy+=h;* N0 ^; T/ K! ?  X! L
            sx=x;* v5 _  p4 T1 z6 q/ n
            continue;/ _! x3 P4 ^& ]
        }: E  `$ E" q  j' p9 C1 S/ V
! K+ a/ K% s: V. G6 Q" q; e) M( J
        if ( ch[0] & 0x80 )
# ~/ Q) N( J5 B3 {        {% J* b/ }+ {0 Z& e+ M' L
            n++;: A% l8 q0 Y7 ^# f, \2 G, W
            ch[1] = szText[n];
, C1 d7 k, D" \1 U            offset = w;" H( y# L& u, a- a8 T
        }
) y' H+ P  D4 [1 S        else
0 Z- o8 |+ \+ r) }  |9 w0 \/ p4 C7 W        {0 i9 S; M  N: Z; |
            ch[1] = '\0';$ }6 H" n. \: b
            offset = w / 2 ;
; H& Z' X- Q* z0 A: o/ o        }
4 n6 n1 ^9 n  u
1 }8 T. a, ^0 ^3 _* ?. b0 x" k        int a, b;
' a7 Y: X& W  I6 F) o* Z+ l        Char2Texture( ch[0], ch[1], a, b );( p+ n5 m+ b6 J7 }5 \% Y0 i  F
   
( e% q1 u- T8 @+ D        // 计算纹理左上角 0.0-1.0
" k5 C+ R0 V& @( c* c+ A        tx1 = (float)(a) / _TextureSize;/ X; i5 b8 i6 w" [& S
        ty1 = (float)(b) / _TextureSize;
5 t) M% C- k. E8 `        // 计算纹理右上角 0.0-1.0
9 F$ t; l' T5 R) i4 V( L5 o        tx2 = tx1 + (float)_TextSize / _TextureSize;
1 Z, c" B4 [& A2 R1 J4 v, Y        ty2 = ty1 + (float)_TextSize / _TextureSize;( y4 R% X5 x  C& B
+ b' R4 U+ e, o. g8 X1 p+ r
        // 填充顶点缓冲区
' y: g8 t5 a9 f        *pVertices++ = FONT2DVERTEX(sx,        sy + h, 0.9f, color, tx1, ty2);$ i8 t% R7 p2 x
        *pVertices++ = FONT2DVERTEX(sx,        sy,        0.9f, color, tx1, ty1);
. d/ d3 m0 O& j! s        *pVertices++ = FONT2DVERTEX(sx + w, sy + h, 0.9f, color, tx2, ty2);
. C& u) O1 Q% B. A        *pVertices++ = FONT2DVERTEX(sx + w, sy,        0.9f, color, tx2, ty1);+ ?* ^) N1 k- h- F7 D5 q* G6 p
        *pVertices++ = FONT2DVERTEX(sx + w, sy + h,    0.9f, color, tx2, ty2);% L, E" G5 L" m2 I7 @5 ^. }
        *pVertices++ = FONT2DVERTEX(sx,        sy,        0.9f, color, tx1, ty1);
. g4 Q& h- K; A* e) z+ P- ^5 U4 ^9 v% u% `. }* i
        wNumTriangles+=2;, ]4 y( r3 e  u6 t  E) x
5 ~% B' o3 |. ~; O
        sx+=offset;    // 坐标x增量7 _4 @- ^; ]0 ^5 _
    }7 L! P5 H  n; {9 M* k/ a/ f8 J& m! i; P
    _pVB->Unlock();
, |9 F9 c1 ^2 h; L' Y: O: T  H) u    _pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, wNumTriangles );# X$ J" O& d% q* t# K, A
, P$ Y) w6 F. K+ l# v$ N3 O: [
    return true;
3 h% r6 [) T5 ]% K2 |}6 y$ t" C/ B+ q. P0 x
结束语( ~' U! K1 _5 M: i- |& V, A: C
记得有一句名言: Keep it simple and stupid.在实现功能的同时,保持代码简单、清晰是非常重要的一件事。相信在往后的日子里,在不论是别人阅读或是你自己回顾的时候,你都会发现一如既往地遵守这个守则,是多么得重要!! U" X. p) u3 q/ R5 J7 g# Q
相信通过上面我那无数的废话,加上代码中还算足够的注释,聪明的你一定能够明白这其中的原理了吧。如果以上的内容还不足以让你完全搞清楚的话,你可以登录我的主页:
6 f6 y5 C- g6 I: q" o炎龙工作室
8 T0 }9 R: @' ?8 o/ I" d9 H2 K上面不仅包括了上面所写的程序代码,还有一个用来演示效果的一个很简单的demo。
: Q1 J4 }2 }, c) E4 u: ]  U说明,以上所实现的CFont是包含在我的游戏引擎中的一个部件,而目前已经实现的部件包括有:
7 r! _! g/ f0 i! e, r  u$ Z1.    CGameFrame(游戏框架类)  -----  封装了窗口及D3D设备的建立,需要派生出自己的子类9 A0 M7 M! ^0 v3 V4 ~
2.    CAudio和CSound(声音类) -----  支持wav/mid/mp3的播放$ p2 U( a3 s0 e% G9 r- _' ?
3.    CDirectInput(控制类)    -----  键盘、鼠标操作
7 L9 _) }( a) n4.    CDirectShow(视频类)     -----  支持avi/mpg/mov等的播放
. t: d. W+ N7 l1 ^5.    CSpriteX(精灵类)        -----  方便游戏中对精灵的控制6 @. L- Z" j% h5 U1 F, [
6.    CFont(字体类)           -----  中英文字体的显示
8 t: v3 J3 E8 W) W+ h( A+ J7.    CTimer(时间类)          -----  高精度时间的控制# Z9 U& d& X4 @' y- U7 _
8.    FPS(fps 类)             -----  fps的计算; E$ E3 m0 n& ?; ]- [/ H+ \1 e
9.    LOG(日志类)             -----  游戏中的错误反应以及状态记录
  }  i: X7 R. H% t最重要的是,这个Game Engine完全是开放源代码的。关于更新的情况、版本说明以及源码下载,请随时关注我的主页!
( h$ A6 K% x4 r9 w' [接下来,我将会继续完善这个Engine,可能加入的有:高效粒子系统、斜45度角地图……

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2008-10-25 10:59 | 只看该作者
原来汉化这么复杂啊,真的由衷感谢这些游戏汉化爱好者,你们辛苦了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

冒险解谜游戏中文网 ChinaAVG

官方微博官方微信号小黑屋 微信玩家群  

(C) ChinaAVG 2004 - 2019 All Right Reserved. Powered by Discuz! X3.2
辽ICP备11008827号 | 桂公网安备 45010702000051号

冒险,与你同在。 冒险解谜游戏中文网ChinaAVG诞生于2004年9月9日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

快速回复 返回顶部 返回列表