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

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

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

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

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

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

讲了一些TTF的知识,也许对汉化有用
- |3 O7 H8 p$ T! R9 {; ~0 m
$ f4 C1 y5 M' M7 L8 c
2 N# ]  Q; o9 Z1 ?: z游戏中汉字显示的实现与技巧
, `) ^5 S! y) g" ]5 E2 g作者:炎龙工作室 千里马肝
2 d+ B9 x! r. M6 k$ S9 c版本:v1.07 b) v) D6 M8 I8 D' [+ _% u
最后更新日期:2002-3-30( O  l3 ^2 |9 ^; r9 i
绪言6 ~9 `* t: G/ p3 d# n
在游戏中,因为我们是中国人麻,通常都需要显示汉字,比方说交待剧情。而对于文字的显示,英文的显示要较其简单得多,因为只有26个字母,就算再加一些标点、符号什么的,用一张位图,就可以足以显示所有的单词了,而相关实现技巧,也比较轻松。 8 V- E6 k" B( Z1 I2 [" q+ Y7 Q- R
而中文的显示方法,要复杂得许多。记得原来在DOS下,汉字的显示都是读的UCDOS的点阵字库,而点阵字库的读取方法,在UCDOS SDK中都有源代码可以参考。但是自从Windows操作系统开始,我们开始了解到一种更好的字库,它就是TTF。
6 ^: s. O! v* Z: U! g# [6 R2 u注:以下我所指的开发环境,除非明确说明,默认的平台是VC6.0+DirectX8.1,使用D3D来加速2D。然后使用的STL是用的SGI实现的那一套STL。" ?" E: W. C, j$ \: v( P1 L7 |
点阵字库
, s) j2 T4 q5 Y* {+ N) o1 }2 U1 a包括现在,有很多游戏都还是使用的点阵字库。因为操作起来比较方便,加上这方面的经验已经积累了好几年了。通常如果只是一种字体就可以满足需要的话,它会是一个比较好、快的解决办法。但是它有3个缺点:' O  q6 O$ c4 d; _( C( y
1.    如果放大显示,不做处理的话,显示出来的汉字,是很难看的。
  ~5 U/ p3 R" w1 H' G2.    像是UCDOS所提供的点阵字库,只有24点阵的有几种字体,如:宋体、黑体、揩体…,而16点阵的好象就只有宋体一种。3 {4 I( U/ T3 p7 [& o
3.    点阵字库,通常是有版权的,尤其是第三方制作的汉字库(如:方正)。
* n5 s1 D$ W+ w" ~在这样的情况下,当我们写好这样的一个显示函数,就算是解决了如:放大、快速显示等问题的话,可供选择的字体还是太过于局限了。所以,在字体的要求比较强的情况下,点阵字库并不是一个好的解决方法,他不够灵活。尽管我们对于它的操作是如此得熟练,可以写出优美的代码来展示我们的编程技巧。( O$ g6 k: p) W. u# P6 B
TTF  ]0 e  w% r4 y3 C
TTF是True Type Font的简称。在Windows\Fonts目录下面,我们可以看到许多后缀为ttf的文件,它就是接下来我们接下来所要谈到的。
4 [1 [  h4 U9 p3 h" ^3 v8 oTTF是一种矢量字库。我们经常可以听到矢量这个词,像是FLASH中的矢量图形,在100*100分辨率下制作的flash,就算它放大为全屏,显示出的画面也不会出现马赛克。所谓矢量,其实说白了就是用点和线来描述图形,这样,在图形需要放大的时候,只要把所有这个图形的点和线放大相应的倍数就可以了。而且,在网站上有很多的TTF字库可以下载,或者你可以去买一些专门的字库光盘。然后在你发行你精心制作的游戏时,可以顺便捎上这些后缀为.ttf的文件就行了。包括Quake这样的惊世之作,也都是用的TTF字库。+ f9 s0 ^/ o  H0 ]
这样,我们就可以解决点阵汉字的一些问题。通过TTF,我们在字体的质量和字库的数量上获得了暂时性的胜利。
& _0 w$ O& T/ e) b0 f6 R2 F字库的读取和显示
: p! q+ }6 Z) ]" G先前谈到点阵字库,只需要很简单的一些操作,就可以显示出想要的汉字。下面我给出一个读取hzk16的函数,它需要一个Surface以供显示用:4 y  v1 Y7 V5 A7 q3 S
#include <io.h>& J8 B, V* j9 V' w6 E9 U% I2 F; [
#include <stdio.h>( U2 d* `# d, G7 _, J( y
#include <conio.h>( \* C* G5 l/ j6 n  I
4 r3 z$ j) ^8 C. [- q
// 读取16x16, L! V1 S& C: f
void DispHZ16(int x, int y, BYTE *Str, LPDIRECTDRAWSURFACE surf)& L7 d0 A0 G, _  M" H
{
. h4 q+ z/ @$ F! e( r; O; A    const int Mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };' v+ A- Q4 b& n, n# O. ?; q
    FILE *HzkFp;
# L6 u, {4 ~  n7 H6 X0 W+ S    WORD i, j, k=0, m;5 \7 v% {' O. C' p& c
    WORD HzNum;4 N% g1 t* {6 Y" Q7 O8 N
    WORD QuHao;
& y, }5 j# e2 s    WORD WeiHao;
7 m- Q9 d- G- k+ w0 B$ E$ [    long offset;2 ?) B) H% O- \, q/ ~$ U& t8 \8 C; X9 ~
    BYTE dotBuffer[32];    - i- v  F1 M6 Q9 D) u  c+ u

6 l) v$ X( H8 K( k4 Z9 d; Z    HzkFp = fopen("HZK16", "rb");4 X" f: [  [6 l  q4 B
   
5 i, Y1 E5 f& A    HzNum = strlen((const char *)Str)/2;" T2 d6 [- e4 Y

# B% C& L; m6 K- k) _/ r    DDSURFACEDESC       ddsd;6 ^7 X7 f5 c  p9 I- a
    LPWORD              lpSurface;
- X6 ?. r# T3 W    HRESULT             ddrval;
7 u2 b' N* _) M  h* \: n   
4 V: M6 h% E- R6 Y# Q    ddsd.dwSize = sizeof(ddsd);
* t1 g6 h& K$ W    * w6 l+ N/ ]+ v) h7 v" N( z/ d
    while((ddrval=surf->Lock(NULL, &ddsd, 0, NULL))==DDERR_WASSTILLDRAWING);
