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

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

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

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

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

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

讲了一些TTF的知识,也许对汉化有用5 A8 k$ d# G' F' V. h
' `  \2 V/ o1 r6 R  a# Y+ L! W4 x
: ?* k& d" w1 U# v9 o* p
游戏中汉字显示的实现与技巧
& [0 C& V/ M" S* i2 c2 i- W# a作者:炎龙工作室 千里马肝" A) t' S& c# z9 Q
版本:v1.0
0 _- E3 }1 D2 G4 a+ p( D2 M0 `" M( Q. t最后更新日期:2002-3-30( x  o' S1 v0 ~6 C9 [
绪言( Z4 o: c7 M* a: }: Y
在游戏中,因为我们是中国人麻,通常都需要显示汉字,比方说交待剧情。而对于文字的显示,英文的显示要较其简单得多,因为只有26个字母,就算再加一些标点、符号什么的,用一张位图,就可以足以显示所有的单词了,而相关实现技巧,也比较轻松。
8 Q# R( l" ^: n4 c5 ?而中文的显示方法,要复杂得许多。记得原来在DOS下,汉字的显示都是读的UCDOS的点阵字库,而点阵字库的读取方法,在UCDOS SDK中都有源代码可以参考。但是自从Windows操作系统开始,我们开始了解到一种更好的字库,它就是TTF。$ U# y. J, k- n9 ]1 |# \9 }
注:以下我所指的开发环境,除非明确说明,默认的平台是VC6.0+DirectX8.1,使用D3D来加速2D。然后使用的STL是用的SGI实现的那一套STL。  Y. z0 Y2 {- r9 L. V2 d( c
点阵字库
; S4 D$ V: S, |. U4 a包括现在,有很多游戏都还是使用的点阵字库。因为操作起来比较方便,加上这方面的经验已经积累了好几年了。通常如果只是一种字体就可以满足需要的话,它会是一个比较好、快的解决办法。但是它有3个缺点:
% F$ j. Y1 |3 H  ?% b/ ~+ {; h/ U3 a1.    如果放大显示,不做处理的话,显示出来的汉字,是很难看的。' {3 c7 [# C. T
2.    像是UCDOS所提供的点阵字库,只有24点阵的有几种字体,如:宋体、黑体、揩体…,而16点阵的好象就只有宋体一种。5 E( X. C1 `' B
3.    点阵字库,通常是有版权的,尤其是第三方制作的汉字库(如:方正)。. N' ]; k: y7 Q# F, c+ k" A
在这样的情况下,当我们写好这样的一个显示函数,就算是解决了如:放大、快速显示等问题的话,可供选择的字体还是太过于局限了。所以,在字体的要求比较强的情况下,点阵字库并不是一个好的解决方法,他不够灵活。尽管我们对于它的操作是如此得熟练,可以写出优美的代码来展示我们的编程技巧。
9 P# T0 j& J$ {+ y# DTTF! P, z$ r% |' X5 B6 P& x
TTF是True Type Font的简称。在Windows\Fonts目录下面,我们可以看到许多后缀为ttf的文件,它就是接下来我们接下来所要谈到的。
* q2 D; [# e6 J) e, S. MTTF是一种矢量字库。我们经常可以听到矢量这个词,像是FLASH中的矢量图形,在100*100分辨率下制作的flash,就算它放大为全屏,显示出的画面也不会出现马赛克。所谓矢量,其实说白了就是用点和线来描述图形,这样,在图形需要放大的时候,只要把所有这个图形的点和线放大相应的倍数就可以了。而且,在网站上有很多的TTF字库可以下载,或者你可以去买一些专门的字库光盘。然后在你发行你精心制作的游戏时,可以顺便捎上这些后缀为.ttf的文件就行了。包括Quake这样的惊世之作,也都是用的TTF字库。; ^$ [- H! }# I; x, P3 t0 Z  w7 f
这样,我们就可以解决点阵汉字的一些问题。通过TTF,我们在字体的质量和字库的数量上获得了暂时性的胜利。
% W3 u2 Q' m$ A6 j& Q! Z字库的读取和显示
# B! h  _- h. x& h先前谈到点阵字库,只需要很简单的一些操作,就可以显示出想要的汉字。下面我给出一个读取hzk16的函数,它需要一个Surface以供显示用:4 k# \% o1 X, p
#include <io.h>: b2 i# c/ O; Y: K; M
#include <stdio.h>
( Z  ~. t$ `+ [8 O- I7 K#include <conio.h>
2 ]" t# r: c' k: H! x% V( Q* i1 ?; W, E! \
// 读取16x163 X8 G0 L4 X' D3 A. T( S& l: |
void DispHZ16(int x, int y, BYTE *Str, LPDIRECTDRAWSURFACE surf)6 N$ C6 Q* U, Y7 ~( ~+ Q
{
# J5 K* x: C5 b$ b/ x7 _    const int Mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };8 Z( [* @7 m# l+ M1 C
    FILE *HzkFp;! N+ w# Z: t& z( J3 a" _% w
    WORD i, j, k=0, m;
2 k$ {1 x; f0 n) t4 h    WORD HzNum;0 C/ {# v8 B5 i" `* g# A5 d& B+ X1 r
    WORD QuHao;+ @, \; E& h* l1 k$ P6 b1 I
    WORD WeiHao;
% x: @1 I: ~/ M' R( {    long offset;
6 g. _$ [& e& q' v' @7 W! G    BYTE dotBuffer[32];    ; l3 S, R" @, q3 D

9 S6 D9 `# k. I    HzkFp = fopen("HZK16", "rb");
7 r, o$ O7 X. C5 q0 v4 |8 i. l/ F   
  `1 ^! `+ r& r5 g    HzNum = strlen((const char *)Str)/2;
1 a9 h5 Q8 H) u& l/ U8 i* J) B3 U1 A9 q
    DDSURFACEDESC       ddsd;
) E6 h1 e8 {( s    LPWORD              lpSurface;
4 ?( i  v8 Q- Y/ l' |    HRESULT             ddrval;. ^0 f" l0 s$ J6 a
    & N, B- Y% N; Y
    ddsd.dwSize = sizeof(ddsd);5 A  v( g2 Q5 P* y" t6 W
   
6 B0 m0 S3 Y6 m0 f' J0 ?; N1 a2 [2 `    while((ddrval=surf->Lock(NULL, &ddsd, 0, NULL))==DDERR_WASSTILLDRAWING);- `7 r5 N/ f; e6 ]" ]# y" o
    if(ddrval == DD_OK)8 [4 u- D  N$ d. E: M0 q
        lpSurface = (LPWORD)ddsd.lpSurface;
9 U$ ?2 V3 z1 I7 }' F( F- f' t% u. I: l/ V
    for(i = 0; i<HzNum; i++)
9 _& R* w" h. v# y% u    {   
8 {3 i0 E9 m4 j, \1 Q        QuHao = Str[i*2]-160;
4 Y3 Z* j$ V& W( w8 |0 r5 Z; H/ @        WeiHao = Str[i*2+1]-160;5 a0 j+ d6 W$ s( n) S
: Q9 l6 u& E! V; X0 U
        offset = ((QuHao - 1) * 94 + (WeiHao-1))*32;8 ~" p5 @) F3 Q" Z7 d6 \

* }  j6 A; D1 {3 `" B* t' ~/ o        fseek(HzkFp, offset, SEEK_SET);9 B* s2 E+ }/ F7 r' w- N
        fread(dotBuffer, 32, 1, HzkFp);
8 r3 H. M0 E# \* [% D( I+ C
6 u* Q+ y% f( E+ Y" |4 `        for(j=0;j<16;j++) - z+ D% _  N/ J! ^. p9 |+ i
            for(k=0;k<2;k++) - U: T+ K# N+ ?) o9 s1 a
                for(m=0;m<8;m++) : O7 W2 N- N$ ?3 @( \2 c  K: {* w
                    if(dotBuffer[j*2+k] & Mask[m])8 Z- w8 x2 G8 v. v, `3 X+ Q* Z
                    {
, F  k5 G2 p1 l( o$ p                        lpSurface[ddsd.lPitch*(y+j+1) + x+k*8+m] = 0x000000;( Y8 {6 |2 Z2 \0 I: e$ T. V
                    }1 S( T! I' E, B2 T  N4 M8 Z
        x+=16;  I' G" h2 G, Q& \4 H$ v
    }
5 E* a% G/ S' w; n# O1 N) v: }9 t4 s6 s/ G
    surf->Unlock(NULL);
+ S1 q- L  f- w- D& U5 f  f, e# ]" V# V# ?; m4 o! F
    fclose(HzkFp);
) E" R" c0 J% W4 `9 ]}
+ i& m9 f' E7 `. r其实原理很简单:
+ o! y' O2 I$ p3 E1.    打开字库9 i$ Z$ L: {- p, Y2 R9 M3 l: ^
2.    计算字符串长度(这个函数只支持中文),并且Lock Surface
/ d$ m  [4 ]# P3.    依次计算出每个汉字所对应的区码和位码(汉字的第1个字节是区码,第2个字节就是位码),然后通过公式计算出这个汉字在字库中的偏移量:. K) V  q: ~+ W4 \
offset = ((QuHao - 1) * 94 + (WeiHao-1))*32;
/ i/ W% n0 p: s2 Q; N6 |4.    读出一个32个字节的点阵6 m) L4 t$ X( v* {$ w  C
5.    绘制到Surface上
6 m6 }7 ~1 n# J- q以上只是16*16点阵字库的显示方法,24*24的读取方法与之类似,大家可以参照相关资料来书写出自己的代码。6 D3 Y( s! ]  K2 h

; l; R  c0 ]9 m4 {4 v1 h1 u, H如何显示TTF字库呢,有很多种手段,下面我按从简单到复杂的的顺序依次介绍:2 u9 x7 g. ~* j$ F4 W
1.    使用Windows API,也就是大家所熟悉的TextOut。通过它,还需要一个HDC(设备句柄),我们就可以随意地在屏幕任何地方显示出文字了。( m. \( ], c% g3 }2 f# y9 O% ^
2.    在http://www.freetype.org,有一个FreeType的免费库,而且是OpenSource的。它目前有2个版本:1.0和2.0。其区别在于,1.0只能读取TTF格式的,而2.0支持更多的文件格式,在使用它之前请详细阅读所要遵循的Licence,以下是摘自FreeType2.0对字库的支持列表:
. G5 z9 [" @8 j. N8 R: c; u# Fo    TrueType fonts (and collections)
) Y7 V. {& M& To    Type 1 fonts
7 u  `* [# P' R) f; Y7 \o    CID-keyed Type 1 fonts , T, H$ ]+ {9 V. c2 M
o    CFF fonts
7 _7 d; O9 E" N8 }( Mo    OpenType fonts (both TrueType and CFF variants) 6 }) s/ D) U% W; w0 Q
o    SFNT-based bitmap fonts
! B! U* R! Z6 P2 Z, r5 Po    X11 PCF fonts
  [4 c' m4 |( u  W( ro    Windows FNT fonts
0 I( z% ^3 r8 f3.    自己研究TTF的格式,然后自己来操作。
5 k; Z, l5 a; W/ o+ M. q& B! }' Q' ]! u2 V* [9 Z, k
....... ╮╮
% x1 A! c9 j6 \3 f5 E( q8 E      \█/倒! - o, d3 c" u7 l) H$ f
      ●
7 F4 o# m2 |: {1 H. ~虽然我们想要把每一件事情都做好,但是也不是每一件事情都要亲历亲为。如果你非要这样,也行^____^,但是过不了多久,你就会陷入泥沼,到时候你会发现自己的热情正在慢慢被磨灭,什么叫做抓狂,相信你很快就会知道^_^。  M8 E" `: A- D9 k2 ~
  {: ^* Q9 \3 }+ M% y
在有多种选择可以取舍的情况下,我们需要考虑一下,对比一下各种解决方法的优劣。1 _5 e  `3 w8 W+ f4 `
" E$ ~5 q& v9 L( u! }% C6 H1 H. S4 W
在DirectDraw时代,我们都不自觉地喜欢上了GetDC,因为……多方便啊。可是现在已经到了DirectX8.1时代了(我要使劲地摇那些还沉醉于DirectX7中,为如何在使用alpha时提升那可怜的1、2个fps的朋友们:醒醒,该起床了!),HDC已经被M$列为禁用品。怎么办呢?是的,你可能已经想到了,我们还一直保存着窗口的hWnd呢,可以通过它来得到hdc,从而调用那些需要hdc的API,可是,这样做是更为愚蠢的,这样对你是没有一点好处的,不信,你就试试吧。有一句话,请牢记:要想你的游戏有更快的速度的话,请不要再去碰HDC了。
* `$ ]1 b* G) `7 W- \8 w. V我们非常清楚hdc是一个超慢的解决办法,它无法在我们的高速游戏中满60分及格。下面来看看FreeType,它更像是一个Service。它的解决方法是,先通过一系列的初始化和设置,告诉FreeType字体的名字和大小等,然后它会动态地申请一个Graphic,再把我们要显示的字画到这个Graphic上,你还可以把它保存为tga格式。不过我们最终所想要的不是这个,所以可能我们还需要从这个Graphic上逐点读取或者用CopyRect,然后再画到我们的画面上。其实它已经是很方便的了,可是需要你去学习如何配置和使用它,这是很花时间的一件事情,而且它最大的优点是可以跨平台,我们需要它吗?如果有一个更为简单的办法,像是如果Textout不是那么慢的话,就好了……8 B- g: f4 o: h: j% T
在这里,顺便谈一下另2个字体显示类:ID3DXFont和CD3DFONT。可能早就有人会说怎么在上面的列表中没有它们?原因我会在下面慢慢地说明:
* t9 y- f. l2 u' iID3DXFont,它存在于D3DX库中,一个现成的字体类,不过对于它的处理方法……我实在不敢恭维,就引用一位大师所说的话来表达我的看法吧: 在内部实现中, ID3DXFont::DrawText()函数确实做了我上面讨论的工作,先建立一张GDI兼容的位图,把文本绘制到位图上,而后把位图拷贝到纹理贴图上去,最后把纹理渲染到屏幕上。这样你就聚齐了所有的龟速的原始GDI函数,还包括了一大堆的额外开销 — 最终,这个函数比原来GDI的DrawTextEx()函数要慢上超过六倍……
, w1 s9 E* k, |1 L  K' ^CD3DFONT,是由M$在D3D的框架代码中提供。不过它只能显示英文,有很多朋友通过自己定制和修改这个类,来实现自己的中文显示。不过效果都不是很好。其实原理,跟ID3DXFont的方法差不多,不过处理方法要聪明了一点。8 @, o5 h; Y. H1 m* ^  E
分析与思考, }) Q' B( z4 X6 W) {2 m, x6 G
那么我们应该怎么办呢?通常我们会幻想,如果可以像处理英文那样,把所有的汉字都保存在一张位图里,该有多好。这样,显示的速度就不是问题了,直接可以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。# g& R5 l. X1 I( k7 e5 m
汉字虽然很多,但是常用的汉字,其实也就只有那么几百个。像这样的字:鬯、鞴,你一辈子会看到多少次呢?如果可以做一个类似于Cache的东西,保存着常用的那些个汉字,在需要显示的的时候,先在Cache中查找,如果有的话,就马上画上去;如果没有,就从字库中提取到Cache中。这样的话,在使用Texture来保存汉字的位图信息的同时,对于每个汉字,我们还要定义一个结构,然后用一个东西把它串起来,综合它们2个,也就实现了我们所要的Cache了。刚开始,我所定义的结构是这样的:
1 C: k; y+ z5 C( _# k+ qstruct Char{" l" `3 J7 E; K% w: s. }3 G
  char hz[3];   // 保存汉字
' G4 n, B9 W6 A$ e  X% E  int frequency;// 使用频率: a3 j' e  W5 r: D7 O
  RECT rect;    // 这个字对应位图的区域
- |7 e; {& u/ k4 c  Bool isUsing; // 是否使用
8 u; t5 Q- j. t9 f, X. q/ o( i}8 q3 D! h# t- e3 E" K
对于汉字和英文,我在这里大概地讲一下原理:汉字是由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, q+ Z- I' v% V$ N. ~" p8 {
接下来,对于使用char[3]来保存汉字,是否真的很合适呢?因为如果把它当作一个字符串来看的话,在查找时就需要使用 strcmp 来比较字符串了,这样一定是会影响速度的。如果不把它看作字符串(字符串的最后一个字节需要以’\0’结尾),只用char[2]的话,我们可以只是简单地调用宏MAKEWORD,把2个byte压成1个WORD。当把文字作为一个WORD来看的时候,这样查找比较时可以用WORD内建的==操作,这样要比调用strcmp函数要快得多。. ~; Z" f7 F4 O9 g) x" j- G. e5 M
int frequency用来标志每个WORD的使用频率。设想,如果一个字已经存在于Cache中,以后每对它调用一次,就让frequency++。这样做还有一个用意是,是否可以在一个合适的时候,以frequency为参照来对这整个Cache排个序,把常用的字放在前面。那么在显示时,可以先在Cache中查找所要显示的字是否已经存在于Cache中,如果有则直接显示,没有的话才需要采取某种手段将字加入到Cache中。一些常用的字(像:我、的、着、了、过……),使得显示的速度将会大大提高。& R& C) \, R4 ^# r6 b5 j! v
其实上面说了半天的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上的区域位置。
  Q5 E( x- P" Q$ m8 Z( C0 w# v使用什么东西来把这n个Char串起来呢,一般会想到的是链表,原因无非有2个:1 随时有新的字加进来,而内存是不连续的 2 它几乎没有容量的限制(除非是内存用完了)。不过链表的访问速度是很慢的,如果使用像数组这样的东西就好了。仔细想想,在这里,我们用来存储的Cache,最大也就是256*256(理由上面说了),所以大小应该会是固定的。我们只需要在数组中的给每一个汉字加上一个标志,说明这个位置的使用情况。那么就使用数组吧,这样的话,访问的速度要更快一些,直接首地址+偏移量就够了,不必像链表,在查找时需要逐node访问。当然,我绝不会想到用new  Char来申请这个数组。因为这样做实在没有必要,请不要过于迷信自己的能力,在STL中已经有vector了,为什么还要自己写呢?^_^最后的一个bool成员变量isUsing,也就是上面所说,用来标志使用情况的。! g2 L6 X5 v/ l3 _+ ]
实际的操作+ m8 H& r3 G: g
上面考虑了那么多,我认为都是实际操作之前所应该有的。先谈谈如何显示吧,因为在DirectX8.1中已经将DirectDraw和Direct3D融合为DirectGraphics了。所以无法像原来那样了…………哦,实在有太多东西要讲了,我还是推荐几篇文章给你吧^_^:, l0 t, z* w5 P# i" `( E) m/ b5 ]5 ~
http://vip.6to23.com/mays/develop/directx/200201/Geczy3Din2D.htm8 `5 `$ r3 b" k. }. c' l' f: X
http://vip.6to23.com/mays/develop/directx/200201/GESurface.htm9 ?6 |1 o7 Y5 {  Z& v; X
http://vip.6to23.com/mays/develop/directx/200112/2DGtoDX8.htm* k  z) U: }& c4 Y! L
http://vip.6to23.com/mays/develop/directx/200201/DX8adv2D.htm* |+ ^3 }- s8 S
接下来,我会假设你已经具备了在DirectX8.1中绘图的基本概念了,所以在你继续往下阅读之前,请务必先仔细阅读以上推荐的文章。: I( x: {  Y" m# S0 D1 {8 R6 Q
前面提到,需要一个vector来对应Texture上各个位置文字的信息,上面已经创建了一个结构Char,则这个vector的定义为:; @/ ]3 B' x# W2 C) |; [. c
    vector <Char> _vBuf;                    // 记录缓冲中现有的文字情况3 M$ q, s. `, r" a
首先,由于可以利用硬件的放大缩小机能,所以字体的大小精度要求不是很高,只需要支持16*16和24*24大小的字体就可以了。我们需要一个这样的初始化函数:+ {# d* @9 C0 r' [' Z
bool CFont:: + Y4 I+ h* l7 D" Y/ q# S( ]4 A
/*-------------------------------------------------------------, T. ?  [- q' D: E5 n. a+ J+ o
LPDIRECT3DDEVICE8 pd3dDevice  ---  D3DDevice设备
- S  Y! Q/ `* P3 ochar szFontName[]              ---  字体名(如: 宋体)
3 K4 Y& ]' L; O4 K$ V, Y9 yint nSize                      ---  字体大小, 只支持16和24+ j0 I/ @$ H# j, Z* u. C0 ]
int nLevel                      ---  纹理的大小级别
1 H/ Q& g$ [" [-------------------------------------------------------------*/
; r7 A6 Y/ n* P) [& Y. KInit( LPDIRECT3DDEVICE8 pd3dDevice, char szFontName[], int nSize, int nLevel )。$ y; }! t6 F! C' h3 ~% [8 G

$ Q3 p0 v/ \2 x( W8 ?" q在DirectX8.1中,由SetTexture(…)所贴的图的大小,也就是Texture的大小,是有大小限制的,长和宽都必须是2^n,而且位图越大,所花费的显存越大,这样留给其他显示用的显存就少了。所以,必须根据需求的不同,来自定Texture(也就是Cache)的大小。因为汉字点阵大小的原因,所以从实用角度而言(比方说只是显示fps或是短小的标题),开辟一个64*64大小的Texture,才能满足最低情况下的需要(这时如果选择16点阵的话可以存放16个汉字,24点阵可以存放7个,依次类推……)。
6 l! e) J6 z5 o+ h/ e! q4 f根据设置,创建Texture:
$ }$ r, O" [$ K5 I$ _0 @    _TextureSize = 32 << nLevel;        // 纹理大小! }. R* o* Z& A0 U
    _TextSize     = nSize;                // 文字大小
% Z' ~, h& p) l: }7 @% H5 m+ e. y' ?$ L    _TextureSize = 32 << nLevel;        // 纹理大小
/ V$ G" a6 e1 S" b9 G    ) K$ s+ X/ @" ]# g- F% E* x
    _RowNum = _TextureSize / _TextSize;    // 计算一行可以容纳多少个文字
( A: w+ Z, J0 q4 a9 \  Z6 v    _Max = _RowNum * _RowNum;            // 计算缓冲最大值
, ]5 l$ g0 C- _+ z; z: ?& D- Z6 `6 Q9 q
创建字体,还是需要使用Win32 API。也就是先创建一个HDC:
# ^+ x' s$ k4 q, g: L5 |2 y/ G    _hDc = CreateCompatibleDC(NULL);
4 t: j( m2 i' X! q( H# v) v& }+ E5 Z6 z3 Q* ?) U
然后创建一个BITMAP和一个FONT,将它们与HDC关联起来。# n$ @2 f4 t. f% R4 y  u
    LOGFONT LogFont;
( k% v% ^6 F% p    ZeroMemory( &LogFont, sizeof(LogFont) );
2 I% f# G, c! X# ~! b( l' k    LogFont.lfHeight            = -_TextSize;
6 j0 i' P4 P6 F4 H+ O3 L3 V    LogFont.lfWidth                = 0;
/ x% u% {2 {/ T: b) {    LogFont.lfEscapement        = 0;2 ]$ S7 a' _' c& e, A
    LogFont.lfOrientation        = 0;
" s( M6 j: M1 z+ @% @1 F2 }    LogFont.lfWeight            = FW_BOLD;
7 K/ C& C6 U. p! r9 d  `    LogFont.lfItalic            = FALSE;
4 G. k4 a. `" k  E6 O6 x    LogFont.lfUnderline            = FALSE;
# l; m. O$ H4 e+ l7 ?6 L# |0 c    LogFont.lfStrikeOut            = FALSE;
" I3 c  h0 Z2 w+ l2 ~2 N) \$ y    LogFont.lfCharSet            = DEFAULT_CHARSET;
9 Y/ j$ T3 n6 X% ]    LogFont.lfOutPrecision        = OUT_DEFAULT_PRECIS; . T; n8 {5 A8 ?& U
    LogFont.lfClipPrecision        = CLIP_DEFAULT_PRECIS;
9 z) d+ ]- E9 J) J* e# {6 ?    LogFont.lfQuality            = DEFAULT_QUALITY;+ o+ X9 o# h8 M9 F
    LogFont.lfPitchAndFamily    = DEFAULT_PITCH;. \% L4 b" J, N( C
    lstrcpy( LogFont.lfFaceName, szFontName );; Z& d! \5 ]8 u, |
   
+ x# {. g" N3 ~# t$ z3 p9 S    _hFont = CreateFontIndirect( &LogFont );4 z+ V* L9 R9 ~" j- A: m. I
    if ( NULL == _hFont )1 B6 r# o) G7 q( a+ q7 i
    {$ _/ K  q5 j; c7 f2 T
        DeleteDC( _hDc );/ Z' G8 p" ^# |4 V9 F) V
        return false;; ]3 @) O8 U/ q! I/ F/ v
    }! Q) J( T) N5 B+ W
   
% e- j. V- i7 W/ I# g8 h/ T(只需要创建一个字体大小的BITMAP即可): _. n! j; n4 `: S2 s# k
    BITMAPINFO bmi;
7 |  E) P$ }, h5 {" f1 `0 H' [' r    ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
0 G/ [: M! A* r( f# ?( h    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);( ]" \* }% i7 m4 M4 `8 e
    bmi.bmiHeader.biWidth        = _TextSize;
% q$ p7 V: d4 k7 H/ d) I9 C  u4 p    bmi.bmiHeader.biHeight        = -_TextSize;
1 Z3 ~1 Z5 _. U: M0 |% S    bmi.bmiHeader.biPlanes        = 1;# x# {9 _# `" M/ e) z1 h+ P4 \
    bmi.bmiHeader.biBitCount    = 32;
9 J+ E! }( H! p) Y: C( a    bmi.bmiHeader.biCompression = BI_RGB;0 N/ d; W- @* w5 v+ t+ G0 r
    7 g$ ^7 c  C& W/ A2 e
(这里需要定义一个指针指向位图的数据:
0 ~( ~, i$ R; D- L) J    DWORD *        _pBits;            // 位图的数据指针)/ B( T1 t3 I: Y$ y9 [

0 }% z  G  R5 G6 g+ j, G    _hBmp = CreateDIBSection( _hDc, &bmi, DIB_RGB_COLORS,
9 z8 C) B. i; g1 P* a( h- U  P7 a        (void **) &_pBits, NULL, 0 );
  ~- R8 k# K5 G  W% h8 S    if ( NULL == _hBmp || NULL == _pBits )) N" H7 o2 Z% c
    {
) |# r+ K# N! o0 |  [( Z( H        DeleteObject( _hFont );
# s3 p% u" i) K& ?( p2 _4 w1 b        DeleteDC( _hDc );5 q6 t# r7 |' z8 k" W
        return false;$ A/ F. o# U% `* l& s
    }* ?" U% F/ e( V' u5 k
    3 D. i# d: r/ x8 s; s& T
    // 将hBmp和hFont加入到hDc% u4 x( P$ ]. [+ u
    SelectObject( _hDc, _hBmp );0 v8 ]8 n) j0 D1 ]+ J
    SelectObject( _hDc, _hFont );
7 ]7 B  j  Y5 K. h; j! P. |6 o; [) v3 T+ C
接着设置背景色和文字色:" H/ M" N& T; X7 E
    SetTextColor( _hDc, RGB(255,255,255) );! x2 W+ _# t+ j, a: n6 I: @
    SetBkColor( _hDc, 0 );, ^, x# @! a$ W) T2 J" T
