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

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

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

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

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

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

讲了一些TTF的知识,也许对汉化有用! i! b" k: Z$ l* m
. Q) }& L  m4 K7 B: I6 Q
0 v2 w) w& S) W4 x9 {3 J. H+ ?- ^
游戏中汉字显示的实现与技巧
1 ~, O" C9 c6 g. A8 S3 E, V作者:炎龙工作室 千里马肝/ m% N( e) @, K8 V- I
版本:v1.0! a  @# [0 @4 u' r8 [! P: K
最后更新日期:2002-3-30
+ R/ _9 c9 x+ [' P4 j绪言8 I+ F. ?" c4 A  Y
在游戏中,因为我们是中国人麻,通常都需要显示汉字,比方说交待剧情。而对于文字的显示,英文的显示要较其简单得多,因为只有26个字母,就算再加一些标点、符号什么的,用一张位图,就可以足以显示所有的单词了,而相关实现技巧,也比较轻松。 * v3 u) m: ^7 _
而中文的显示方法,要复杂得许多。记得原来在DOS下,汉字的显示都是读的UCDOS的点阵字库,而点阵字库的读取方法,在UCDOS SDK中都有源代码可以参考。但是自从Windows操作系统开始,我们开始了解到一种更好的字库,它就是TTF。
/ c4 m+ W4 ]6 d, P* A注:以下我所指的开发环境,除非明确说明,默认的平台是VC6.0+DirectX8.1,使用D3D来加速2D。然后使用的STL是用的SGI实现的那一套STL。
, a2 T0 {$ V: R0 V8 Z1 L% c3 w" y0 u* H点阵字库4 c9 c2 G& V4 Y/ u* C6 S+ N0 K
包括现在,有很多游戏都还是使用的点阵字库。因为操作起来比较方便,加上这方面的经验已经积累了好几年了。通常如果只是一种字体就可以满足需要的话,它会是一个比较好、快的解决办法。但是它有3个缺点:4 Q7 f5 u% x& f2 u
1.    如果放大显示,不做处理的话,显示出来的汉字,是很难看的。
1 B3 ?0 m" K, `; }, Y1 G2.    像是UCDOS所提供的点阵字库,只有24点阵的有几种字体,如:宋体、黑体、揩体…,而16点阵的好象就只有宋体一种。; e6 j0 H( h+ B
3.    点阵字库,通常是有版权的,尤其是第三方制作的汉字库(如:方正)。
3 H( Z- q$ y$ x1 k' d在这样的情况下,当我们写好这样的一个显示函数,就算是解决了如:放大、快速显示等问题的话,可供选择的字体还是太过于局限了。所以,在字体的要求比较强的情况下,点阵字库并不是一个好的解决方法,他不够灵活。尽管我们对于它的操作是如此得熟练,可以写出优美的代码来展示我们的编程技巧。
4 d! A" i- d5 P8 d1 M$ K2 CTTF
4 `4 q( E8 }) l% f4 JTTF是True Type Font的简称。在Windows\Fonts目录下面,我们可以看到许多后缀为ttf的文件,它就是接下来我们接下来所要谈到的。
* k/ Y3 N& X- U, ]. H$ jTTF是一种矢量字库。我们经常可以听到矢量这个词,像是FLASH中的矢量图形,在100*100分辨率下制作的flash,就算它放大为全屏,显示出的画面也不会出现马赛克。所谓矢量,其实说白了就是用点和线来描述图形,这样,在图形需要放大的时候,只要把所有这个图形的点和线放大相应的倍数就可以了。而且,在网站上有很多的TTF字库可以下载,或者你可以去买一些专门的字库光盘。然后在你发行你精心制作的游戏时,可以顺便捎上这些后缀为.ttf的文件就行了。包括Quake这样的惊世之作,也都是用的TTF字库。
" v- ?) l9 x, J这样,我们就可以解决点阵汉字的一些问题。通过TTF,我们在字体的质量和字库的数量上获得了暂时性的胜利。
) j0 X3 Y$ ?% q5 k0 v+ ^字库的读取和显示0 c9 T$ B3 y  m/ O
先前谈到点阵字库,只需要很简单的一些操作,就可以显示出想要的汉字。下面我给出一个读取hzk16的函数,它需要一个Surface以供显示用:1 l# s* S0 s' K
#include <io.h># D( J+ ?# U4 e, I
#include <stdio.h>3 u9 ^4 C( I7 z7 f) o7 z
#include <conio.h>2 c' F; z, h. G0 ]7 E: _

/ Y8 Z3 S+ {& `7 x- u4 \// 读取16x16
! p  H; F+ B! jvoid DispHZ16(int x, int y, BYTE *Str, LPDIRECTDRAWSURFACE surf)% l! O- z* Y2 g$ T
{
9 q' n, s2 {* r# m8 E7 m    const int Mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
% d1 V: h. H! O6 Y' H  D1 W) c    FILE *HzkFp;- l0 V3 S; Y- e# g( v$ d0 h
    WORD i, j, k=0, m;4 S, f4 Z; t  `" }9 e5 i
    WORD HzNum;- ]* W6 U8 \" b" ~$ Y; i( a
    WORD QuHao;
; G$ W- C$ k5 [" r* o" n) j. a* ^8 o    WORD WeiHao;
3 D  A2 y* @+ v" X' e: g    long offset;  D1 G1 ^0 l# S4 F/ f2 i% e
    BYTE dotBuffer[32];   
# C4 h; X3 N; u. m0 S* R/ Q
5 e8 G1 A. w* j    HzkFp = fopen("HZK16", "rb");1 U3 i+ A4 z6 F' n2 `: O
   
/ k; l/ P& v8 \( ^6 C5 {( q* @    HzNum = strlen((const char *)Str)/2;2 @8 j* \  x' u: X
4 c3 I' D; Y$ w
    DDSURFACEDESC       ddsd;
$ O5 P( v9 A# L    LPWORD              lpSurface;, m  ?, s% |# H
    HRESULT             ddrval;+ l+ _, c( E* c3 b( R5 d
    9 m: z. \7 j) g" f; Z7 `
    ddsd.dwSize = sizeof(ddsd);4 Q5 H; j% `0 W- J
   
) d# r+ w+ S2 A% v1 w* E' u    while((ddrval=surf->Lock(NULL, &ddsd, 0, NULL))==DDERR_WASSTILLDRAWING);
4 a- G5 y% e# p: u    if(ddrval == DD_OK)
( d  L) `8 o4 h9 O        lpSurface = (LPWORD)ddsd.lpSurface;
3 k- u; \6 H8 [
6 L: c- I/ E( i' M- Z: a7 y# P. R    for(i = 0; i<HzNum; i++)0 _7 _( t. e! U5 L' M9 C, U' I: z
    {    / x% n/ d0 @& r5 l; c2 j+ a, c+ z
        QuHao = Str[i*2]-160;# j. M& g- N+ W
        WeiHao = Str[i*2+1]-160;& N6 [: F) H, ?. j& i' K$ G% }

& L* W* o/ i- F& c9 t5 u* `7 u        offset = ((QuHao - 1) * 94 + (WeiHao-1))*32;
2 r& C- U. A) ]' t7 T) [
- r7 I  u* b- ?2 B& `9 |4 U. T        fseek(HzkFp, offset, SEEK_SET);% g/ l2 e% [" U- }
        fread(dotBuffer, 32, 1, HzkFp);
; P& G4 s. y* M7 R" `2 t' e. J6 q1 w$ W5 e4 `0 ~( S& X5 L- }
        for(j=0;j<16;j++)
0 x4 Q& X2 `. j            for(k=0;k<2;k++)
) C* a3 b/ Q: j- K9 U2 k& Q- T; D                for(m=0;m<8;m++) 7 Z; l; _0 ?+ b! @" T7 t
                    if(dotBuffer[j*2+k] & Mask[m]). O/ J1 T9 f- T
                    {
3 L- w& W4 t- ?2 ^3 S" i                        lpSurface[ddsd.lPitch*(y+j+1) + x+k*8+m] = 0x000000;
$ f/ R' A; k' U# ^2 h# c  ~2 p                    }
: }% _7 _. N% ^; ?! K        x+=16;/ l% }) A( i+ |0 G: n) ~- o7 p# q. }
    }+ U* r+ y0 {- V
) j" L# t) e) W$ K; M. E. U
    surf->Unlock(NULL);/ M" ]* i0 Q* d5 A% a# t

1 p/ I) c7 X; u    fclose(HzkFp);
& U* n) E) e0 C, n}
7 c$ `4 M7 x9 @( c7 U# i! q其实原理很简单:
# f3 [; j# R/ H  v1.    打开字库
4 R; L2 N7 @' {% I) `2.    计算字符串长度(这个函数只支持中文),并且Lock Surface" d, B) T( N/ K6 G7 P1 x4 S
3.    依次计算出每个汉字所对应的区码和位码(汉字的第1个字节是区码,第2个字节就是位码),然后通过公式计算出这个汉字在字库中的偏移量:
3 V. D+ H9 {* x; G' x2 Goffset = ((QuHao - 1) * 94 + (WeiHao-1))*32;
) w5 C- @1 l! `4.    读出一个32个字节的点阵
5 ]9 ~' o  _% k# s9 V8 Y5.    绘制到Surface上. n1 i9 e0 C& ^/ J: X
以上只是16*16点阵字库的显示方法,24*24的读取方法与之类似,大家可以参照相关资料来书写出自己的代码。
- \# j( v. ^( s9 ?) r# i! N8 z$ c. x2 ^% ]: q
如何显示TTF字库呢,有很多种手段,下面我按从简单到复杂的的顺序依次介绍:: X! X& E  y  a, U
1.    使用Windows API,也就是大家所熟悉的TextOut。通过它,还需要一个HDC(设备句柄),我们就可以随意地在屏幕任何地方显示出文字了。
% ?% t) O- D! n5 N. L. Q2.    在http://www.freetype.org,有一个FreeType的免费库,而且是OpenSource的。它目前有2个版本:1.0和2.0。其区别在于,1.0只能读取TTF格式的,而2.0支持更多的文件格式,在使用它之前请详细阅读所要遵循的Licence,以下是摘自FreeType2.0对字库的支持列表:% [( G* ^( I% j  C8 h
o    TrueType fonts (and collections) . d7 j3 x( @+ l
o    Type 1 fonts
6 H. x' Z% v/ ^8 |' ?$ m, O: Po    CID-keyed Type 1 fonts $ ?. j1 y. n/ E" f7 v( m8 q
o    CFF fonts 2 e- s/ J9 ~/ S4 M$ q* F! c
o    OpenType fonts (both TrueType and CFF variants) " x, x. q- O4 G
o    SFNT-based bitmap fonts
4 [$ C  X" {( ?; f* u) c9 Io    X11 PCF fonts + L# O! S4 r! \8 n# ~+ x6 i: }
o    Windows FNT fonts
5 A; Z4 [4 S: V2 i1 z3.    自己研究TTF的格式,然后自己来操作。
  @1 ]  m2 h# u1 I2 m+ h
& ~( b; f" `! E5 A, i9 J" n....... ╮╮ 7 _! u$ B$ c. z/ i0 R( r6 a/ k
      \█/倒!
/ h- Z9 t& o4 g9 m( u, @* V; p      ●
! m+ ?8 ]1 O6 u虽然我们想要把每一件事情都做好,但是也不是每一件事情都要亲历亲为。如果你非要这样,也行^____^,但是过不了多久,你就会陷入泥沼,到时候你会发现自己的热情正在慢慢被磨灭,什么叫做抓狂,相信你很快就会知道^_^。
3 R, x7 E. H+ R* Z) U; R. }( d& l. B+ g4 F. g; J- K, y
在有多种选择可以取舍的情况下,我们需要考虑一下,对比一下各种解决方法的优劣。
, h% O+ q8 W$ x/ i2 j1 H: ?" `9 p' Z9 y% W# h& T" \
在DirectDraw时代,我们都不自觉地喜欢上了GetDC,因为……多方便啊。可是现在已经到了DirectX8.1时代了(我要使劲地摇那些还沉醉于DirectX7中,为如何在使用alpha时提升那可怜的1、2个fps的朋友们:醒醒,该起床了!),HDC已经被M$列为禁用品。怎么办呢?是的,你可能已经想到了,我们还一直保存着窗口的hWnd呢,可以通过它来得到hdc,从而调用那些需要hdc的API,可是,这样做是更为愚蠢的,这样对你是没有一点好处的,不信,你就试试吧。有一句话,请牢记:要想你的游戏有更快的速度的话,请不要再去碰HDC了。) e4 c% D" ~, b$ f* q+ _0 Z
我们非常清楚hdc是一个超慢的解决办法,它无法在我们的高速游戏中满60分及格。下面来看看FreeType,它更像是一个Service。它的解决方法是,先通过一系列的初始化和设置,告诉FreeType字体的名字和大小等,然后它会动态地申请一个Graphic,再把我们要显示的字画到这个Graphic上,你还可以把它保存为tga格式。不过我们最终所想要的不是这个,所以可能我们还需要从这个Graphic上逐点读取或者用CopyRect,然后再画到我们的画面上。其实它已经是很方便的了,可是需要你去学习如何配置和使用它,这是很花时间的一件事情,而且它最大的优点是可以跨平台,我们需要它吗?如果有一个更为简单的办法,像是如果Textout不是那么慢的话,就好了……! U9 I* e/ E+ B% [$ j, `' K4 D0 A: i5 c
在这里,顺便谈一下另2个字体显示类:ID3DXFont和CD3DFONT。可能早就有人会说怎么在上面的列表中没有它们?原因我会在下面慢慢地说明:0 Q, S2 L7 k( X; R
ID3DXFont,它存在于D3DX库中,一个现成的字体类,不过对于它的处理方法……我实在不敢恭维,就引用一位大师所说的话来表达我的看法吧: 在内部实现中, ID3DXFont::DrawText()函数确实做了我上面讨论的工作,先建立一张GDI兼容的位图,把文本绘制到位图上,而后把位图拷贝到纹理贴图上去,最后把纹理渲染到屏幕上。这样你就聚齐了所有的龟速的原始GDI函数,还包括了一大堆的额外开销 — 最终,这个函数比原来GDI的DrawTextEx()函数要慢上超过六倍……
& `$ o  L% N+ U) t6 `5 r6 e( F6 zCD3DFONT,是由M$在D3D的框架代码中提供。不过它只能显示英文,有很多朋友通过自己定制和修改这个类,来实现自己的中文显示。不过效果都不是很好。其实原理,跟ID3DXFont的方法差不多,不过处理方法要聪明了一点。
+ M, {1 |2 r7 b: {, Z' e分析与思考
: A) I# N/ i% Y, a2 z$ F: y$ K那么我们应该怎么办呢?通常我们会幻想,如果可以像处理英文那样,把所有的汉字都保存在一张位图里,该有多好。这样,显示的速度就不是问题了,直接可以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。
5 s& m1 |+ x2 u+ i& X  P汉字虽然很多,但是常用的汉字,其实也就只有那么几百个。像这样的字:鬯、鞴,你一辈子会看到多少次呢?如果可以做一个类似于Cache的东西,保存着常用的那些个汉字,在需要显示的的时候,先在Cache中查找,如果有的话,就马上画上去;如果没有,就从字库中提取到Cache中。这样的话,在使用Texture来保存汉字的位图信息的同时,对于每个汉字,我们还要定义一个结构,然后用一个东西把它串起来,综合它们2个,也就实现了我们所要的Cache了。刚开始,我所定义的结构是这样的:4 B* K2 A; L9 B# J1 t# B0 O
struct Char{3 ?% g2 ?  R* @' J) I
  char hz[3];   // 保存汉字: H# [- K: H" F( ~& o$ L- v
  int frequency;// 使用频率
8 |  p9 B: k8 o5 _1 Y' r' I; B  RECT rect;    // 这个字对应位图的区域8 x. j9 Q6 n1 M1 N) o
  Bool isUsing; // 是否使用4 x( g, N) t% s$ a3 k
}! [: h/ w9 t- l* z) Y6 r
对于汉字和英文,我在这里大概地讲一下原理:汉字是由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’。& W, Z, j- ]6 Y- K1 C- J1 n
接下来,对于使用char[3]来保存汉字,是否真的很合适呢?因为如果把它当作一个字符串来看的话,在查找时就需要使用 strcmp 来比较字符串了,这样一定是会影响速度的。如果不把它看作字符串(字符串的最后一个字节需要以’\0’结尾),只用char[2]的话,我们可以只是简单地调用宏MAKEWORD,把2个byte压成1个WORD。当把文字作为一个WORD来看的时候,这样查找比较时可以用WORD内建的==操作,这样要比调用strcmp函数要快得多。7 N- q& y5 U' [/ O. i1 S( C
int frequency用来标志每个WORD的使用频率。设想,如果一个字已经存在于Cache中,以后每对它调用一次,就让frequency++。这样做还有一个用意是,是否可以在一个合适的时候,以frequency为参照来对这整个Cache排个序,把常用的字放在前面。那么在显示时,可以先在Cache中查找所要显示的字是否已经存在于Cache中,如果有则直接显示,没有的话才需要采取某种手段将字加入到Cache中。一些常用的字(像:我、的、着、了、过……),使得显示的速度将会大大提高。9 E8 ~+ c2 `7 X9 |" @: @3 K
其实上面说了半天的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上的区域位置。
0 E, `" ^) B3 ~6 o! R使用什么东西来把这n个Char串起来呢,一般会想到的是链表,原因无非有2个:1 随时有新的字加进来,而内存是不连续的 2 它几乎没有容量的限制(除非是内存用完了)。不过链表的访问速度是很慢的,如果使用像数组这样的东西就好了。仔细想想,在这里,我们用来存储的Cache,最大也就是256*256(理由上面说了),所以大小应该会是固定的。我们只需要在数组中的给每一个汉字加上一个标志,说明这个位置的使用情况。那么就使用数组吧,这样的话,访问的速度要更快一些,直接首地址+偏移量就够了,不必像链表,在查找时需要逐node访问。当然,我绝不会想到用new  Char来申请这个数组。因为这样做实在没有必要,请不要过于迷信自己的能力,在STL中已经有vector了,为什么还要自己写呢?^_^最后的一个bool成员变量isUsing,也就是上面所说,用来标志使用情况的。" y7 `1 P' @7 `/ y3 h1 ?
实际的操作: P5 l: e" }& X# A7 Y; M) @
上面考虑了那么多,我认为都是实际操作之前所应该有的。先谈谈如何显示吧,因为在DirectX8.1中已经将DirectDraw和Direct3D融合为DirectGraphics了。所以无法像原来那样了…………哦,实在有太多东西要讲了,我还是推荐几篇文章给你吧^_^:
5 o- Y  G" d+ \http://vip.6to23.com/mays/develop/directx/200201/Geczy3Din2D.htm
. x' \, _) o( V  A6 x, Rhttp://vip.6to23.com/mays/develop/directx/200201/GESurface.htm6 Z4 p4 N% o# q( \- n
http://vip.6to23.com/mays/develop/directx/200112/2DGtoDX8.htm" B: y. U' r- A% v4 X: p0 ^8 t3 T
http://vip.6to23.com/mays/develop/directx/200201/DX8adv2D.htm
/ }& D1 j# O5 m/ Y接下来,我会假设你已经具备了在DirectX8.1中绘图的基本概念了,所以在你继续往下阅读之前,请务必先仔细阅读以上推荐的文章。, ?$ R& }# D8 J$ U% H' O8 M, v$ {
前面提到,需要一个vector来对应Texture上各个位置文字的信息,上面已经创建了一个结构Char,则这个vector的定义为:
) {7 `  s# i6 l/ x' Z; e( t    vector <Char> _vBuf;                    // 记录缓冲中现有的文字情况
# q! N% G( B8 ?$ o6 }: g) o4 d首先,由于可以利用硬件的放大缩小机能,所以字体的大小精度要求不是很高,只需要支持16*16和24*24大小的字体就可以了。我们需要一个这样的初始化函数:- Z# V8 v( K+ ?# f4 ^1 K
bool CFont::
: _2 }0 N2 T# s. {/*-------------------------------------------------------------. l9 {5 D* J/ I4 ?+ B. l
LPDIRECT3DDEVICE8 pd3dDevice  ---  D3DDevice设备8 o" B; W2 t; L
char szFontName[]              ---  字体名(如: 宋体)
/ O: z8 V  q+ a& B) |  y3 Cint nSize                      ---  字体大小, 只支持16和24
* t' e# ^  l  o; h, cint nLevel                      ---  纹理的大小级别6 d5 O$ P; [4 z5 u7 X! D
-------------------------------------------------------------*/. n" i6 U3 B+ [! v6 h% M
Init( LPDIRECT3DDEVICE8 pd3dDevice, char szFontName[], int nSize, int nLevel )。. d8 S4 m0 R. t8 W/ T
6 {$ v/ \" |' J; Y) y
在DirectX8.1中,由SetTexture(…)所贴的图的大小,也就是Texture的大小,是有大小限制的,长和宽都必须是2^n,而且位图越大,所花费的显存越大,这样留给其他显示用的显存就少了。所以,必须根据需求的不同,来自定Texture(也就是Cache)的大小。因为汉字点阵大小的原因,所以从实用角度而言(比方说只是显示fps或是短小的标题),开辟一个64*64大小的Texture,才能满足最低情况下的需要(这时如果选择16点阵的话可以存放16个汉字,24点阵可以存放7个,依次类推……)。
. `( `; @+ o- D3 O. N& G% }! c根据设置,创建Texture:
2 M- q8 U; `* I0 u6 Z# Z    _TextureSize = 32 << nLevel;        // 纹理大小
2 s' J! H; _$ r! J2 U    _TextSize     = nSize;                // 文字大小# U! w8 |, B0 z8 Q/ Y( U7 Q6 e
    _TextureSize = 32 << nLevel;        // 纹理大小$ R  L1 |. L$ I; Q. B7 y
    5 Y+ W" I& z2 J
    _RowNum = _TextureSize / _TextSize;    // 计算一行可以容纳多少个文字
1 y) H% |6 O4 e8 X# g+ d    _Max = _RowNum * _RowNum;            // 计算缓冲最大值
5 h. G/ `( i4 M: P2 N; W9 i. B0 K$ }7 H+ m& N( S5 h* X8 i
创建字体,还是需要使用Win32 API。也就是先创建一个HDC:
2 Z% Y8 M1 Y7 _4 Q- a    _hDc = CreateCompatibleDC(NULL);5 O* j/ Z' @6 v, n, D# f% {

* y: B+ K0 X' H" \/ `然后创建一个BITMAP和一个FONT,将它们与HDC关联起来。
- E9 ]1 h; ?2 a  S# \    LOGFONT LogFont;1 O9 R3 a- ]' A
    ZeroMemory( &LogFont, sizeof(LogFont) );; V& M6 V1 M. U& u, ~
    LogFont.lfHeight            = -_TextSize;* O; t8 c' A$ g# b; V: r
    LogFont.lfWidth                = 0;8 c  {# m: \9 K& A- @
    LogFont.lfEscapement        = 0;& C2 ?' c+ j7 v3 `) e! P
    LogFont.lfOrientation        = 0;
$ r# A" c/ {" Q' t4 [    LogFont.lfWeight            = FW_BOLD;
, c. H/ ]8 ^8 v* N# n1 i2 X( y( R    LogFont.lfItalic            = FALSE;% Z. I; G. a8 T
    LogFont.lfUnderline            = FALSE;
6 v" J7 Y& ^' t% q" r3 n    LogFont.lfStrikeOut            = FALSE;- N; {6 J+ U& d* z  D; Y
    LogFont.lfCharSet            = DEFAULT_CHARSET;$ Z: X8 s. s+ R- o( V
    LogFont.lfOutPrecision        = OUT_DEFAULT_PRECIS;
8 k  j+ C7 w  `3 H2 ]    LogFont.lfClipPrecision        = CLIP_DEFAULT_PRECIS; , l+ b* C7 j. |, w& o  Z8 {
    LogFont.lfQuality            = DEFAULT_QUALITY;
4 I- e7 u* d# x  R/ k" A8 w! M    LogFont.lfPitchAndFamily    = DEFAULT_PITCH;
& |: c/ }5 r* L    lstrcpy( LogFont.lfFaceName, szFontName );
4 f: C0 \: @( K! t* s    % \/ `( c. j/ |
    _hFont = CreateFontIndirect( &LogFont );
0 |; e9 Y4 T2 N8 H( h% Z    if ( NULL == _hFont )
% Q" t7 r0 E, I5 G& P    {4 T7 z; y$ z* u8 F$ }7 ^
        DeleteDC( _hDc );
2 H3 i  E( ~4 s* ]7 @        return false;
* v: ~$ o0 s& S" e# A6 A    }
4 R; c& P5 n% K2 o7 z   
/ d0 q' v6 s) J1 t8 h(只需要创建一个字体大小的BITMAP即可)4 [$ ?: {* T9 i0 |+ b
    BITMAPINFO bmi;" H% g0 v6 |9 y, P, z( V1 b
    ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
" }$ s6 m  G7 F; @+ [8 ^5 C# {# L    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);9 B* S) h7 ?1 m4 a, A! P
    bmi.bmiHeader.biWidth        = _TextSize;
6 s+ P( P! }$ d1 V! Z6 \, V9 v; y    bmi.bmiHeader.biHeight        = -_TextSize;
& W! o7 `; S4 y3 o+ R    bmi.bmiHeader.biPlanes        = 1;
- A- D3 _& y# ]! Y: l, S, _8 B    bmi.bmiHeader.biBitCount    = 32;" C6 W1 Q7 C- C
    bmi.bmiHeader.biCompression = BI_RGB;
# g3 y8 Q2 k) \# |" P7 _+ R    4 H3 g- Q/ T) ~# O* ~' w  V4 ?
(这里需要定义一个指针指向位图的数据:1 X0 W4 z* }- k8 f0 [+ ~
    DWORD *        _pBits;            // 位图的数据指针)- ?8 H( j. D5 p$ ^% S

. F5 ]4 f7 a3 p/ }    _hBmp = CreateDIBSection( _hDc, &bmi, DIB_RGB_COLORS,
  o& k* I9 L- K' F0 y5 ^        (void **) &_pBits, NULL, 0 );5 Y  a. z& c! w7 s# ~9 W$ b2 p
    if ( NULL == _hBmp || NULL == _pBits )! J! P7 G6 P$ S/ X% T' i
    {8 \$ c3 ?: H5 N* ^& V( y
        DeleteObject( _hFont );$ `2 U5 ?* m. w$ Z- D
        DeleteDC( _hDc );
) ]. ~/ w, O4 K        return false;
4 d/ m' ?0 n" G3 c0 }    }8 _, o5 v; j4 l' v; V6 c- B
    * b3 n$ |$ H5 |  g) E
    // 将hBmp和hFont加入到hDc2 O0 [9 F$ w" j& t  \- @
    SelectObject( _hDc, _hBmp );) [0 b6 S+ g" ^* O6 C' }( `
    SelectObject( _hDc, _hFont );. i) q6 f; }* T& s, ]% F% [

: y. h% I+ M3 \: `$ s- [接着设置背景色和文字色:8 j9 ~9 m3 g+ p5 F! Y+ v3 E
    SetTextColor( _hDc, RGB(255,255,255) );8 D' P& h( v! g* U; P8 D
    SetBkColor( _hDc, 0 );
3 H- G4 {' Q( l4 @& o5 W
& v; {/ U4 k. M设置文字为上对齐:% @" v& g0 o2 M& s
    SetTextAlign( _hDc, TA_TOP );
0 R, G. T+ N; ]0 b' w0 e
& c: _& e/ e: J创建Texture所需要的顶点缓冲:- @& B' m- W% }4 u
    if ( FAILED( _pd3dDevice->CreateVertexBuffer( _Max * 6 * sizeof(FONT2DVERTEX),/ C$ Q# @/ J9 |. J2 J
        D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,
) m: W  W# c; o; i% J1 F# H8 k        D3DPOOL_DEFAULT, &_pVB ) ) )
$ q( K3 f" r4 U9 H/ Y' \. ^    {. d  f8 Q: s' V
        DeleteObject( _hFont );7 W  z0 l9 w, r' z9 k! ~* d  k- g
        DeleteObject( _hBmp );
- g" U9 U' j  t9 Y5 v3 s        DeleteDC( _hDc );
4 b( v* A1 X  {/ b        return false;
  {' D  ~& @/ z( l  j; v. v    }
! {+ ]. J/ ^+ n/ q9 I, X8 h+ F   
- Y7 O3 c; _% p- P, d  T创建Texture! F4 C3 {; c% {$ f% @
    if ( FAILED( _pd3dDevice->CreateTexture( _TextureSize, _TextureSize, 1, 0,
! w9 O7 W( q6 P; D/ O$ |        D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &_pTexture ) ) )* `8 x  A- h! e4 w( i" X1 R2 T7 W
    {
( E0 p# t9 _/ g* U/ l. u        DeleteObject( _hFont );
6 I% R) n4 _/ d+ d) y/ Z        DeleteObject( _hBmp );! {, k* L) i4 l, }3 l! C- N
        DeleteDC( _hDc );7 s- n8 `1 B$ n# I0 V
        SAFE_RELEASE(_pVB);7 y, ]. k# T/ [* f$ o" J& e% |
         return false;0 N0 Y/ [& S1 |
    }9 h! i, m$ B# |( |! e* N
; R: l+ x8 n( A0 b# |. X
设置渲染设备的渲染属性:% ?! a  N% h$ {
    _pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE,   TRUE );
9 I! P: b. S0 f) \6 r+ M    _pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );* P0 B0 u: a( z* \2 d6 Q
    _pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );; a! K8 S& \! k7 V, G% o
    _pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE,        TRUE );
& m2 r4 h: d0 |( Q% |% `4 D1 Y! f    _pd3dDevice->SetRenderState( D3DRS_ALPHAREF,            0x08 );
0 g& c  {: G3 ?: }$ |    _pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC,            D3DCMP_GREATEREQUAL );
, g& a. K- p2 _0 V) ~    _pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,    D3DTOP_MODULATE );! p. W) {0 W( f! W
        
+ f1 O  u2 \4 K  C: ]6 ^7 q    _pd3dDevice->SetTexture( 0, _pTexture );& `. c. ]" F, e8 q+ `
    _pd3dDevice->SetVertexShader( D3DFVF_FONT2DVERTEX );
, c3 S6 `; v  o    _pd3dDevice->SetStreamSource( 0, _pVB, sizeof(FONT2DVERTEX) );
6 z! r& L1 K/ I8 K# `3 `. |: Z5 e0 U6 d! Z% v# S' n
设置缓冲的最大容量
3 H9 X) n# m6 m- T/ A; d    _vBuf.resize( _Max );; U* B% f! _0 R$ n& Q) o

, a& B7 m) v. t# e. E! s. S这样,初始化完成了。接下来是如何把一个汉字写到Texture中,以及如何进行管理。定义函数:5 W# J& V( L* z5 \  s( R
// 得到文字在纹理中的位置3 s6 v# V1 m8 O4 D. R
void CFont::$ @9 x( P3 ]8 i, N; z
/*-------------------------------------------------------------
) V$ P3 u" I) R9 r$ Lchar c1   ---  文字的第1个字节" L1 I/ h% @; F0 L. ~& h; H
char c2   ---  文字的第2个字节2 n' Y* O1 o8 H  q( U4 W3 j
int & tX  ---  写入纹理中的坐标x, ^# ^. Y# A: j( [# ^5 M3 G
int & tY  ---  写入纹理中的坐标y! d. N3 ]% K1 q1 ^1 t3 D7 r* j. n0 F
-------------------------------------------------------------*/
$ {; n: k( N3 v7 AChar2Texture( char c1, char c2, int & tX, int & tY )
1 i' G0 V/ \% V. B7 }5 I3 o$ _{) F0 b, ~& r3 W, H) I# o+ E
    WORD w = MAKEWORD(c1, c2);        // 把此字变为WORD
) g3 m0 O+ Z7 s& r7 d9 \1 ?    vector<Char>::iterator it = find( _vBuf.begin(), _vBuf.end(), w );4 A2 Q' e2 b' ^* Q% V
    if ( it == _vBuf.end() )        // 如果没找到
' t/ V0 J3 M) N# y5 A6 c3 c7 R7 C    {7 i! z( f5 i4 x' E2 t9 F
        it = find( _vBuf.begin(), _vBuf.end(), 0 ); // 查找空闲位置
9 a% a  U6 O2 }% W0 R        if ( it == _vBuf.end() )    // 缓冲已满6 C. {# S" ^  _
        {  U6 q1 Y$ n0 g  \; {
            for(; it!=_vBuf.begin(); it-- )  N; P8 t2 p: s- v7 p
            {
  @: v9 x3 N0 E' I                it->hz = 0;
1 n4 @5 y3 g) D* s" j4 l/ J            }
" y7 x$ `7 d4 E# n8 n, z- _//            Log.Output( "字体缓冲已满, 清空!" );
; v9 V; T" {& U! B+ Q6 V" L        }
# G% _( @" G# ~4 N# |8 v+ p7 t; |9 O: P) {8 K2 y- C
        // 计算当前空闲的Char在缓冲中是第几个
' ?0 U% e: H. P' L1 \) g; \. P( s" S        int at = it-_vBuf.begin();$ {* ?8 l7 n8 C$ ]! `

; U5 o/ A( e4 k0 }5 g/ D$ A: Y  U        // 得到空闲位置的坐标
8 R7 \8 J% g: P( v! Z) v        tX = (at % _RowNum) * _TextSize;9 n# H5 @4 E! F6 W7 ]/ F
        tY = (at / _RowNum) * _TextSize;: |- ~3 g4 E) ]0 ]9 J

8 t: f. {7 m- }4 w        // 设置这个Char为使用中- c+ C0 v* f, P. l* H
        (*it).hz = w;+ f! a0 Z& o3 {8 @" a6 ^  K
6 c4 V( B: N5 i; w; O2 D+ w& |1 d% E& O
        RECT rect = {0, 0, _TextSize, _TextSize};
/ A% `1 X/ |; |. v! h: Z# L        char sz[3] = {c1, c2, '\0'};; u0 @* c$ @9 V+ x
        // 填充背景为黑色(透明色)
$ |. c% {* \" [( T1 A- i        FillRect( _hDc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );! {( e4 v: r5 T( V  s
        // 往hBitmap上写字4 v4 N- O7 I- h  V8 |) E: W  d( J
        ::TextOut( _hDc, 0, 0, sz, c1 & 0x80 ? 2 : 1 );
. x# o. i( a1 i        ; p1 T7 C  l2 u% F( Z( b% d; l0 S/ F
        // 锁定表面, 把汉字写入纹理, 白色的是字(可见), 黑色为背景(透明)
. l( q6 p3 ]% Y2 s0 U$ q" o        D3DLOCKED_RECT d3dlr;
$ d) F+ y# y4 i  _5 V9 P- h" c        _pTexture->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);
0 }$ H! i! Y- b0 H0 R        BYTE * pDstRow = (BYTE*)( (WORD *)d3dlr.pBits + tY * _TextureSize + tX );0 t3 l  k* o; o. c! M/ g
        0 a5 ~( z: G& ^. ?9 w* P
        for (DWORD y=0; y<_TextSize; y++). |- ]9 v- B7 D  F. B+ K
        {* t+ A: ^2 H3 U: j) e* P
            WORD * pDst16 = (WORD*)pDstRow;
# M1 o# U" c( M            for (DWORD x=0; x<_TextSize; x++)/ ^8 Y9 m+ F3 T. d2 t
            {
% o8 @! N5 x/ D* U) B                BYTE bAlpha = (BYTE)((_pBits[_TextSize * y + x] & 0xff) >> 4);
- Q" B; j* I# U& [8 ]( }                if (bAlpha > 0)
, }/ y' A/ \$ W  `) K8 ?, i                    *pDst16++ = (bAlpha << 12) | 0x0fff;
3 [; s/ `/ P6 J                else
- ~% n& S" v5 f3 o2 P1 W# b# E                    *pDst16++ = 0x0000;# D% M" h# W& P0 ]: y
            }  j* K, h& q5 p" s
            pDstRow += d3dlr.Pitch;( c* l& I" K  w+ b% D+ q
        }
3 x9 U0 D/ c4 v        _pTexture->UnlockRect( NULL );
; E+ A  |' v: P5 G. c* Z% f( F    }% V; {, f! w5 E/ D) Q/ [5 q1 i
    else0 E. r- y6 `$ |7 i- }
    {% x: \( W1 F" r+ P6 E1 J
        // 计算当前空闲的Char在缓冲中是第几个% S; ?# j1 C5 n! U. [
        int at = it-_vBuf.begin();
' O4 w0 |2 w* a! J9 ]2 }/ D4 r7 w/ R8 c& c7 P; j4 k  h) f
        // 得到这个字的坐标4 t: z0 f, c: u. }/ n
        tX = (at % _RowNum) * _TextSize;
- ]8 i- {0 v) l        tY = (at / _RowNum) * _TextSize;
$ k: ?9 ?$ m2 M9 N    }
# |  l4 ?8 ]1 V7 `1 ]}
2 R1 J. {! W. {以上代码中的注释已经很清楚了,相信无须我多言。这里唯一需要声明的是:原来所定义的Char结构是这样的; x5 M! R8 R8 U/ {) Q6 O4 }
struct Char{7 J5 K# L5 G. W) y9 r
  char hz[3];   // 保存汉字
, M8 U" t  D% P  int frequency;// 使用频率
5 U' f3 h; r- J7 J: n  RECT rect;    // 这个字对应位图的区域
+ T9 p8 b0 `- W! |- g  Bool isUsing; // 是否使用
7 [5 a; g& a( M6 w( q: Z; a}
3 z2 p" G. Y7 M( f6 V7 m) d- y* u后来因为将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。) F2 a' d, v5 r) A7 n
然后对于RECT rect,因为没有了int frequency,意味着一旦将汉字写入到Texture,其位置就不会变动了。所以,很容易根据find函数操作后的iterator,直接计算出这个汉字所在Texture的位置。这样,RECT rect也不再必须。
; H) V$ ~! I4 F) a- k而bool isUsing,它本身就是个鸡肋,要也可以,这样结构更加清晰。不过,直接通过观察WORD hz为0或非0,即可实现isUsing的作用了。
+ O3 [8 A% d) K+ K为什么要对结构Char这么精雕细琢呢?* j7 S  ^) f; S/ ~, a* |6 _  ^
1.    既然没有必要的东西,就应该删除) Q% n* l0 k+ b! |3 J
2.    Char结构的大小越大,vector所要求的内存越大; j; \; z$ S& D3 j: q3 |! ]
3.    小的结构,find可以更快地查找出所结果
9 m* U. Y# C, @" g2 O' J为什么find会正常工作呢?这里我要大概地讲一下find是如何查找出所需的位置的:它只是简单地使用while从vector的begin一直遍历到end,逐个判断,直到找到为止。find要求必须实现自己的operator ==(),进一步跟踪到find的源码中,发现也是这样。于是前面的结构Char变成了现在这样:5 b! \" {/ A5 U5 S% |* u: f) z3 @
    struct Char{
3 V* j& z0 d2 _. `8 U  R% Q        WORD    hz;                // 文字
0 O  u' ~3 U9 e6 m3 K) g- p* p- q: G0 T! M! i. l! I8 T' \
        Char() : hz(0) {}
+ z  j/ V- S# ?& `) L; r2 r5 `& p0 [" {
        // 用作查找文字7 w4 ~$ i7 `! G9 \, t8 R7 q
        inline bool operator == ( WORD h ) const0 S1 Z. j8 e8 k- L$ \
        {
/ n! X+ C# ^8 n            return hz==h ? true : false;
" [% Z, p- v: \0 D3 f3 f; J+ j! p2 j        }
9 X( M7 d* O0 c- X' p% _    };" k* `$ w9 `1 J/ y
是不是很简单?^___^4 @7 N* q* [$ |& O2 p# @
! f7 ]4 Y4 u# `0 \7 l5 \4 F
终于到了显示的函数了:! `9 M0 L1 j  ~; O. x% \- t0 o6 E
// 得到文字在纹理中的位置5 G' \  I/ Z- m
bool CFont::
, A/ M& L- {2 R. L/ q; ]  t/*-------------------------------------------------------------- ~5 l5 [% M' a8 x8 {
char szText[]  ---  显示的字符串
% E, O/ i# U% K& p$ yint x           ---  屏幕坐标x
0 w$ i9 P2 k- Y5 bint y           ---  屏幕坐标y
; j- c$ F2 j8 X2 G$ N% RD3DCOLOR       ---  颜色及alpha值
% g( B: c( `& h( E; b& j& K$ v* zint nLen       ---  字符串长度. H0 a" c2 ~! j8 K
float fScale   ---  放大比例
3 U; G1 i, B8 A5 ^# J; X( o-------------------------------------------------------------*/. E, W" h& n; B. y
TextOut( char szText[], int x, int y, D3DCOLOR color, int nLen, float fScale )1 [- c; w; j  q5 q7 N) W
{
4 ?! H$ U4 ~5 `' ^1 R; D* u    Assert( szText!=NULL );
$ b6 N0 y3 z5 Q( Z
' ~" H( y- |' ?2 Y9 n9 a% A) L0 L) A    float sx = x, sy = y,
, E0 G) [7 y( s; Y% `  `          offset=0, w=0, h=0, tx1=0, ty1=0, tx2=0, ty2=0;
5 U0 U7 z& O  v9 i8 W6 o    w = h = (float)_TextSize * fScale;  b5 s% |  {! O+ _+ [. f: U

$ r  T8 ?/ W* ]. J$ V9 r/ P    char ch[3] = {0,0,0};- C; W, B" z* {  g7 h
    FONT2DVERTEX * pVertices = NULL;# d: C9 m9 l" n) L
    UINT wNumTriangles = 0;; x1 b" {  g$ h; t3 G8 p% o, z
    _pVB->Lock(0, 0, (BYTE**)&pVertices, D3DLOCK_DISCARD);( k' ~8 u! M  r2 O7 X3 z

# O4 b$ J% M! K5 X3 p7 p' \& g0 S    if ( -1 == nLen ||                // 默认值-1
& r$ z+ L0 L1 v* V, O8 s, w8 ~% _         nLen > lstrlen( szText ) ) // 如果nLen大于字符串实际长度, 则nLen=实际长度, k) y' N" Q& X, ~* z" y! H
        nLen = lstrlen( szText );
- }6 }% y5 s  {% m4 R2 T9 e    for (int n=0; n<nLen; n++ )8 i0 v1 a7 P  L& C
    {1 s8 l: {. \8 R! z9 }
        ch[0] = szText[n];. [  ~+ o' f1 X* r8 P9 y
6 @; g! {6 i3 F) Z$ B; d9 k
        if ( ch[0]=='\n' )
) E' d; m& W% E& r9 H        {
8 e, e. [, P% @) N1 x            sy+=h;5 N9 w* V* {5 E) [1 h. j
            sx=x;
6 P) f; H! Q1 y  G+ I, Z            continue;  L4 Q$ X2 f. F1 c. L* _
        }
. y6 F3 k& t( K6 ^: k1 C- o2 G  j+ H. I
        if ( ch[0] & 0x80 )
" @/ D# R7 O' [        {
/ s" Y5 C3 R: Q+ {- Z" v            n++;" I) E5 D# d& `% G$ G. S
            ch[1] = szText[n];: p. z# V8 g6 M2 x
            offset = w;
7 {- W- Z! F3 F: v0 S& L        }  M6 j! K8 d2 H% F0 R3 E4 t% v" B
        else1 M. }- S+ i; c% D
        {. b: b3 n- @3 ]! z! E/ P9 N& ]
            ch[1] = '\0';! I% G/ d) \& z+ E
            offset = w / 2 ;  x9 Q6 n3 W9 N/ A$ `* g
        }/ ?1 K. r. g! t; ]+ g- n7 p

% P$ V5 k/ v1 ^. G6 P- V        int a, b;
) z+ o- v2 `9 ?+ Z( d5 ]* B0 w  k+ D        Char2Texture( ch[0], ch[1], a, b );
9 U6 T9 P- \- Z0 y; I    ! h  W, l6 K2 l2 q
        // 计算纹理左上角 0.0-1.0
* i  @1 C6 z  X, @. `/ u1 V        tx1 = (float)(a) / _TextureSize;
( y8 q# ^' O7 |0 @        ty1 = (float)(b) / _TextureSize;, G% [" x. N" s: o( K  H
        // 计算纹理右上角 0.0-1.00 P& C4 d5 _" w  Y* O& q/ {2 V: v
        tx2 = tx1 + (float)_TextSize / _TextureSize;
& P) W0 u$ T: _3 D5 I+ W        ty2 = ty1 + (float)_TextSize / _TextureSize;
1 R# Z) Y; |( S2 A5 d. p; X; m; F$ h2 U" b0 W
        // 填充顶点缓冲区
. j7 Q3 a/ L# Z; s+ r/ w% A7 _        *pVertices++ = FONT2DVERTEX(sx,        sy + h, 0.9f, color, tx1, ty2);
1 I  ]: L' h8 o( u2 Z; M! ]- u        *pVertices++ = FONT2DVERTEX(sx,        sy,        0.9f, color, tx1, ty1);+ F# l. z! J  P- U' S1 |8 E9 r
        *pVertices++ = FONT2DVERTEX(sx + w, sy + h, 0.9f, color, tx2, ty2);8 C! ]6 U' Q4 B7 Y: h  N- s9 T
        *pVertices++ = FONT2DVERTEX(sx + w, sy,        0.9f, color, tx2, ty1);
7 B1 r, G( s' S        *pVertices++ = FONT2DVERTEX(sx + w, sy + h,    0.9f, color, tx2, ty2);3 r1 t# r9 }8 j2 v3 i# ^2 H( A' y
        *pVertices++ = FONT2DVERTEX(sx,        sy,        0.9f, color, tx1, ty1);0 _" j' P6 N  a) ~: ]2 ~
6 ]; {5 z- S: t
        wNumTriangles+=2;
0 I! }9 p. Z# j( w) M* j* d0 @- B' t) g  ]# I) N& ?: C
        sx+=offset;    // 坐标x增量
) G  q! j. `, X! s9 I( D    }
3 \6 @$ B) I% x    _pVB->Unlock();
  `7 g1 T6 H0 A5 ], A    _pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, wNumTriangles );
! Y: |; [* @% P( k( z+ O% W9 B" q
    return true;* K, j% R/ \% Q# y$ p! k/ o) F! I
}
' X2 e6 }6 g! t$ L, H4 G5 |结束语
  T+ ~5 ?$ Q% k" V- ?) W( I记得有一句名言: Keep it simple and stupid.在实现功能的同时,保持代码简单、清晰是非常重要的一件事。相信在往后的日子里,在不论是别人阅读或是你自己回顾的时候,你都会发现一如既往地遵守这个守则,是多么得重要!- a" f, T: e- f5 F. l4 I
相信通过上面我那无数的废话,加上代码中还算足够的注释,聪明的你一定能够明白这其中的原理了吧。如果以上的内容还不足以让你完全搞清楚的话,你可以登录我的主页:
1 m/ I5 s4 z$ h* ^. U炎龙工作室9 Y8 t2 m. w# u  I
上面不仅包括了上面所写的程序代码,还有一个用来演示效果的一个很简单的demo。
; S$ E3 G/ o8 O( {说明,以上所实现的CFont是包含在我的游戏引擎中的一个部件,而目前已经实现的部件包括有:
# l- L! a6 r. f* H( ?3 L1.    CGameFrame(游戏框架类)  -----  封装了窗口及D3D设备的建立,需要派生出自己的子类
, |8 `' B) n1 p* j( \2.    CAudio和CSound(声音类) -----  支持wav/mid/mp3的播放
) Q# C: _+ I7 Q1 x1 B3.    CDirectInput(控制类)    -----  键盘、鼠标操作
9 [, g: f; w; U. {/ X3 [5 S% X4.    CDirectShow(视频类)     -----  支持avi/mpg/mov等的播放0 L: L9 K0 C8 h" z7 R
5.    CSpriteX(精灵类)        -----  方便游戏中对精灵的控制
* z0 D9 w/ @, |$ J  x8 [+ S6.    CFont(字体类)           -----  中英文字体的显示* m4 b- r$ p* z
7.    CTimer(时间类)          -----  高精度时间的控制2 s, ~; u3 [0 c: _6 {! c
8.    FPS(fps 类)             -----  fps的计算
, U% b" R+ _  H/ [4 ?  G+ T9.    LOG(日志类)             -----  游戏中的错误反应以及状态记录
) F7 V! N$ \2 W# Q( ~最重要的是,这个Game Engine完全是开放源代码的。关于更新的情况、版本说明以及源码下载,请随时关注我的主页!
: [8 g5 O! r+ [6 s8 L# h* `6 B5 A接下来,我将会继续完善这个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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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