+ ~2 d( Q3 R3 L8 o  e, e/ X    if(ddrval == DD_OK)3 [- F. \% I* A3 Z+ M) W
        lpSurface = (LPWORD)ddsd.lpSurface;
" S! D# l$ V- |" D( p$ I& E3 y' j- J9 A; [( K  G+ m
    for(i = 0; i<HzNum; i++)- F# W: h( {: @. [6 U/ p
    {    * q0 z) R+ F$ h7 B" t
        QuHao = Str[i*2]-160;! u8 P5 f8 I7 |, V" j
        WeiHao = Str[i*2+1]-160;
0 _, X/ a6 U; j: p+ T& t' s2 C& x$ M! G. H7 P3 {, m" G; q
        offset = ((QuHao - 1) * 94 + (WeiHao-1))*32;
' H' C; a+ Z. G: R+ y0 m. P
. W5 M, O7 y8 L- j        fseek(HzkFp, offset, SEEK_SET);! Q4 m! z( Q6 ]9 L" o
        fread(dotBuffer, 32, 1, HzkFp);
3 k& o( E4 N, P! n3 N: z" @9 [' g$ q# l
        for(j=0;j<16;j++)
* i- I8 j8 U; u: t% w% z4 r% m            for(k=0;k<2;k++)
' q( L+ U$ x* T                for(m=0;m<8;m++)
8 J0 U* B1 B( V9 \; z                    if(dotBuffer[j*2+k] & Mask[m])6 S5 L/ u9 q8 b6 C
                    {" N, F" n% @% B: u: `( d
                        lpSurface[ddsd.lPitch*(y+j+1) + x+k*8+m] = 0x000000;
5 o9 E0 }# y; P2 q                    }  E( I; M+ ~/ W4 u
        x+=16;
  ~% _/ U8 S$ f' w' O    }! L" v7 A8 G) d- D
4 b8 S4 ]9 Z& j( w! Y! Y+ Z
    surf->Unlock(NULL);5 e( j. Y5 p2 m+ Q

: f6 E8 U$ W1 p/ N3 i    fclose(HzkFp);: m9 ?" a2 O5 |7 r9 P% ]
}
/ k" A( g/ u" [# w2 M其实原理很简单:
1 B: A$ U6 e, m4 j  T+ u4 U6 n1.    打开字库# [, z* X3 g# u' T+ d4 B8 _- f
2.    计算字符串长度(这个函数只支持中文),并且Lock Surface
& V) i$ N8 |0 w7 e& n, k0 M8 U3.    依次计算出每个汉字所对应的区码和位码(汉字的第1个字节是区码,第2个字节就是位码),然后通过公式计算出这个汉字在字库中的偏移量:
  s7 l# |0 h6 |# @+ W0 aoffset = ((QuHao - 1) * 94 + (WeiHao-1))*32;4 v, J& n" z- _. v* P( I) R# r. r" t
4.    读出一个32个字节的点阵
, `5 q+ l6 \: m6 I) I2 O' |+ t& j5.    绘制到Surface上
: W- {2 J6 m; I# |7 E8 g) w以上只是16*16点阵字库的显示方法,24*24的读取方法与之类似,大家可以参照相关资料来书写出自己的代码。
7 m5 A: }5 `  v, p  p- d( {9 S8 n1 U- Q
如何显示TTF字库呢,有很多种手段,下面我按从简单到复杂的的顺序依次介绍:" Y6 T& }. y/ N% W1 a1 d  t% G
1.    使用Windows API,也就是大家所熟悉的TextOut。通过它,还需要一个HDC(设备句柄),我们就可以随意地在屏幕任何地方显示出文字了。
  M4 B0 j$ r6 v5 R0 _2.    在http://www.freetype.org,有一个FreeType的免费库,而且是OpenSource的。它目前有2个版本:1.0和2.0。其区别在于,1.0只能读取TTF格式的,而2.0支持更多的文件格式,在使用它之前请详细阅读所要遵循的Licence,以下是摘自FreeType2.0对字库的支持列表:* a$ x) ^$ d) C3 Y) Y& A
o    TrueType fonts (and collections) 5 i6 |) H, r; k' ^  y
o    Type 1 fonts
* I# X5 ?2 `  k5 B2 s9 X+ F( ao    CID-keyed Type 1 fonts 5 g7 F3 b8 L9 f3 Z' `1 a1 I3 [
o    CFF fonts . d0 }) c/ \0 I
o    OpenType fonts (both TrueType and CFF variants) - e' F, U/ e5 S# Q3 o( K- K3 S$ Y
o    SFNT-based bitmap fonts
0 ^2 x6 C/ K- U: |8 h3 [. so    X11 PCF fonts
. k1 [( [. n; D7 S/ w# u* Jo    Windows FNT fonts
" V& C' o* W  e% B7 r3.    自己研究TTF的格式,然后自己来操作。/ @, [* R; j% |" A' a3 Z! Y
7 x; ~: R/ `1 D  ~1 O* X
....... ╮╮ ( {- c" y3 J) X; t9 L
      \█/倒!
- Y1 P9 ]* M. i9 s2 N! p& s5 g$ i      ●# ?( e$ e& |; K0 S9 E, {
虽然我们想要把每一件事情都做好,但是也不是每一件事情都要亲历亲为。如果你非要这样,也行^____^,但是过不了多久,你就会陷入泥沼,到时候你会发现自己的热情正在慢慢被磨灭,什么叫做抓狂,相信你很快就会知道^_^。6 ?# t0 o, q; H0 f; \
5 d7 \6 e1 f$ t
在有多种选择可以取舍的情况下,我们需要考虑一下,对比一下各种解决方法的优劣。
% x$ I3 J1 B  g8 u  ~/ M1 y9 G: u7 v2 u" z. s- h: ]
在DirectDraw时代,我们都不自觉地喜欢上了GetDC,因为……多方便啊。可是现在已经到了DirectX8.1时代了(我要使劲地摇那些还沉醉于DirectX7中,为如何在使用alpha时提升那可怜的1、2个fps的朋友们:醒醒,该起床了!),HDC已经被M$列为禁用品。怎么办呢?是的,你可能已经想到了,我们还一直保存着窗口的hWnd呢,可以通过它来得到hdc,从而调用那些需要hdc的API,可是,这样做是更为愚蠢的,这样对你是没有一点好处的,不信,你就试试吧。有一句话,请牢记:要想你的游戏有更快的速度的话,请不要再去碰HDC了。# t0 Y* y' \0 |: ?" A
我们非常清楚hdc是一个超慢的解决办法,它无法在我们的高速游戏中满60分及格。下面来看看FreeType,它更像是一个Service。它的解决方法是,先通过一系列的初始化和设置,告诉FreeType字体的名字和大小等,然后它会动态地申请一个Graphic,再把我们要显示的字画到这个Graphic上,你还可以把它保存为tga格式。不过我们最终所想要的不是这个,所以可能我们还需要从这个Graphic上逐点读取或者用CopyRect,然后再画到我们的画面上。其实它已经是很方便的了,可是需要你去学习如何配置和使用它,这是很花时间的一件事情,而且它最大的优点是可以跨平台,我们需要它吗?如果有一个更为简单的办法,像是如果Textout不是那么慢的话,就好了……
4 V' `0 F$ [7 V4 Z9 ], R$ I在这里,顺便谈一下另2个字体显示类:ID3DXFont和CD3DFONT。可能早就有人会说怎么在上面的列表中没有它们?原因我会在下面慢慢地说明:6 V# i: C9 s, k% c1 w
ID3DXFont,它存在于D3DX库中,一个现成的字体类,不过对于它的处理方法……我实在不敢恭维,就引用一位大师所说的话来表达我的看法吧: 在内部实现中, ID3DXFont::DrawText()函数确实做了我上面讨论的工作,先建立一张GDI兼容的位图,把文本绘制到位图上,而后把位图拷贝到纹理贴图上去,最后把纹理渲染到屏幕上。这样你就聚齐了所有的龟速的原始GDI函数,还包括了一大堆的额外开销 — 最终,这个函数比原来GDI的DrawTextEx()函数要慢上超过六倍……. F8 ?. ?0 N+ r4 ~. N
CD3DFONT,是由M$在D3D的框架代码中提供。不过它只能显示英文,有很多朋友通过自己定制和修改这个类,来实现自己的中文显示。不过效果都不是很好。其实原理,跟ID3DXFont的方法差不多,不过处理方法要聪明了一点。
$ W+ |3 N4 ^- r, f; I3 B分析与思考$ Y& q0 Y! F1 [' S! P$ n# O2 j
那么我们应该怎么办呢?通常我们会幻想,如果可以像处理英文那样,把所有的汉字都保存在一张位图里,该有多好。这样,显示的速度就不是问题了,直接可以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 v' w" Z+ h, I/ |- e8 v汉字虽然很多,但是常用的汉字,其实也就只有那么几百个。像这样的字:鬯、鞴,你一辈子会看到多少次呢?如果可以做一个类似于Cache的东西,保存着常用的那些个汉字,在需要显示的的时候,先在Cache中查找,如果有的话,就马上画上去;如果没有,就从字库中提取到Cache中。这样的话,在使用Texture来保存汉字的位图信息的同时,对于每个汉字,我们还要定义一个结构,然后用一个东西把它串起来,综合它们2个,也就实现了我们所要的Cache了。刚开始,我所定义的结构是这样的:% K, ?7 P$ Z$ O: S
struct Char{# j& O5 L0 i/ w* ^9 H. O; P& d
  char hz[3];   // 保存汉字
4 N" F" d1 W3 `- q( U% @; b  int frequency;// 使用频率
" n8 V# S% }" ~! I  RECT rect;    // 这个字对应位图的区域5 N1 o" p+ F0 I, k5 H
  Bool isUsing; // 是否使用. L" H4 g! T+ A
}
1 p& t& D$ i# E+ ?) Q对于汉字和英文,我在这里大概地讲一下原理:汉字是由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’。
1 b- R5 \- w) n4 j9 d  I+ X& o+ p接下来,对于使用char[3]来保存汉字,是否真的很合适呢?因为如果把它当作一个字符串来看的话,在查找时就需要使用 strcmp 来比较字符串了,这样一定是会影响速度的。如果不把它看作字符串(字符串的最后一个字节需要以’\0’结尾),只用char[2]的话,我们可以只是简单地调用宏MAKEWORD,把2个byte压成1个WORD。当把文字作为一个WORD来看的时候,这样查找比较时可以用WORD内建的==操作,这样要比调用strcmp函数要快得多。/ t+ i0 ^$ E/ k7 W( @4 X* ?% T* d
int frequency用来标志每个WORD的使用频率。设想,如果一个字已经存在于Cache中,以后每对它调用一次,就让frequency++。这样做还有一个用意是,是否可以在一个合适的时候,以frequency为参照来对这整个Cache排个序,把常用的字放在前面。那么在显示时,可以先在Cache中查找所要显示的字是否已经存在于Cache中,如果有则直接显示,没有的话才需要采取某种手段将字加入到Cache中。一些常用的字(像:我、的、着、了、过……),使得显示的速度将会大大提高。3 X% \; U$ `. z% [3 F6 j$ h
其实上面说了半天的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上的区域位置。
8 Z4 [) Q6 I. S7 V, P使用什么东西来把这n个Char串起来呢,一般会想到的是链表,原因无非有2个:1 随时有新的字加进来,而内存是不连续的 2 它几乎没有容量的限制(除非是内存用完了)。不过链表的访问速度是很慢的,如果使用像数组这样的东西就好了。仔细想想,在这里,我们用来存储的Cache,最大也就是256*256(理由上面说了),所以大小应该会是固定的。我们只需要在数组中的给每一个汉字加上一个标志,说明这个位置的使用情况。那么就使用数组吧,这样的话,访问的速度要更快一些,直接首地址+偏移量就够了,不必像链表,在查找时需要逐node访问。当然,我绝不会想到用new  Char来申请这个数组。因为这样做实在没有必要,请不要过于迷信自己的能力,在STL中已经有vector了,为什么还要自己写呢?^_^最后的一个bool成员变量isUsing,也就是上面所说,用来标志使用情况的。* l" {% ^  x. D* h4 s
实际的操作% V) S* l7 b6 D" B' j  d2 o
上面考虑了那么多,我认为都是实际操作之前所应该有的。先谈谈如何显示吧,因为在DirectX8.1中已经将DirectDraw和Direct3D融合为DirectGraphics了。所以无法像原来那样了…………哦,实在有太多东西要讲了,我还是推荐几篇文章给你吧^_^:
8 d9 L9 V3 e" _. qhttp://vip.6to23.com/mays/develop/directx/200201/Geczy3Din2D.htm7 p! t$ {9 t" p0 Q) Z
http://vip.6to23.com/mays/develop/directx/200201/GESurface.htm
! o& {; |9 g$ g! Ahttp://vip.6to23.com/mays/develop/directx/200112/2DGtoDX8.htm
! o! [+ O* K! b. N2 Chttp://vip.6to23.com/mays/develop/directx/200201/DX8adv2D.htm: m" @  U& |% {+ \7 _
接下来,我会假设你已经具备了在DirectX8.1中绘图的基本概念了,所以在你继续往下阅读之前,请务必先仔细阅读以上推荐的文章。
) L& f( l! \9 ~2 o, Y5 i前面提到,需要一个vector来对应Texture上各个位置文字的信息,上面已经创建了一个结构Char,则这个vector的定义为:* ]- u- n( {" {5 ]8 K
    vector <Char> _vBuf;                    // 记录缓冲中现有的文字情况
9 u- ^. y$ ^+ Z1 ]& K0 @. i首先,由于可以利用硬件的放大缩小机能,所以字体的大小精度要求不是很高,只需要支持16*16和24*24大小的字体就可以了。我们需要一个这样的初始化函数:$ i6 ^" U, W, e$ ?, i
bool CFont:: 7 z& k7 N( L, v
/*-------------------------------------------------------------: ~  K  Y" e- o' W, N, Z
LPDIRECT3DDEVICE8 pd3dDevice  ---  D3DDevice设备9 z( D4 s# s- k3 A( B% S
char szFontName[]              ---  字体名(如: 宋体); Q% [9 E$ q) j+ E- b
int nSize                      ---  字体大小, 只支持16和245 p& y6 \* O5 o; F4 o7 Q: {
int nLevel                      ---  纹理的大小级别
1 n8 D# [$ J  n  S1 r( \-------------------------------------------------------------*// I. Z. t: u2 @: g6 @
Init( LPDIRECT3DDEVICE8 pd3dDevice, char szFontName[], int nSize, int nLevel )。1 P0 ?$ L: W& _9 B" j
% b$ X- D0 C& V8 G
在DirectX8.1中,由SetTexture(…)所贴的图的大小,也就是Texture的大小,是有大小限制的,长和宽都必须是2^n,而且位图越大,所花费的显存越大,这样留给其他显示用的显存就少了。所以,必须根据需求的不同,来自定Texture(也就是Cache)的大小。因为汉字点阵大小的原因,所以从实用角度而言(比方说只是显示fps或是短小的标题),开辟一个64*64大小的Texture,才能满足最低情况下的需要(这时如果选择16点阵的话可以存放16个汉字,24点阵可以存放7个,依次类推……)。; H! E3 j6 X6 n& ^
根据设置,创建Texture:. A3 I; O" i% |2 i3 \' F+ A' g( n
    _TextureSize = 32 << nLevel;        // 纹理大小
  x5 ]; l* f" f  |    _TextSize     = nSize;                // 文字大小
  B' @# a) y; F" q) ^2 g    _TextureSize = 32 << nLevel;        // 纹理大小) d* H% O1 ^( o* {  _
    3 }: q2 h7 ?3 C/ P4 o
    _RowNum = _TextureSize / _TextSize;    // 计算一行可以容纳多少个文字
# D: `1 p+ J, X. X3 P    _Max = _RowNum * _RowNum;            // 计算缓冲最大值
' s% N( C5 X" J/ l9 N4 Q) P/ V+ u# p& Z7 C+ {/ H5 [. ~
创建字体,还是需要使用Win32 API。也就是先创建一个HDC:" c$ t& m& t' A) h2 {
    _hDc = CreateCompatibleDC(NULL);
% E1 f5 }5 l" [1 N' X+ n
" O  S8 A- Z. }6 A, x+ c- _/ C然后创建一个BITMAP和一个FONT,将它们与HDC关联起来。$ d5 w1 f; t" z- U4 C
    LOGFONT LogFont;
  M* t/ e) w$ |" J! E    ZeroMemory( &LogFont, sizeof(LogFont) );
0 V% A0 A" q, T  g( [7 t" l5 C    LogFont.lfHeight            = -_TextSize;
. ~* a  h7 T% t; c4 t1 J    LogFont.lfWidth                = 0;# J# A* a' U) m# T: G# b; y
    LogFont.lfEscapement        = 0;
+ J. y& v3 z, i# d0 }    LogFont.lfOrientation        = 0;
& y. e, {; ~+ B9 d( _, V% @    LogFont.lfWeight            = FW_BOLD;5 H7 T& s% @" z/ q" D
    LogFont.lfItalic            = FALSE;
2 F2 a- n+ }9 \8 m+ {; @3 M    LogFont.lfUnderline            = FALSE;
! a& }4 P' E( _8 R: q    LogFont.lfStrikeOut            = FALSE;) D0 P3 t: ~1 |/ L( H
    LogFont.lfCharSet            = DEFAULT_CHARSET;% S9 R' X% e" F: o) _) B
    LogFont.lfOutPrecision        = OUT_DEFAULT_PRECIS;
4 C( m) V: v4 P1 R% m# L    LogFont.lfClipPrecision        = CLIP_DEFAULT_PRECIS;
% y& M) A# T  P4 X( d( y+ P$ u    LogFont.lfQuality            = DEFAULT_QUALITY;
! v  x: M( `; L' e% i. B$ O1 X& I1 Z    LogFont.lfPitchAndFamily    = DEFAULT_PITCH;. J, s* i1 K* J0 u: U! t/ z! o
    lstrcpy( LogFont.lfFaceName, szFontName );
: m& A2 u, ~$ e. M) ^: R) o( W   
$ j) m; ~% g# u/ n* z1 \" u' J    _hFont = CreateFontIndirect( &LogFont );
$ J2 r: M5 d" }    if ( NULL == _hFont )+ r7 b- z1 t3 h7 H) Y
    {! l' B0 N3 L' Q" y' b* {. f- j
        DeleteDC( _hDc );" n0 n' z/ {5 N
        return false;. v0 r" c5 s1 _" ~- v2 g5 \
    }  C5 ^( Y. g- f9 u
   
* e: q$ d% j1 w(只需要创建一个字体大小的BITMAP即可)8 r  k: Z" S7 z$ F& j2 i5 k, j
    BITMAPINFO bmi;
: N" ]4 R" a) p' X9 T( m( g* o$ d    ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
; T& A! V, X: o* Q9 a    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
& t% O# y+ H0 a; q' ^9 \    bmi.bmiHeader.biWidth        = _TextSize;, `0 Y) `8 M8 x, j% y! k: d# [
    bmi.bmiHeader.biHeight        = -_TextSize;
) X3 S$ f3 j+ k9 H' W3 V4 P$ f    bmi.bmiHeader.biPlanes        = 1;: N% Q, A4 H2 Y4 q- L) R
    bmi.bmiHeader.biBitCount    = 32;3 N; Q7 B* r% G% x
    bmi.bmiHeader.biCompression = BI_RGB;+ T( E8 `! W% r% i/ U7 P
    ; U- m0 u: P# j
(这里需要定义一个指针指向位图的数据:: F; f) W3 ]. x+ z6 _+ Z2 [8 a# \
    DWORD *        _pBits;            // 位图的数据指针)
( y9 Y8 f! e7 ^0 l1 r7 I
5 R- }. Z0 b' T+ ~3 ?) K    _hBmp = CreateDIBSection( _hDc, &bmi, DIB_RGB_COLORS,
% x7 D2 z. }4 c0 x        (void **) &_pBits, NULL, 0 );8 v- b6 x$ P1 y( l6 K8 O3 i
    if ( NULL == _hBmp || NULL == _pBits )
% \" ]& K- ]$ {5 z, E4 f( I    {, [1 B5 b/ \( J3 `6 T# }6 \
        DeleteObject( _hFont );
6 x" m' R1 S: f7 z  N        DeleteDC( _hDc );
% u+ |4 e. ~% U. ?$ |7 X4 f        return false;3 f9 ~& Q6 ~5 O. x8 P  O
    }
6 V- D& Z# R8 q% v+ _4 E   
0 g+ E, @) M+ I) j5 U4 T/ Y2 I    // 将hBmp和hFont加入到hDc3 N8 M, @2 L1 r. S3 p
    SelectObject( _hDc, _hBmp );+ A4 |" s4 z# X6 |- B
    SelectObject( _hDc, _hFont );3 t+ g+ @( A& Q& B& Q

! V" C! L$ e) t  g接着设置背景色和文字色:# Y% C! w& J8 b, i. c2 ?7 r+ G
    SetTextColor( _hDc, RGB(255,255,255) );
6 N$ E& d0 m6 m2 M    SetBkColor( _hDc, 0 );
4 R  W% w/ t& e1 u1 ]2 ]& ^+ r2 A
2 k5 ~$ ~& \1 J( I9 c设置文字为上对齐:; v2 A6 f% }- E3 |* x: y" L
    SetTextAlign( _hDc, TA_TOP );
, H2 P9 N- E5 l, W* t
2 [0 e# L% D$ K6 P创建Texture所需要的顶点缓冲:
, H( F# e4 X6 Y: ]7 V5 w3 L! _3 t) f    if ( FAILED( _pd3dDevice->CreateVertexBuffer( _Max * 6 * sizeof(FONT2DVERTEX),% _) F; b3 @* {9 p1 ]
        D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,# B* ?7 M$ w( d
        D3DPOOL_DEFAULT, &_pVB ) ) )' Q* A$ y3 s! _' j& \  F8 w8 P
    {
: P( ~* w- _5 ?: h; c        DeleteObject( _hFont );! N8 n- ^+ U6 m9 k
        DeleteObject( _hBmp );9 A0 u1 f4 ]) r( v; k+ ?8 v6 Y. M
        DeleteDC( _hDc );
# ]: N' }% n$ H2 \        return false;
# F7 p, c$ W6 Q6 f0 Y6 t    }
8 M" l/ Q) ^+ Q: M% m2 ~" B   
) Y9 B% v4 j; B/ @; D创建Texture- j4 ]' L+ v% `3 L+ V6 ^& K4 ~, X
    if ( FAILED( _pd3dDevice->CreateTexture( _TextureSize, _TextureSize, 1, 0, 8 t6 Y  C2 l' G1 s/ A( Z
        D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &_pTexture ) ) )  O8 Y8 @: O4 u/ l0 T3 n4 e
    {* B" }8 H& x% Z, ?% ]- N7 z& O
        DeleteObject( _hFont );3 B2 L7 P3 h! k
        DeleteObject( _hBmp );
& P, o% C2 [$ C5 q% x; L, S        DeleteDC( _hDc );
+ r* ]/ S/ l2 o        SAFE_RELEASE(_pVB);, t9 P7 ]$ n# }$ e: ]' N$ z0 S
         return false;