8 u$ \2 ?" ^% e& K
设置文字为上对齐:
1 A- z, ?: `/ Y( Y% P    SetTextAlign( _hDc, TA_TOP );
) {2 R" ?# b+ ~. r1 h$ u9 d. ^. h7 B' M, d: l
创建Texture所需要的顶点缓冲:
" n8 x" j! U1 O: C6 z1 [0 `5 r- D    if ( FAILED( _pd3dDevice->CreateVertexBuffer( _Max * 6 * sizeof(FONT2DVERTEX),; F& \) a7 J- i. G: _3 B
        D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,: f. C6 [/ T4 [- D3 E
        D3DPOOL_DEFAULT, &_pVB ) ) )
* R! Z! S3 ]7 L) Q3 S* G" R    {
3 U$ I5 n  n# S: Z5 l        DeleteObject( _hFont );
+ U* ?) Q/ u  L% y2 f        DeleteObject( _hBmp );
, i$ l+ M: Q& ^) d9 |        DeleteDC( _hDc );& @& ~  D) b& i
        return false;( |1 y  h4 \: J  Z3 N7 h
    }
( }9 [% A' x: N7 v; e1 j9 N% }   
* R3 r7 G( D, P# P3 X创建Texture! \- j7 v- z2 {$ P3 ]7 [+ g
    if ( FAILED( _pd3dDevice->CreateTexture( _TextureSize, _TextureSize, 1, 0, 4 U$ p( [& y7 N- d+ d* A
        D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &_pTexture ) ) )
# f6 u  ]1 O; t    {/ P0 R9 q8 y4 H
        DeleteObject( _hFont );
: W  F8 r1 a: {! h% L& i        DeleteObject( _hBmp );' c. Y) y& }5 A4 U! K  g( u6 J: i/ C, M  o
        DeleteDC( _hDc );
7 ]/ g4 z# S; \1 \        SAFE_RELEASE(_pVB);
$ h; q! [9 u" j# Z% B; X8 ^; G9 U9 u- |. D         return false;4 h3 p7 ?  q0 X8 w! h: n- l  S
    }
2 d" Y& v" C0 ~7 ~5 |
' w4 m+ O. T$ J2 k# n, t. V6 ^! t设置渲染设备的渲染属性:
# W; J: q/ ?3 }    _pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE,   TRUE );/ T2 c( I2 \/ U4 I5 {0 P5 B2 o2 G5 u
    _pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );! l) i$ q9 N; q+ H% t
    _pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
5 w* c2 {2 i' G& ^- v9 v' J    _pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE,        TRUE );
( O2 e- O' @( d+ `& g+ {    _pd3dDevice->SetRenderState( D3DRS_ALPHAREF,            0x08 );  \2 i; A1 ]2 y! f- e
    _pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC,            D3DCMP_GREATEREQUAL );
) \0 ~/ _( H$ B* d' z    _pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,    D3DTOP_MODULATE );
3 W/ |8 a. h: c" }* s% u+ H, u) V        
! B! Y: I4 V( e# }( V1 o    _pd3dDevice->SetTexture( 0, _pTexture );* h# U( M4 }+ [# Z( e1 T2 D. f, R
    _pd3dDevice->SetVertexShader( D3DFVF_FONT2DVERTEX );1 g6 k/ B0 }6 v0 z/ N7 r
    _pd3dDevice->SetStreamSource( 0, _pVB, sizeof(FONT2DVERTEX) );
/ Q' r. o, \# `( _' t2 h% Y) l2 t* `* y+ o6 \/ g
设置缓冲的最大容量  k4 S- N( R% Z4 G+ H0 R
    _vBuf.resize( _Max );
& D9 Q7 L( }0 U( `! U. l8 d
4 K& A! C) @0 s9 M; h5 V这样,初始化完成了。接下来是如何把一个汉字写到Texture中,以及如何进行管理。定义函数:
/ C) K% \8 m6 v0 t4 W// 得到文字在纹理中的位置0 J6 \0 C6 M5 ]1 t
void CFont::2 y! J8 L8 n- s! R# s5 H2 R
/*-------------------------------------------------------------
: e  L* F% }, x! W' V/ m. A+ ychar c1   ---  文字的第1个字节3 E8 N3 \  E" Q& O& z. I
char c2   ---  文字的第2个字节& ]4 R2 e. O9 D8 T7 u4 s
int & tX  ---  写入纹理中的坐标x: ^$ b0 L# c8 G) c
int & tY  ---  写入纹理中的坐标y# M+ n! Q& |# C
-------------------------------------------------------------*/% J- \4 K# x9 p. N# e8 Z7 R, p
Char2Texture( char c1, char c2, int & tX, int & tY )3 w9 c0 ^. Z% \- ]) c
{3 b8 e; m6 T; x
    WORD w = MAKEWORD(c1, c2);        // 把此字变为WORD
2 O. I, ]/ w) p# E6 |7 @' B    vector<Char>::iterator it = find( _vBuf.begin(), _vBuf.end(), w );
7 H& [8 Q- W$ B% ?    if ( it == _vBuf.end() )        // 如果没找到# Y; X, K; J, v  T
    {% X9 z" K. m  R+ F, d1 y" r
        it = find( _vBuf.begin(), _vBuf.end(), 0 ); // 查找空闲位置
& ]/ t4 L: B1 C" U8 r7 e4 @        if ( it == _vBuf.end() )    // 缓冲已满$ t/ P3 }2 ?3 ], V$ `" H
        {4 G% L& n. P  c9 |2 @
            for(; it!=_vBuf.begin(); it-- )- y" `  T/ h7 k* d7 s6 W
            {2 {/ p. S: [( ^
                it->hz = 0;4 f' W9 y% S# [7 G3 L' s" B6 |
            }
0 C) U6 |( @7 g8 |: d//            Log.Output( "字体缓冲已满, 清空!" );: D( _  C; s" Z$ |  q' v
        }- D- {9 V5 H3 u+ E7 O
/ [& E$ y& D  I) {# i7 [
        // 计算当前空闲的Char在缓冲中是第几个
) G+ h0 a0 m4 [: ]/ c8 Y. q" Z4 i        int at = it-_vBuf.begin();( |2 a) X. d+ A

0 |5 u5 `7 P- ], }1 B        // 得到空闲位置的坐标
: t+ k( k7 H7 J( E- L' R( |$ s        tX = (at % _RowNum) * _TextSize;- R: ]- ^* ~1 U' k( t
        tY = (at / _RowNum) * _TextSize;! n+ _0 v9 Z4 e  Q  Z6 I! ]
" ^- a8 ?/ N" `5 P4 ]
        // 设置这个Char为使用中; w9 g# ^5 z( R
        (*it).hz = w;
& v  m1 K* C: p1 ^/ E- H, {; k
' x9 p0 M$ C5 |  `2 N  s8 y        RECT rect = {0, 0, _TextSize, _TextSize};
2 D2 i+ s& q# y" b' B        char sz[3] = {c1, c2, '\0'};4 M7 C' J2 V3 }; D5 S+ r9 K& w; t
        // 填充背景为黑色(透明色)
, i5 n% q4 o- W; v' t        FillRect( _hDc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
/ |1 n' @/ K2 @) N- }0 d        // 往hBitmap上写字  c0 Y) O0 A0 ]$ x1 Y' O! P6 P! q
        ::TextOut( _hDc, 0, 0, sz, c1 & 0x80 ? 2 : 1 );$ W4 i, e, F: K; C
        
6 w# E4 k" W; l- U" w3 p        // 锁定表面, 把汉字写入纹理, 白色的是字(可见), 黑色为背景(透明)
) F' M4 K( `* H3 \& i, ?, b% K        D3DLOCKED_RECT d3dlr;; ~0 H# q! t4 s4 v
        _pTexture->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);( f6 O# v, f, ]' g7 m$ |
        BYTE * pDstRow = (BYTE*)( (WORD *)d3dlr.pBits + tY * _TextureSize + tX );3 b$ ~$ k$ f: E* |9 H7 n4 }
        8 F  [: x8 {* @+ ]. |
        for (DWORD y=0; y<_TextSize; y++)2 W8 x6 p: h& [7 X, w) j8 E6 l
        {
2 o0 ^( R: A  \            WORD * pDst16 = (WORD*)pDstRow;' J) F/ E% b! A
            for (DWORD x=0; x<_TextSize; x++)/ g0 W4 i; ]/ w+ x0 M2 _  M& G
            {
2 h' E' w" L1 q% x                BYTE bAlpha = (BYTE)((_pBits[_TextSize * y + x] & 0xff) >> 4);5 y& ?/ n1 O: p) V; z5 M
                if (bAlpha > 0)
8 C1 z+ V. O* p7 d* \$ b                    *pDst16++ = (bAlpha << 12) | 0x0fff;/ f* y9 }/ ]  f& {" E, f" z
                else
8 F8 i! G% G3 w: p6 a6 N' f                    *pDst16++ = 0x0000;
* k6 U9 x; L. H2 x* V1 }            }
5 N9 q  U% G9 e# G            pDstRow += d3dlr.Pitch;
" p! i$ D& [, Y        }; F% W0 e3 d+ G* e2 |- P( b
        _pTexture->UnlockRect( NULL );. t0 M1 t- b1 g  q
    }+ A* B% U  ]+ m% t* g8 ?% g8 _
    else2 l5 o4 \4 s  H! N" \1 _
    {
+ P- j' k; Z# h  v' y% m7 m0 k9 l        // 计算当前空闲的Char在缓冲中是第几个
& v, t3 ?) ~4 G8 ~, {! Y        int at = it-_vBuf.begin();5 v/ f8 l7 [9 y9 U
7 o! s' {% ^5 c1 b- W; k% N- j: H4 Z. v
        // 得到这个字的坐标7 k" X: v- `/ V& s; g5 J- F3 ~
        tX = (at % _RowNum) * _TextSize;
5 S) }) L# Y$ `- |  l        tY = (at / _RowNum) * _TextSize;4 i8 U/ g, R; C; P5 \
    }