7 v6 j  ~- [% ^% S$ h5 a    }
: v7 ^2 F2 B6 d0 U# x  u3 _; b: ?1 g$ |% A  u7 F
设置渲染设备的渲染属性:
- v  O2 U* r2 D( K. o) \; \  |* [    _pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE,   TRUE );
6 Z) c, k* ]+ K: n, b& ], Q    _pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );) X0 ~" [5 A2 d4 a( L
    _pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
! r" M" p. u2 W( k1 n% j- s    _pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE,        TRUE );. q( g! |2 ]$ F" X' i
    _pd3dDevice->SetRenderState( D3DRS_ALPHAREF,            0x08 );: Y7 S8 i; I+ Q8 _
    _pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC,            D3DCMP_GREATEREQUAL );* t) h6 r0 e3 o
    _pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,    D3DTOP_MODULATE );
; ~( h- S6 N0 c# Y7 _' z        
; e% M; k1 V. y/ _( @0 p7 q( v; A% U    _pd3dDevice->SetTexture( 0, _pTexture );
! A$ u( n" J% B    _pd3dDevice->SetVertexShader( D3DFVF_FONT2DVERTEX );' E1 l! M& V- ^: a5 S
    _pd3dDevice->SetStreamSource( 0, _pVB, sizeof(FONT2DVERTEX) );. B# a7 C/ n# ]% g) F

% V5 c7 @  E+ j2 O/ s设置缓冲的最大容量
5 U3 G& ]# t) s& z6 Q! `    _vBuf.resize( _Max );
4 E+ `( q2 O( P) N: M& V; R& f
$ D$ s! ]4 G0 i' t# W这样,初始化完成了。接下来是如何把一个汉字写到Texture中,以及如何进行管理。定义函数:% s6 q1 N- Z5 I4 ]: P+ x
// 得到文字在纹理中的位置
9 G  P" R- P( Q2 \& R8 H, qvoid CFont::+ }5 @- }- q! A0 O( o  r( E
/*-------------------------------------------------------------; w3 [7 {2 p7 X( `) X- m2 E6 [& l
char c1   ---  文字的第1个字节
4 j1 w4 ?8 v+ f- nchar c2   ---  文字的第2个字节
+ n2 F0 m6 h# ^# aint & tX  ---  写入纹理中的坐标x
9 O, k3 A; ^1 }5 Pint & tY  ---  写入纹理中的坐标y5 D  h! Z: M7 ?$ Q
-------------------------------------------------------------*/* T% m+ J4 S0 o, @7 P
Char2Texture( char c1, char c2, int & tX, int & tY )- s7 g9 W- H' S% A2 V9 ?
{3 v) ?( Z) l4 W( G
    WORD w = MAKEWORD(c1, c2);        // 把此字变为WORD  x/ G0 k( h; L% H/ V
    vector<Char>::iterator it = find( _vBuf.begin(), _vBuf.end(), w );/ s  r" \( m  A
    if ( it == _vBuf.end() )        // 如果没找到
2 I2 n8 w! ]  F6 q2 @    {
( \+ {  r6 p" B( j3 M% w, u0 l        it = find( _vBuf.begin(), _vBuf.end(), 0 ); // 查找空闲位置
1 J# `8 |- d5 e0 k$ c        if ( it == _vBuf.end() )    // 缓冲已满5 W  g( k/ |/ ?" s2 q5 Q
        {
" l9 e4 o  y4 V            for(; it!=_vBuf.begin(); it-- )" P! J; `% s7 U0 i2 Y. d7 B
            {
9 _* g9 O2 o7 U8 r% _- I. L' |5 U, I                it->hz = 0;
7 e$ b* C5 k8 z  J: E            }
$ \- u/ w+ |- {1 X//            Log.Output( "字体缓冲已满, 清空!" );
; S9 R/ H, B/ n, R8 I        }
3 v. c( S# ?9 C  c0 E5 R" t/ K! x
4 P3 G; _3 D& ?' p" }' F) l        // 计算当前空闲的Char在缓冲中是第几个
! i  j/ j) J* N7 ^: g        int at = it-_vBuf.begin();( b: _  v7 Z) p6 m" N, U
( w; t/ b$ q( ~4 ~6 A/ ~
        // 得到空闲位置的坐标
1 u: m  x- F9 N% F3 o1 g2 ^        tX = (at % _RowNum) * _TextSize;! t( J- f' E3 D; ?9 m6 x
        tY = (at / _RowNum) * _TextSize;
7 k$ v* M- h9 J- S6 e1 H$ @% S- X. o) }, M2 ]& `
        // 设置这个Char为使用中
8 ]- z9 g. H! f. |( s  I6 M        (*it).hz = w;
- g5 n/ h9 T0 ^9 x5 h. h
% h* E5 f' [% A7 O6 y2 F8 w  B        RECT rect = {0, 0, _TextSize, _TextSize};
2 I( N" ]1 q: J& y* q/ ?3 k5 }        char sz[3] = {c1, c2, '\0'};' O* I: h; o4 m. B- D8 F9 h; m
        // 填充背景为黑色(透明色)8 `: n  O3 `6 ?% p* {
        FillRect( _hDc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );7 [2 i* ^4 F( Z3 H! }* X
        // 往hBitmap上写字
8 t% U, G$ k0 d2 M: r* [1 z# U! R% s        ::TextOut( _hDc, 0, 0, sz, c1 & 0x80 ? 2 : 1 );8 Y0 j0 g6 K7 O7 [$ C/ X
        ! T4 G. N0 {1 I
        // 锁定表面, 把汉字写入纹理, 白色的是字(可见), 黑色为背景(透明)
2 Z$ q7 k; \; C/ l        D3DLOCKED_RECT d3dlr;
+ w  {: w6 F5 W) c- Q# ?        _pTexture->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);
& P+ D6 l; }9 }" I        BYTE * pDstRow = (BYTE*)( (WORD *)d3dlr.pBits + tY * _TextureSize + tX );5 s; [; Q5 s2 f" Y4 \( C! g# \+ p4 y4 u
        * F; V- [" H/ z4 t* {4 L
        for (DWORD y=0; y<_TextSize; y++): `' Q/ b. I% h! N4 l# M2 t
        {
3 c5 F/ N' x) K4 q8 |3 c            WORD * pDst16 = (WORD*)pDstRow;4 d+ K4 y* u7 b6 O' u& x8 M( c
            for (DWORD x=0; x<_TextSize; x++)
. }1 o5 L: f( |6 W            {
5 B, F0 j* G0 S" ^* w! g                BYTE bAlpha = (BYTE)((_pBits[_TextSize * y + x] & 0xff) >> 4);
9 ]6 A5 L  N/ h; X, V                if (bAlpha > 0)
7 \' L6 W4 |8 j  G: d* D% S                    *pDst16++ = (bAlpha << 12) | 0x0fff;
. V: i* W& N* Z- h                else4 O# r+ X5 k" l% ?; ]) {1 m
                    *pDst16++ = 0x0000;9 ^* k. y/ E* ~3 d* `+ N" M
            }  `3 C& I! q. o2 ~, _' ~  B* ]8 G6 p
            pDstRow += d3dlr.Pitch;3 b) f2 ^. @0 h# D
        }
4 W6 ?* b( @% }- q( @/ o        _pTexture->UnlockRect( NULL );7 z4 h+ ?6 {3 ~& f+ F
    }! v0 g( q/ x  ^; }1 w
    else
6 o5 W+ D  y7 g; W    {
! Q3 w7 u2 f- j7 }        // 计算当前空闲的Char在缓冲中是第几个
# C. y6 r3 `$ a6 N+ w; _        int at = it-_vBuf.begin();: A: S& K% K0 M! S

2 M5 M1 B# E% x/ c        // 得到这个字的坐标
$ b& }- }! B( K# a        tX = (at % _RowNum) * _TextSize;
( n! M3 `8 u" D( S! F2 _        tY = (at / _RowNum) * _TextSize;9 ~4 g* k6 Q& H5 F3 v) t
    }
2 a( H$ K6 j, P% Y9 [}" N! S8 k, Q, A1 f
以上代码中的注释已经很清楚了,相信无须我多言。这里唯一需要声明的是:原来所定义的Char结构是这样的
% A, o6 m% H# E5 G/ }struct Char{5 D5 R$ H* |" u6 J, N
  char hz[3];   // 保存汉字
- `/ e8 \, Q+ f0 [/ t  int frequency;// 使用频率
5 T. Q! b- G: }, N$ [1 l  RECT rect;    // 这个字对应位图的区域7 Z- L1 k8 B  w+ Y1 V, A
  Bool isUsing; // 是否使用& w$ `2 i) S7 ^
}
$ n; u6 V5 F( K5 A* g0 V) \后来因为将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。
% B1 u6 o4 s$ D8 ]/ l8 H然后对于RECT rect,因为没有了int frequency,意味着一旦将汉字写入到Texture,其位置就不会变动了。所以,很容易根据find函数操作后的iterator,直接计算出这个汉字所在Texture的位置。这样,RECT rect也不再必须。7 o9 y) j7 t, D% ]$ g; J: k
而bool isUsing,它本身就是个鸡肋,要也可以,这样结构更加清晰。不过,直接通过观察WORD hz为0或非0,即可实现isUsing的作用了。
+ d" X" B: @6 @: R: X为什么要对结构Char这么精雕细琢呢?
1 C, [2 z! N5 w1.    既然没有必要的东西,就应该删除  h4 _; m1 B: H9 C' m( l
2.    Char结构的大小越大,vector所要求的内存越大
9 h% W. d, @6 v( N+ X* i3 n  S3.    小的结构,find可以更快地查找出所结果
) l( |% M+ b: K& O为什么find会正常工作呢?这里我要大概地讲一下find是如何查找出所需的位置的:它只是简单地使用while从vector的begin一直遍历到end,逐个判断,直到找到为止。find要求必须实现自己的operator ==(),进一步跟踪到find的源码中,发现也是这样。于是前面的结构Char变成了现在这样:
0 j  Y" q0 l. u/ ]/ r: V' {1 p1 Z; I    struct Char{( b, P- I- p( r8 l
        WORD    hz;                // 文字: O0 B( f' \% ?; f# X
1 l( y% Q+ o9 H7 D% x
        Char() : hz(0) {}3 u- V- m5 W! j! E- E
+ e# [$ A, L5 Q# N6 C8 c) e7 s
        // 用作查找文字, f; h* m# b: G. u- n" H
        inline bool operator == ( WORD h ) const
; z9 }% H% W3 }$ Q  l" Y        {$ l9 ^# ?" r" V' \+ W
            return hz==h ? true : false;
, D0 D" H7 A0 Z2 a0 o        }
5 o$ g/ X, k, C  r7 i    };4 B- F! ^3 f. \# V# [* v2 W+ v
是不是很简单?^___^
8 w0 y) m  S. `( Z
2 Z  z( d) C0 b$ L终于到了显示的函数了:+ Z, S( c+ {+ D' Q. f4 p
// 得到文字在纹理中的位置
) k' u3 Z! w: w3 _$ a+ {8 f( Ebool CFont::( o- a2 V/ o" `8 K
/*-------------------------------------------------------------8 y) M+ K: w1 x8 U1 n, ?
char szText[]  ---  显示的字符串. P0 E( T0 X* F8 T, b
int x           ---  屏幕坐标x
) c. z. Z5 u% V# Vint y           ---  屏幕坐标y
1 q' Q: d- ^( y+ A/ h8 oD3DCOLOR       ---  颜色及alpha值
4 ]/ R; `" m7 O% {; wint nLen       ---  字符串长度
0 K; N7 M# u7 M1 W: t, i% Ffloat fScale   ---  放大比例& b, D$ z  x4 `  U4 N' C
-------------------------------------------------------------*/9 ?; v4 _( m9 T& K+ E+ ^8 h
TextOut( char szText[], int x, int y, D3DCOLOR color, int nLen, float fScale )
1 E. T; J/ E/ Q{  l, E/ q4 Z1 n: \5 m+ ^
    Assert( szText!=NULL );; D" Y7 V, {/ A) q) ~: s

% ]* F+ d' e) T7 F- }5 J; }    float sx = x, sy = y,: x1 `; p2 o& s# u- f
          offset=0, w=0, h=0, tx1=0, ty1=0, tx2=0, ty2=0;
" z3 w) o  F$ P$ g7 V& X2 r) h7 o    w = h = (float)_TextSize * fScale;! G* _/ Z& \4 X7 R: `# ~$ {
; W5 W/ M/ L6 h
    char ch[3] = {0,0,0};
/ o$ ^! u' \" @3 M7 g6 U    FONT2DVERTEX * pVertices = NULL;
# z- a, v5 ?% z1 G$ ]/ S! U- S    UINT wNumTriangles = 0;) q0 e. q. |) }* ~  U' L
    _pVB->Lock(0, 0, (BYTE**)&pVertices, D3DLOCK_DISCARD);- d$ f4 e7 a8 }8 D( h% _. ]
- ^& e# s: f: @
    if ( -1 == nLen ||                // 默认值-1
5 e& A; H/ k) ^. ?0 \4 U# d# H) z  H         nLen > lstrlen( szText ) ) // 如果nLen大于字符串实际长度, 则nLen=实际长度
/ N0 C. @4 O8 _; c        nLen = lstrlen( szText );
( G0 Z1 k" D9 x" T9 \    for (int n=0; n<nLen; n++ )
9 ^" c! a# Y3 O; N0 E+ K    {* B, }; @) ~3 W' c0 V8 a$ r% }
        ch[0] = szText[n];* H. p! l& s# w" n$ I; w5 B

  m7 f$ q9 F4 @, i- U! ~        if ( ch[0]=='\n' )( l$ ?+ p! z8 B# @
        {
) _+ x% T4 H2 A" A) _) z" _% n            sy+=h;
$ Y* G0 \  |4 u8 d* a0 _: k            sx=x;
* S3 _5 w! F; o& B  Z' [+ `            continue;
' Z* i/ R% r  B) `        }
1 w, g' E: c" l
0 |& @! ^& X# b6 f7 t        if ( ch[0] & 0x80 )
  |% O6 H1 A5 u3 N# f7 z. \9 E        {
/ T- O5 e; \, ]- z. o            n++;
) ~- `; i1 c1 ^& K( e            ch[1] = szText[n];
. k% r; D/ X: r2 L$ A5 i            offset = w;
" Z, e. u; Y6 g5 g( p        }
3 E' Y& Q, T4 G: y9 l        else3 E; L  w  R4 a
        {$ P9 q8 D7 p( {; D. a$ ]( E  m+ [
            ch[1] = '\0';! ]% T( H; V6 y, R4 k
            offset = w / 2 ;
+ Z, n' j- s) w& D: D1 e        }( q% d. E3 ]: g' a! h2 ?

4 N4 y& N* t/ j        int a, b;
2 ~9 q- i! p$ h/ [0 q. T        Char2Texture( ch[0], ch[1], a, b );  m; f" f; z! S
   
: @8 k  Z8 v  _1 ]        // 计算纹理左上角 0.0-1.0
& W) v5 H" Z7 u$ b/ x5 D1 S        tx1 = (float)(a) / _TextureSize;
4 d8 C' K; R$ c+ H3 v# [+ }        ty1 = (float)(b) / _TextureSize;
5 K6 H& g1 d  |1 u- f        // 计算纹理右上角 0.0-1.0
' s5 v& p# N  ~5 F6 V. P* P        tx2 = tx1 + (float)_TextSize / _TextureSize;
' c0 `) C/ H' t9 r+ }' q        ty2 = ty1 + (float)_TextSize / _TextureSize;/ p* N. }. Y* ~& {0 i! W

# h7 c  ~( w; T0 v' t! m, u        // 填充顶点缓冲区4 w' w0 Z0 R+ ^" H
        *pVertices++ = FONT2DVERTEX(sx,        sy + h, 0.9f, color, tx1, ty2);
+ _& }) u: Q0 V, t+ B/ Y6 [: t        *pVertices++ = FONT2DVERTEX(sx,        sy,        0.9f, color, tx1, ty1);5 W0 g& e" `; p1 a4 S
        *pVertices++ = FONT2DVERTEX(sx + w, sy + h, 0.9f, color, tx2, ty2);
5 S$ I/ {( c, c) K        *pVertices++ = FONT2DVERTEX(sx + w, sy,        0.9f, color, tx2, ty1);* Q/ P; \. x7 k, y
        *pVertices++ = FONT2DVERTEX(sx + w, sy + h,    0.9f, color, tx2, ty2);; t7 N* v# ]( C6 h/ O  h
        *pVertices++ = FONT2DVERTEX(sx,        sy,        0.9f, color, tx1, ty1);, l8 T) k' \, `
! e# Z- l4 d. m
        wNumTriangles+=2;, K$ N8 r# c& W% O0 S
; ^& V6 g$ M" u7 q# ?0 _; w9 B- Z
        sx+=offset;    // 坐标x增量
; p9 V* G0 y2 t- g4 ]% w    }
9 Y) n- A; k+ d3 q: E# k3 c3 i    _pVB->Unlock();
2 x# Z4 Y. Z# ^" Y. z' A$ z" S    _pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, wNumTriangles );
: ^  Y4 Y  o+ O
& K% D+ l$ o# y3 x1 x% T    return true;$ P2 h" {& p% g( r7 h" `
}
. e3 z) n: K6 o% ]# U" c结束语
$ \7 H/ y/ Y: h. w% s6 _# _# n. I记得有一句名言: Keep it simple and stupid.在实现功能的同时,保持代码简单、清晰是非常重要的一件事。相信在往后的日子里,在不论是别人阅读或是你自己回顾的时候,你都会发现一如既往地遵守这个守则,是多么得重要!
$ k6 t: e& P3 W6 G* V相信通过上面我那无数的废话,加上代码中还算足够的注释,聪明的你一定能够明白这其中的原理了吧。如果以上的内容还不足以让你完全搞清楚的话,你可以登录我的主页:
5 W- n9 X% b# I炎龙工作室
& V4 V% g. Z4 B3 W$ H. i  I0 Z; {4 u上面不仅包括了上面所写的程序代码,还有一个用来演示效果的一个很简单的demo。+ n& K+ O" d3 Q5 t: V
说明,以上所实现的CFont是包含在我的游戏引擎中的一个部件,而目前已经实现的部件包括有:
$ c: i8 E. h- h; d1.    CGameFrame(游戏框架类)  -----  封装了窗口及D3D设备的建立,需要派生出自己的子类8 K$ I2 g+ P- z& m, j" o
2.    CAudio和CSound(声音类) -----  支持wav/mid/mp3的播放
; j$ S8 V6 V8 f. P% H$ K3.    CDirectInput(控制类)    -----  键盘、鼠标操作
7 ?# Z3 B+ Q/ X$ O3 X& b4.    CDirectShow(视频类)     -----  支持avi/mpg/mov等的播放, H( e9 i$ S" p+ g6 g9 u7 H7 }5 K
5.    CSpriteX(精灵类)        -----  方便游戏中对精灵的控制
/ {0 E; S( Q" h& c) D0 a& S& F6.    CFont(字体类)           -----  中英文字体的显示
% p- y- f: X' M9 }( @7.    CTimer(时间类)          -----  高精度时间的控制
; R0 ^6 g9 p4 B) D8 Q8 W* l8.    FPS(fps 类)             -----  fps的计算9 E- G! F: W+ _$ V
9.    LOG(日志类)             -----  游戏中的错误反应以及状态记录
4 s1 r" \0 B, q- f& J% M% m& G5 x最重要的是,这个Game Engine完全是开放源代码的。关于更新的情况、版本说明以及源码下载,请随时关注我的主页!
$ o6 s* ~/ q! n0 e5 F' ~接下来,我将会继续完善这个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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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