- n9 o1 m1 T2 m. l}
: V' S# L+ i  Q! v& B. r* f7 Z以上代码中的注释已经很清楚了,相信无须我多言。这里唯一需要声明的是:原来所定义的Char结构是这样的
0 A& A) q! `; Ostruct Char{
/ I! \1 o' ]  g+ ]" _, ]  char hz[3];   // 保存汉字
( S- n9 @0 }: @$ m* e  int frequency;// 使用频率
& w" G/ N# z$ t: T  l) U  RECT rect;    // 这个字对应位图的区域' e" B2 f& S7 v3 F9 q/ i
  Bool isUsing; // 是否使用/ u7 d/ Y& {- f, n- _! O$ T2 N" w- _
}/ }/ k3 n- K5 L! o
后来因为将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。
+ A& {/ K: n6 S+ n然后对于RECT rect,因为没有了int frequency,意味着一旦将汉字写入到Texture,其位置就不会变动了。所以,很容易根据find函数操作后的iterator,直接计算出这个汉字所在Texture的位置。这样,RECT rect也不再必须。: k, f! U) I" T: F1 c. ^/ A
而bool isUsing,它本身就是个鸡肋,要也可以,这样结构更加清晰。不过,直接通过观察WORD hz为0或非0,即可实现isUsing的作用了。' T( f5 E# E- A! q
为什么要对结构Char这么精雕细琢呢?+ _* |5 B  W$ Z: K) L7 b0 H
1.    既然没有必要的东西,就应该删除
% B5 V9 `. a- v5 b2.    Char结构的大小越大,vector所要求的内存越大
4 M7 f1 I+ U6 v4 {' y2 i. A: ~3.    小的结构,find可以更快地查找出所结果
$ I8 F# e& Q. T$ }为什么find会正常工作呢?这里我要大概地讲一下find是如何查找出所需的位置的:它只是简单地使用while从vector的begin一直遍历到end,逐个判断,直到找到为止。find要求必须实现自己的operator ==(),进一步跟踪到find的源码中,发现也是这样。于是前面的结构Char变成了现在这样:: X& A- k& P) [+ O7 z3 y. W( }
    struct Char{
8 U% E" J7 {+ o5 K- l* l& O  @        WORD    hz;                // 文字
" S; c9 T' N6 [# k# U" B( n$ F  R8 U3 M
        Char() : hz(0) {}
# Z1 ]; m1 o2 ~3 |: |# p  b: M& R" ~" @3 }, d9 @
        // 用作查找文字' F+ x7 o6 ~& o
        inline bool operator == ( WORD h ) const
) ~% g. p7 d# W        {
& j) Z; I  I' I* G, C            return hz==h ? true : false;: w5 i* Z& ~: E: i+ q& a4 L; \, [/ M
        }$ F' C* K& |  C5 ]
    };
/ Z( R/ W" H0 a5 i* ?( Y3 w是不是很简单?^___^
( Q; \# c' s! _& ]1 W8 N% R
9 n  v: a# B0 T$ W终于到了显示的函数了:
8 L- b7 j) w7 r2 t9 M1 E8 t// 得到文字在纹理中的位置
( e5 V/ _, r# N% y/ x5 mbool CFont::
4 k# _; N+ w8 @/*-------------------------------------------------------------( v. L  B/ E' m, q% g8 J; ~
char szText[]  ---  显示的字符串
: W+ f  z$ @5 R2 B& Tint x           ---  屏幕坐标x8 h7 G* l. {; A3 M; I( _1 Y* T
int y           ---  屏幕坐标y
" B# ?) E( l! o/ o2 o" \% yD3DCOLOR       ---  颜色及alpha值# d) {6 v4 y* i- G) d+ U
int nLen       ---  字符串长度3 G& C+ j; D& ^. v# p2 q% A8 [
float fScale   ---  放大比例+ k6 A- O3 r$ F( D# D
-------------------------------------------------------------*// G0 d, k/ z( V* i  _% h; f
TextOut( char szText[], int x, int y, D3DCOLOR color, int nLen, float fScale )# B$ n0 N; Q' u: E; j8 r
{1 P: {6 J) _4 l7 ]2 C' F8 e
    Assert( szText!=NULL );
9 v/ u/ A# U* V( d
5 |; s. H4 p$ Z4 s9 g    float sx = x, sy = y,
  Y- l- B# P, T/ \/ L0 _: B          offset=0, w=0, h=0, tx1=0, ty1=0, tx2=0, ty2=0;
$ l+ s! `6 e1 U    w = h = (float)_TextSize * fScale;
) v" x# U: U' M# }8 Y* d- M/ M, z- `
% h/ q3 q) `2 A. R3 S; U& G) Z    char ch[3] = {0,0,0};
- V: k3 n2 I2 l- J( z) C( M    FONT2DVERTEX * pVertices = NULL;2 b8 Z, ]1 c0 r7 o% s& J, P! y
    UINT wNumTriangles = 0;
3 G  I3 X- N5 E8 q# V    _pVB->Lock(0, 0, (BYTE**)&pVertices, D3DLOCK_DISCARD);
/ _* N1 ~) i0 b; @; F! H6 A! A" p% _1 J: q% d9 O! i
    if ( -1 == nLen ||                // 默认值-1
8 Q, z( }4 H5 l         nLen > lstrlen( szText ) ) // 如果nLen大于字符串实际长度, 则nLen=实际长度
/ ^' K" v; u& L( ^        nLen = lstrlen( szText );1 B+ V1 h' D* {9 L
    for (int n=0; n<nLen; n++ ); h4 `+ @: O5 I+ z* k  N
    {& z0 {. G9 B, l% `* `
        ch[0] = szText[n];
' `5 L1 w; P5 B8 Q0 X8 |) Y/ c0 V* }' y$ f
        if ( ch[0]=='\n' )
  A# a2 h7 N. l+ F. x# R. {8 C! E        {! A) [! g/ A( m+ S! Y( g9 j
            sy+=h;
4 F. n; ]$ C$ |0 H            sx=x;
+ V4 _% [& F& v; ~+ c4 P            continue;
- T( o. S. u! V8 T6 d2 Y- _* S& p        }) H! f- _4 x3 J: D' y  W: R# D. s

; A2 m3 p6 {# y" b( N4 I7 c' c        if ( ch[0] & 0x80 )6 w( u' X1 h$ |& \
        {0 _1 Q* d4 Q' a, L3 u
            n++;
: _5 S. B# K- P$ s            ch[1] = szText[n];
6 E3 F4 [- u+ \. V            offset = w;+ _: u3 y5 r# M3 s$ W$ |, _
        }
. Y" W7 O1 s, e1 Q. p/ t& _        else; F8 s6 \$ N/ O$ D: d& [, d/ ~
        {( d3 k; i: `9 r2 b
            ch[1] = '\0';
  N; l$ {4 M6 m* `! z            offset = w / 2 ;
; t0 e# O/ Q" o5 A        }
1 x# R) ~+ m+ a! e4 W. s2 F5 f' I9 S. l7 l
        int a, b;
- F6 N5 H) ~$ F. O; U7 m' T0 q! |        Char2Texture( ch[0], ch[1], a, b );
: L7 E, K6 j. S6 u5 D6 `- o& ?   
" [# {: E6 h# c2 q" N1 m        // 计算纹理左上角 0.0-1.0) Q6 f' J! K) B9 M. x6 K3 K
        tx1 = (float)(a) / _TextureSize;
9 x- T$ A" W1 i$ i        ty1 = (float)(b) / _TextureSize;" J0 Y5 g  W9 }) A) k0 g  d
        // 计算纹理右上角 0.0-1.0+ D. h* |$ }& T; g5 U
        tx2 = tx1 + (float)_TextSize / _TextureSize;
/ e7 I0 C5 A$ J- M$ p        ty2 = ty1 + (float)_TextSize / _TextureSize;
* s/ }  z# O3 j5 \" n: s, g4 r
/ y' i# k/ x) E0 t3 X5 H1 V        // 填充顶点缓冲区
  \! a; I6 J: p/ [/ r        *pVertices++ = FONT2DVERTEX(sx,        sy + h, 0.9f, color, tx1, ty2);/ C+ |9 }3 R3 T, |
        *pVertices++ = FONT2DVERTEX(sx,        sy,        0.9f, color, tx1, ty1);
; V# p  l. K+ ~/ U3 H* H$ T& Q9 Y        *pVertices++ = FONT2DVERTEX(sx + w, sy + h, 0.9f, color, tx2, ty2);
9 A" @! g, u* |# H7 V5 p: @        *pVertices++ = FONT2DVERTEX(sx + w, sy,        0.9f, color, tx2, ty1);; O& G9 U# ~: I/ K2 W1 `
        *pVertices++ = FONT2DVERTEX(sx + w, sy + h,    0.9f, color, tx2, ty2);
5 E6 p8 s. |0 _% j        *pVertices++ = FONT2DVERTEX(sx,        sy,        0.9f, color, tx1, ty1);. r$ D2 r% P' k, l8 s& M
9 J( m% i+ _$ _1 A0 U" L6 U
        wNumTriangles+=2;( ~( }2 t4 k( h. L

) c' F# N5 u5 J& Z/ [/ j        sx+=offset;    // 坐标x增量
. I/ k* o2 o2 E: E* e. q; s' {    }% K9 W6 d& @9 {5 N2 U0 J* e1 r
    _pVB->Unlock();
& S4 i& ^1 f% ?, O    _pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, wNumTriangles );! [+ m, h1 b4 `- p$ Y: F

/ V; Y9 V0 F7 i9 S    return true;% |5 B3 \$ D- }% o% L4 @2 y
}! h9 G& b9 a& d( h+ {. g
结束语3 D0 G" q% R+ j% M- [7 U
记得有一句名言: Keep it simple and stupid.在实现功能的同时,保持代码简单、清晰是非常重要的一件事。相信在往后的日子里,在不论是别人阅读或是你自己回顾的时候,你都会发现一如既往地遵守这个守则,是多么得重要!$ F$ Z% @4 p$ }1 f  w; E
相信通过上面我那无数的废话,加上代码中还算足够的注释,聪明的你一定能够明白这其中的原理了吧。如果以上的内容还不足以让你完全搞清楚的话,你可以登录我的主页:
% ]4 i. S7 U: M炎龙工作室
, R3 @4 ]+ D# h5 Y: m' i& K9 [上面不仅包括了上面所写的程序代码,还有一个用来演示效果的一个很简单的demo。5 F# x- ~' H3 ]" @$ O# a" c6 F: u
说明,以上所实现的CFont是包含在我的游戏引擎中的一个部件,而目前已经实现的部件包括有:* A9 a6 j8 ^; P$ n0 P
1.    CGameFrame(游戏框架类)  -----  封装了窗口及D3D设备的建立,需要派生出自己的子类
, K% c  c- {! X3 e2.    CAudio和CSound(声音类) -----  支持wav/mid/mp3的播放  @( {5 H. p, I- ?) |$ h6 G3 Q( u
3.    CDirectInput(控制类)    -----  键盘、鼠标操作1 \; |; @6 `/ r" l2 G8 L* r) \
4.    CDirectShow(视频类)     -----  支持avi/mpg/mov等的播放% c# y1 T! t0 P0 h
5.    CSpriteX(精灵类)        -----  方便游戏中对精灵的控制
! o+ l, c+ U* d3 m6.    CFont(字体类)           -----  中英文字体的显示' p9 D- v% A" x& [/ c$ Q
7.    CTimer(时间类)          -----  高精度时间的控制+ j& B; t4 B0 I1 [
8.    FPS(fps 类)             -----  fps的计算% O" G2 f9 Y7 |& a% q1 f, o! V. j
9.    LOG(日志类)             -----  游戏中的错误反应以及状态记录
8 @$ }, R7 y2 m1 S9 [- T最重要的是,这个Game Engine完全是开放源代码的。关于更新的情况、版本说明以及源码下载,请随时关注我的主页!
7 X! W% Q) C8 K5 L5 ~接下来,我将会继续完善这个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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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