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

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

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

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

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

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

讲了一些TTF的知识,也许对汉化有用# b" i1 L( Q. E, H; z( m( Y% c. V, N
4 M  `3 F: f& q( M

0 v6 W+ D5 F8 x! u8 D游戏中汉字显示的实现与技巧# |! c' H2 E: v- Y. S* ^* ~
作者:炎龙工作室 千里马肝
2 J7 k( f' s3 }  b2 U( f版本:v1.0) ]) f" q# f- h/ j6 a  Y$ u
最后更新日期:2002-3-30
& x; {% a2 C4 J2 c! i/ a: R绪言
, Y% G2 y" ]1 p: \4 ~1 D6 x在游戏中,因为我们是中国人麻,通常都需要显示汉字,比方说交待剧情。而对于文字的显示,英文的显示要较其简单得多,因为只有26个字母,就算再加一些标点、符号什么的,用一张位图,就可以足以显示所有的单词了,而相关实现技巧,也比较轻松。
+ Y7 k4 f" r4 B; _7 D' U6 X而中文的显示方法,要复杂得许多。记得原来在DOS下,汉字的显示都是读的UCDOS的点阵字库,而点阵字库的读取方法,在UCDOS SDK中都有源代码可以参考。但是自从Windows操作系统开始,我们开始了解到一种更好的字库,它就是TTF。
) \2 Q1 C/ t; x注:以下我所指的开发环境,除非明确说明,默认的平台是VC6.0+DirectX8.1,使用D3D来加速2D。然后使用的STL是用的SGI实现的那一套STL。6 }! g7 N! x+ C! _% Q" r$ |
点阵字库
" P/ d, _  G5 E7 N9 G包括现在,有很多游戏都还是使用的点阵字库。因为操作起来比较方便,加上这方面的经验已经积累了好几年了。通常如果只是一种字体就可以满足需要的话,它会是一个比较好、快的解决办法。但是它有3个缺点:
& Z9 K. q& H! P( M1.    如果放大显示,不做处理的话,显示出来的汉字,是很难看的。  W) l; V0 f. m  Z$ i  z
2.    像是UCDOS所提供的点阵字库,只有24点阵的有几种字体,如:宋体、黑体、揩体…,而16点阵的好象就只有宋体一种。
; `$ w7 ]- I0 f5 }: w3.    点阵字库,通常是有版权的,尤其是第三方制作的汉字库(如:方正)。  F0 r, x' n/ }2 ^' I/ |) x
在这样的情况下,当我们写好这样的一个显示函数,就算是解决了如:放大、快速显示等问题的话,可供选择的字体还是太过于局限了。所以,在字体的要求比较强的情况下,点阵字库并不是一个好的解决方法,他不够灵活。尽管我们对于它的操作是如此得熟练,可以写出优美的代码来展示我们的编程技巧。
$ O/ E# u2 K) M+ \TTF5 ?  p- }6 o! p) l5 }" I* F$ x
TTF是True Type Font的简称。在Windows\Fonts目录下面,我们可以看到许多后缀为ttf的文件,它就是接下来我们接下来所要谈到的。- a8 q+ I7 S! J! J/ f
TTF是一种矢量字库。我们经常可以听到矢量这个词,像是FLASH中的矢量图形,在100*100分辨率下制作的flash,就算它放大为全屏,显示出的画面也不会出现马赛克。所谓矢量,其实说白了就是用点和线来描述图形,这样,在图形需要放大的时候,只要把所有这个图形的点和线放大相应的倍数就可以了。而且,在网站上有很多的TTF字库可以下载,或者你可以去买一些专门的字库光盘。然后在你发行你精心制作的游戏时,可以顺便捎上这些后缀为.ttf的文件就行了。包括Quake这样的惊世之作,也都是用的TTF字库。# E: ], t! O, ~: \0 J% X0 Y2 e# y
这样,我们就可以解决点阵汉字的一些问题。通过TTF,我们在字体的质量和字库的数量上获得了暂时性的胜利。1 k0 X# X( T  o. F
字库的读取和显示# I2 v9 M& d2 f8 o% y
先前谈到点阵字库,只需要很简单的一些操作,就可以显示出想要的汉字。下面我给出一个读取hzk16的函数,它需要一个Surface以供显示用:. ^$ W$ M5 W# T
#include <io.h>
7 T( D! r; A$ [6 Y#include <stdio.h>
0 A" k8 [  ]1 k$ L, T  f( q#include <conio.h>
# j. T* S; a! n! h) [: F0 D
0 U/ W9 e+ X- K& a0 z// 读取16x16
# R/ }5 O" k9 c. }+ M1 g0 a3 Pvoid DispHZ16(int x, int y, BYTE *Str, LPDIRECTDRAWSURFACE surf)2 @1 d. h% n! h
{* W5 Z  x0 s3 l: U5 g4 d6 M7 X
    const int Mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };+ {( I8 S. d% A: J3 [8 H8 n
    FILE *HzkFp;
( E3 e& j/ }2 V& A    WORD i, j, k=0, m;
; X7 [8 T3 `" ]8 {8 L0 Q    WORD HzNum;
, @: i; o* Z/ O) b4 j; R7 E6 J    WORD QuHao;* U: ?/ m, K, {9 q8 S
    WORD WeiHao;
* J  R# }; {( N; B+ A; M* P    long offset;. k" c  w3 W9 ^
    BYTE dotBuffer[32];    # ^4 i1 W' s6 B% L3 _& g/ q
. \9 z! q" ?8 R7 P4 M# K5 F+ @
    HzkFp = fopen("HZK16", "rb");
4 h: g0 i. y& ?8 B% K% R- s" y  h    # [% W; I6 C" e7 k
    HzNum = strlen((const char *)Str)/2;$ H6 f5 l/ I* N7 K% |

% H4 r5 h5 u. p& k" x    DDSURFACEDESC       ddsd;
0 z) |' h* y: H5 E    LPWORD              lpSurface;
  W9 ^' \: q, `3 N) Z5 G    HRESULT             ddrval;' G: _' N' u, M4 q: v( e5 V6 O
    " u4 B! J6 e5 {9 K8 v- E
    ddsd.dwSize = sizeof(ddsd);0 V6 I1 x* \6 \+ L7 l1 ]# R
    ) I, W: K: p& w" E
    while((ddrval=surf->Lock(NULL, &ddsd, 0, NULL))==DDERR_WASSTILLDRAWING);& r5 S: h: z) W# c! S: {
    if(ddrval == DD_OK)6 x( O( v7 m5 u  R5 o. @: X; ]' r+ P
        lpSurface = (LPWORD)ddsd.lpSurface;
* w, K; K/ u) k8 {- V* d
7 `" X2 H# e5 E1 X4 n; O    for(i = 0; i<HzNum; i++)& m1 M# P# M& U- h3 s) ^# J' J
    {    5 Y2 E% ]+ b8 n
        QuHao = Str[i*2]-160;+ M5 }8 U3 o/ F9 G* r5 I' c
        WeiHao = Str[i*2+1]-160;
  x" O& C/ W* T2 b$ V, X
# O' {) Y" O& m% m$ n1 X0 D* ~        offset = ((QuHao - 1) * 94 + (WeiHao-1))*32;: t! w6 n+ a9 k! O
; ^, Y3 i& C/ b3 v+ \6 Q0 x
        fseek(HzkFp, offset, SEEK_SET);6 ?9 x8 l$ ~/ a- v
        fread(dotBuffer, 32, 1, HzkFp);5 c4 O" T7 M$ \6 e. _

# [7 g  b9 y6 C- o! d+ O0 ]        for(j=0;j<16;j++)
2 c# L% d% l. @4 F, {. l: `: K            for(k=0;k<2;k++)
! a. {$ [! Y5 a                for(m=0;m<8;m++)
& Y9 Y- y% v8 ~$ L* S, D                    if(dotBuffer[j*2+k] & Mask[m])
; G' o# q. c! I. W: r! t+ Q0 {8 F# R                    {9 g2 i2 P# r" @7 r/ L! J5 Q  f
                        lpSurface[ddsd.lPitch*(y+j+1) + x+k*8+m] = 0x000000;
, ^& Y3 v7 u2 t' @                    }
/ v" k6 ]! t/ g8 B. Z9 y! m        x+=16;7 j% c" y4 I4 W/ ]
    }: @0 ]1 H' d9 _2 P  P
+ ^( g. x( Y  N$ W2 h
    surf->Unlock(NULL);: F- r1 M# B/ m& _; @$ n+ K
4 @' V3 p: W4 a
    fclose(HzkFp);$ `+ a; k# c6 u* e& I" J
}
; Q6 z% P, L- B% S其实原理很简单:7 W- W# g6 ], o9 \
1.    打开字库; }1 s- i6 V) u( E
2.    计算字符串长度(这个函数只支持中文),并且Lock Surface6 Q7 I0 U3 K, w. h" `
3.    依次计算出每个汉字所对应的区码和位码(汉字的第1个字节是区码,第2个字节就是位码),然后通过公式计算出这个汉字在字库中的偏移量:
* P* o0 S( y( c9 {$ [' T" Goffset = ((QuHao - 1) * 94 + (WeiHao-1))*32;, L. W8 N% L; W1 j7 }; f& Y0 W
4.    读出一个32个字节的点阵
! |0 \$ o; |) }6 n; r/ j; X5.    绘制到Surface上' D) w5 j5 |; Y% c) e
以上只是16*16点阵字库的显示方法,24*24的读取方法与之类似,大家可以参照相关资料来书写出自己的代码。1 b7 }. h% H3 F
5 ^8 F. U& L. L  f+ G
如何显示TTF字库呢,有很多种手段,下面我按从简单到复杂的的顺序依次介绍:0 J& q2 M2 T, q) R/ X2 J+ `
1.    使用Windows API,也就是大家所熟悉的TextOut。通过它,还需要一个HDC(设备句柄),我们就可以随意地在屏幕任何地方显示出文字了。1 S" c8 l! f- G) w* g% e
2.    在http://www.freetype.org,有一个FreeType的免费库,而且是OpenSource的。它目前有2个版本:1.0和2.0。其区别在于,1.0只能读取TTF格式的,而2.0支持更多的文件格式,在使用它之前请详细阅读所要遵循的Licence,以下是摘自FreeType2.0对字库的支持列表:. x' t" p. I5 E6 p6 u  k
o    TrueType fonts (and collections) 8 g6 t. k6 K. m2 m* }; U
o    Type 1 fonts
0 [; n; M8 `6 D: c  D$ b7 no    CID-keyed Type 1 fonts
0 l5 G% L$ Y, G# P, E1 Wo    CFF fonts ' b/ R+ d3 j5 Z
o    OpenType fonts (both TrueType and CFF variants) 7 f3 v# p6 ?" u1 Y  `
o    SFNT-based bitmap fonts
" A# K! x. O% l6 ?+ j* Fo    X11 PCF fonts * }3 H4 u; v$ U1 ?5 I
o    Windows FNT fonts
5 ?+ x$ k, ~3 v7 F  E8 r$ Y3.    自己研究TTF的格式,然后自己来操作。/ @' `  K  U6 x) B! h
3 r5 L/ C  {6 L8 p* H
....... ╮╮
9 O3 o, L1 Z* q/ {      \█/倒! ) _' K8 B2 W# v% H
      ●
4 J2 q' n! u8 x# i虽然我们想要把每一件事情都做好,但是也不是每一件事情都要亲历亲为。如果你非要这样,也行^____^,但是过不了多久,你就会陷入泥沼,到时候你会发现自己的热情正在慢慢被磨灭,什么叫做抓狂,相信你很快就会知道^_^。' S3 {. s( B8 Q, U' `+ V
2 J7 W* U* i5 S9 C: d
在有多种选择可以取舍的情况下,我们需要考虑一下,对比一下各种解决方法的优劣。
$ [0 J) X1 n5 \$ N1 n' |  f" z' q1 D2 z# F
在DirectDraw时代,我们都不自觉地喜欢上了GetDC,因为……多方便啊。可是现在已经到了DirectX8.1时代了(我要使劲地摇那些还沉醉于DirectX7中,为如何在使用alpha时提升那可怜的1、2个fps的朋友们:醒醒,该起床了!),HDC已经被M$列为禁用品。怎么办呢?是的,你可能已经想到了,我们还一直保存着窗口的hWnd呢,可以通过它来得到hdc,从而调用那些需要hdc的API,可是,这样做是更为愚蠢的,这样对你是没有一点好处的,不信,你就试试吧。有一句话,请牢记:要想你的游戏有更快的速度的话,请不要再去碰HDC了。+ L  b. f! r+ c" p4 F
我们非常清楚hdc是一个超慢的解决办法,它无法在我们的高速游戏中满60分及格。下面来看看FreeType,它更像是一个Service。它的解决方法是,先通过一系列的初始化和设置,告诉FreeType字体的名字和大小等,然后它会动态地申请一个Graphic,再把我们要显示的字画到这个Graphic上,你还可以把它保存为tga格式。不过我们最终所想要的不是这个,所以可能我们还需要从这个Graphic上逐点读取或者用CopyRect,然后再画到我们的画面上。其实它已经是很方便的了,可是需要你去学习如何配置和使用它,这是很花时间的一件事情,而且它最大的优点是可以跨平台,我们需要它吗?如果有一个更为简单的办法,像是如果Textout不是那么慢的话,就好了……
3 Y4 h4 I  e1 G在这里,顺便谈一下另2个字体显示类:ID3DXFont和CD3DFONT。可能早就有人会说怎么在上面的列表中没有它们?原因我会在下面慢慢地说明:$ l# Y; p& T* ^# a+ O: e1 N
ID3DXFont,它存在于D3DX库中,一个现成的字体类,不过对于它的处理方法……我实在不敢恭维,就引用一位大师所说的话来表达我的看法吧: 在内部实现中, ID3DXFont::DrawText()函数确实做了我上面讨论的工作,先建立一张GDI兼容的位图,把文本绘制到位图上,而后把位图拷贝到纹理贴图上去,最后把纹理渲染到屏幕上。这样你就聚齐了所有的龟速的原始GDI函数,还包括了一大堆的额外开销 — 最终,这个函数比原来GDI的DrawTextEx()函数要慢上超过六倍……3 o2 }+ v6 X# a9 f, f3 k4 Q3 X: f
CD3DFONT,是由M$在D3D的框架代码中提供。不过它只能显示英文,有很多朋友通过自己定制和修改这个类,来实现自己的中文显示。不过效果都不是很好。其实原理,跟ID3DXFont的方法差不多,不过处理方法要聪明了一点。
1 v1 A, F  B/ ~7 m分析与思考. i% j3 G5 H2 H
那么我们应该怎么办呢?通常我们会幻想,如果可以像处理英文那样,把所有的汉字都保存在一张位图里,该有多好。这样,显示的速度就不是问题了,直接可以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。& ?1 D7 {: _- H2 O. s
汉字虽然很多,但是常用的汉字,其实也就只有那么几百个。像这样的字:鬯、鞴,你一辈子会看到多少次呢?如果可以做一个类似于Cache的东西,保存着常用的那些个汉字,在需要显示的的时候,先在Cache中查找,如果有的话,就马上画上去;如果没有,就从字库中提取到Cache中。这样的话,在使用Texture来保存汉字的位图信息的同时,对于每个汉字,我们还要定义一个结构,然后用一个东西把它串起来,综合它们2个,也就实现了我们所要的Cache了。刚开始,我所定义的结构是这样的:, J) g, T! {; J* b. _
struct Char{" s2 U  {- _& i
  char hz[3];   // 保存汉字
0 Z  W) b' e: p  int frequency;// 使用频率% ~% ]; D0 Q6 \  [. B" X
  RECT rect;    // 这个字对应位图的区域
* p$ I' B! Z) c" E$ A7 \/ U  Bool isUsing; // 是否使用
6 I+ I. p+ }" {, |- P( H1 G% R}
7 w5 v% F0 H6 M% y1 u/ l/ g7 M* E' Z对于汉字和英文,我在这里大概地讲一下原理:汉字是由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 w6 o& v! `# @
接下来,对于使用char[3]来保存汉字,是否真的很合适呢?因为如果把它当作一个字符串来看的话,在查找时就需要使用 strcmp 来比较字符串了,这样一定是会影响速度的。如果不把它看作字符串(字符串的最后一个字节需要以’\0’结尾),只用char[2]的话,我们可以只是简单地调用宏MAKEWORD,把2个byte压成1个WORD。当把文字作为一个WORD来看的时候,这样查找比较时可以用WORD内建的==操作,这样要比调用strcmp函数要快得多。3 w( k" s4 S% P, u
int frequency用来标志每个WORD的使用频率。设想,如果一个字已经存在于Cache中,以后每对它调用一次,就让frequency++。这样做还有一个用意是,是否可以在一个合适的时候,以frequency为参照来对这整个Cache排个序,把常用的字放在前面。那么在显示时,可以先在Cache中查找所要显示的字是否已经存在于Cache中,如果有则直接显示,没有的话才需要采取某种手段将字加入到Cache中。一些常用的字(像:我、的、着、了、过……),使得显示的速度将会大大提高。$ c; R& n0 I+ p, C  x: U
其实上面说了半天的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上的区域位置。
" z- i* k" l! y  e: c9 N' F使用什么东西来把这n个Char串起来呢,一般会想到的是链表,原因无非有2个:1 随时有新的字加进来,而内存是不连续的 2 它几乎没有容量的限制(除非是内存用完了)。不过链表的访问速度是很慢的,如果使用像数组这样的东西就好了。仔细想想,在这里,我们用来存储的Cache,最大也就是256*256(理由上面说了),所以大小应该会是固定的。我们只需要在数组中的给每一个汉字加上一个标志,说明这个位置的使用情况。那么就使用数组吧,这样的话,访问的速度要更快一些,直接首地址+偏移量就够了,不必像链表,在查找时需要逐node访问。当然,我绝不会想到用new  Char来申请这个数组。因为这样做实在没有必要,请不要过于迷信自己的能力,在STL中已经有vector了,为什么还要自己写呢?^_^最后的一个bool成员变量isUsing,也就是上面所说,用来标志使用情况的。
) {: W9 z6 B; m- [& ]: Y- G实际的操作4 o5 E# q$ W6 z
上面考虑了那么多,我认为都是实际操作之前所应该有的。先谈谈如何显示吧,因为在DirectX8.1中已经将DirectDraw和Direct3D融合为DirectGraphics了。所以无法像原来那样了…………哦,实在有太多东西要讲了,我还是推荐几篇文章给你吧^_^:
9 w" ]' q( d- Z2 Ohttp://vip.6to23.com/mays/develop/directx/200201/Geczy3Din2D.htm* O" a5 q- Z' r. `7 J, E. x0 f
http://vip.6to23.com/mays/develop/directx/200201/GESurface.htm$ M' x/ i. @9 ~0 ~
http://vip.6to23.com/mays/develop/directx/200112/2DGtoDX8.htm, l+ t, h* K4 E7 ?0 Q, T
http://vip.6to23.com/mays/develop/directx/200201/DX8adv2D.htm5 V8 L5 t2 I" G( O$ r$ B
接下来,我会假设你已经具备了在DirectX8.1中绘图的基本概念了,所以在你继续往下阅读之前,请务必先仔细阅读以上推荐的文章。
+ c* R) h! r, G8 ]2 W" e( ^; U前面提到,需要一个vector来对应Texture上各个位置文字的信息,上面已经创建了一个结构Char,则这个vector的定义为:% P8 C  i! ?' m9 b
    vector <Char> _vBuf;                    // 记录缓冲中现有的文字情况. }$ Y$ ]$ n% y( O( q4 _2 x
首先,由于可以利用硬件的放大缩小机能,所以字体的大小精度要求不是很高,只需要支持16*16和24*24大小的字体就可以了。我们需要一个这样的初始化函数:6 y& A; S. H& A% O+ C& y9 x: o
bool CFont:: $ w. g- [: d8 u( I
/*-------------------------------------------------------------
( V8 Y  }( |3 t6 a5 _: N$ M  v0 mLPDIRECT3DDEVICE8 pd3dDevice  ---  D3DDevice设备
8 I. T, f/ [& s$ P9 ichar szFontName[]              ---  字体名(如: 宋体)
0 Q% G  E& p0 `  m. M# B7 T( gint nSize                      ---  字体大小, 只支持16和24
4 G  n% x$ _6 E- x1 B2 w# s9 B! ~+ zint nLevel                      ---  纹理的大小级别- n6 p* i+ k: A5 A  ?
-------------------------------------------------------------*/
4 y; j/ `; S. l7 i: O8 uInit( LPDIRECT3DDEVICE8 pd3dDevice, char szFontName[], int nSize, int nLevel )。' c- }8 v3 Y9 I$ N" l2 i
! w3 Q' z5 z+ E
在DirectX8.1中,由SetTexture(…)所贴的图的大小,也就是Texture的大小,是有大小限制的,长和宽都必须是2^n,而且位图越大,所花费的显存越大,这样留给其他显示用的显存就少了。所以,必须根据需求的不同,来自定Texture(也就是Cache)的大小。因为汉字点阵大小的原因,所以从实用角度而言(比方说只是显示fps或是短小的标题),开辟一个64*64大小的Texture,才能满足最低情况下的需要(这时如果选择16点阵的话可以存放16个汉字,24点阵可以存放7个,依次类推……)。
* C1 e: p! p, }' O5 H2 a" p根据设置,创建Texture:
4 I! h3 m5 U' z  U& Z2 b- k3 P    _TextureSize = 32 << nLevel;        // 纹理大小6 z5 D! X' g) t4 v* |
    _TextSize     = nSize;                // 文字大小
$ L2 \( H+ w% _/ n) j4 {0 e    _TextureSize = 32 << nLevel;        // 纹理大小
. L6 `6 N: d: M1 q# S: K    1 x. w# z, \9 t& }/ @$ |
    _RowNum = _TextureSize / _TextSize;    // 计算一行可以容纳多少个文字6 t% O  n; P, v/ h
    _Max = _RowNum * _RowNum;            // 计算缓冲最大值* y/ P( m* J5 N1 {1 M
9 \# s2 _  z4 _
创建字体,还是需要使用Win32 API。也就是先创建一个HDC:
9 Z8 I) Z) E' m' a& O% O, p1 F    _hDc = CreateCompatibleDC(NULL);# {) s' E1 J; X0 b" |

" ~9 K' w, j  X, I然后创建一个BITMAP和一个FONT,将它们与HDC关联起来。+ p! \) V  @8 I
    LOGFONT LogFont;* D7 A. t. c* \
    ZeroMemory( &LogFont, sizeof(LogFont) );+ S$ P7 @5 \5 Z5 u1 |4 U' N3 S
    LogFont.lfHeight            = -_TextSize;
7 z' U0 f* [2 B! F0 N    LogFont.lfWidth                = 0;
) k/ [) H5 F% H" d" D2 J    LogFont.lfEscapement        = 0;8 x7 z, @5 b! f: t: E
    LogFont.lfOrientation        = 0;. O, Y7 a( m/ d1 m  O9 U/ `
    LogFont.lfWeight            = FW_BOLD;
8 ]( P% ^! h! L: p    LogFont.lfItalic            = FALSE;
  [3 [$ F! p9 T1 O8 c8 p8 w  P# c    LogFont.lfUnderline            = FALSE;; q+ V0 K1 m8 A- a% `2 q
    LogFont.lfStrikeOut            = FALSE;( B" P" A$ i' v! \
    LogFont.lfCharSet            = DEFAULT_CHARSET;
8 I0 M7 q+ g0 M) f0 X2 {& B    LogFont.lfOutPrecision        = OUT_DEFAULT_PRECIS; ( g! S) \; ^- {5 ^
    LogFont.lfClipPrecision        = CLIP_DEFAULT_PRECIS;
; x3 C* ?. t( U. h4 P    LogFont.lfQuality            = DEFAULT_QUALITY;
6 k! N. I7 g9 c, l3 T8 i6 s2 N% r! p# b    LogFont.lfPitchAndFamily    = DEFAULT_PITCH;
5 \5 d" ]: k9 @' e1 J    lstrcpy( LogFont.lfFaceName, szFontName );" C, \5 ~4 C8 |) `
   
/ s% C3 l/ O" z9 l% x; T4 q. G, b3 n    _hFont = CreateFontIndirect( &LogFont );
7 A, k( n7 {7 T; t+ F4 }    if ( NULL == _hFont ): B, l" r/ I' }$ e* t+ p4 d- O
    {# z2 V8 D" a" R
        DeleteDC( _hDc );
& ?" H5 U6 b' Y+ [        return false;! e+ T! {9 d& K' @/ x5 D
    }0 O8 h: ?7 b! k1 @) b# F3 Z! z% B
   
7 K$ z6 a* x0 F9 B% Y2 M8 I(只需要创建一个字体大小的BITMAP即可)# g  U! x$ O) E5 Y: w6 S
    BITMAPINFO bmi;) D' x' d; A3 X4 q. }7 Y
    ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));- }- L  t4 e, Q
    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);8 Q1 D0 C  T: n/ a) r
    bmi.bmiHeader.biWidth        = _TextSize;
" H" k4 e. ]! k# D    bmi.bmiHeader.biHeight        = -_TextSize;' O- ?/ u" g+ m2 p( @2 x' H$ G1 l
    bmi.bmiHeader.biPlanes        = 1;% ?& X& `3 ~% b
    bmi.bmiHeader.biBitCount    = 32;" a) O5 N, j6 u6 N
    bmi.bmiHeader.biCompression = BI_RGB;( u5 G7 }! X+ m
   
% i$ l  L4 K; v0 h/ U: [" O  f(这里需要定义一个指针指向位图的数据:8 y) p& @* s7 l6 U; s6 ?3 h! E
    DWORD *        _pBits;            // 位图的数据指针)4 L* K" Y, ?+ x. \

, a) P6 T6 n+ Y% D3 T    _hBmp = CreateDIBSection( _hDc, &bmi, DIB_RGB_COLORS,
) ^6 U# ~4 S" M/ k5 B1 `        (void **) &_pBits, NULL, 0 );
/ P" B! a; F' f$ ~, t( b    if ( NULL == _hBmp || NULL == _pBits )9 ?5 j5 y7 x5 _  H: I0 u
    {
: Q" K. \6 E5 U( c        DeleteObject( _hFont );6 o( s7 s/ T' e8 m# l
        DeleteDC( _hDc );
/ [0 O1 |6 \- u: V        return false;
3 Y3 R* O( x1 [7 \) p+ G    }3 k8 m& G% N' d6 C, M) u) t
    & R+ w$ b+ ~' |2 w5 ?- N
    // 将hBmp和hFont加入到hDc
1 ^7 r1 {& M6 f) m3 r    SelectObject( _hDc, _hBmp );
7 N& z4 A! b' _5 P  ^    SelectObject( _hDc, _hFont );
2 Z) ]3 l$ G) Q" g) f* n5 F8 m4 p( q* k
接着设置背景色和文字色:
/ N( s( V" D, U% C    SetTextColor( _hDc, RGB(255,255,255) );
) U4 w  @5 f; @# o! h    SetBkColor( _hDc, 0 );
; p8 S2 s2 \( O" p" U
5 S3 W' [2 l1 L" a8 A9 w# Z0 G$ H设置文字为上对齐:+ t  P1 f( Z" i: e: z1 b
    SetTextAlign( _hDc, TA_TOP );
  W6 m0 D5 }7 i. S3 z9 E- @! }: n7 p- Y  r$ v
创建Texture所需要的顶点缓冲:
+ y! w) U. N4 [! |+ S  x) E5 O    if ( FAILED( _pd3dDevice->CreateVertexBuffer( _Max * 6 * sizeof(FONT2DVERTEX),& ?" F0 Q4 _+ I  Y2 H0 k
        D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,- n, F: R7 X2 g9 ?# Q
        D3DPOOL_DEFAULT, &_pVB ) ) )( h: D' @& M7 F" k" x
    {
: t( X6 C! ]% r6 s- H6 d9 A$ M        DeleteObject( _hFont );
0 Q8 x/ j, y) C* P8 C/ F% N) k        DeleteObject( _hBmp );8 g8 r: C$ T. y& k, G
        DeleteDC( _hDc );
& N  l6 \* ]( q) g8 K* K1 M        return false;/ L$ H) A& [7 F1 {4 J- m6 U
    }8 g! X( M: O9 z; f
    # n% @7 G( ]$ \! G0 e3 a8 [, |
创建Texture
% ]# Y* U! G% y; C- b    if ( FAILED( _pd3dDevice->CreateTexture( _TextureSize, _TextureSize, 1, 0, ! Z. T/ R$ w7 h& V  Y: R/ x0 t3 J
        D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &_pTexture ) ) )' q9 k5 S4 Z& j6 O
    {
8 y5 j8 y4 L5 m* j: \  a) a9 ^( ^' N        DeleteObject( _hFont );
( H3 A$ s# {! v& ~( [: P7 p        DeleteObject( _hBmp );
1 w1 U( a. o0 s6 ?/ C        DeleteDC( _hDc );
& T! E$ X, f2 y4 p) O& K        SAFE_RELEASE(_pVB);
1 f* T1 p- \# R& ~& _5 X         return false;
/ y% Z3 x4 N6 ~1 p& _! u    }
. k5 `; C3 S1 K1 ^( z! K6 W! c3 r8 O7 ?/ G4 D
设置渲染设备的渲染属性:1 S! }! A" m$ r) S
    _pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE,   TRUE );2 c+ h( q9 P- o# o3 y1 u
    _pd3dDevice->SetRenderState( D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA );+ z- C# Q" c" Q1 w! t
    _pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
# a. x2 H* D1 b/ d    _pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE,        TRUE );
' \" T" x! T. e1 Z! s/ y8 [, E0 b    _pd3dDevice->SetRenderState( D3DRS_ALPHAREF,            0x08 );  P/ L: k3 G. W% Y/ v. O
    _pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC,            D3DCMP_GREATEREQUAL );  }! q8 f3 {9 {2 G7 A$ ^
    _pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,    D3DTOP_MODULATE );
3 I3 h) Z) o) n/ ~( x        
8 U. ]0 B' ?* l) l# A/ t    _pd3dDevice->SetTexture( 0, _pTexture );% s0 F4 u" L1 \6 ?; x! _8 ]
    _pd3dDevice->SetVertexShader( D3DFVF_FONT2DVERTEX );3 u: W% ^6 Y6 N
    _pd3dDevice->SetStreamSource( 0, _pVB, sizeof(FONT2DVERTEX) );
/ w. F  N7 e- c- |$ o$ h0 R  y5 e" B' S% N  \
设置缓冲的最大容量! F. n' L* Z+ g% k3 m
    _vBuf.resize( _Max );. J$ B/ B2 ^* Q, [6 V1 E6 \) }5 m
& u% g; ~4 F* w# Y8 T% f2 o3 q
这样,初始化完成了。接下来是如何把一个汉字写到Texture中,以及如何进行管理。定义函数:( k6 [/ ^( b3 `
// 得到文字在纹理中的位置
1 I* C1 R5 X' n4 c: r3 ?void CFont::
5 \  R, [3 Z+ l/*-------------------------------------------------------------
% V. ?4 h2 w; W3 }char c1   ---  文字的第1个字节
) f; {8 u) x1 e/ s- Schar c2   ---  文字的第2个字节
* s  m' V" a; T" M  B, L* _int & tX  ---  写入纹理中的坐标x8 b8 v8 a  \$ \0 Z5 R5 I/ @" t6 H
int & tY  ---  写入纹理中的坐标y
+ N8 q  r1 k& Q/ B-------------------------------------------------------------*/
0 ^) c, m/ ~4 @' aChar2Texture( char c1, char c2, int & tX, int & tY )$ R# a8 e' \& ?7 {1 R" o
{
3 _' E# R; x" I    WORD w = MAKEWORD(c1, c2);        // 把此字变为WORD' I" V% E3 j6 h# A
    vector<Char>::iterator it = find( _vBuf.begin(), _vBuf.end(), w );$ r8 J. h5 T- K1 t2 }( r) @
    if ( it == _vBuf.end() )        // 如果没找到. x+ m# H- c0 p" _3 T
    {+ ^( a# n! j: }* M* @6 |, t  A; y, H
        it = find( _vBuf.begin(), _vBuf.end(), 0 ); // 查找空闲位置3 X% F& S9 p( A: z6 F; X/ c; P- a4 J$ T
        if ( it == _vBuf.end() )    // 缓冲已满
0 R+ S; p) \0 G+ A$ j. `& t8 U        {( k  |1 u% x" N3 @; h
            for(; it!=_vBuf.begin(); it-- )
* G" O: U3 k; c4 A, o9 p5 |            {- F; c; O$ S+ ?5 C$ g
                it->hz = 0;
5 P* E; y0 ?; u" y2 b$ C8 b" i2 B            }
+ Y$ h; @6 y- o% J//            Log.Output( "字体缓冲已满, 清空!" );
! n- p! N: G3 c! ]* Z        }
% k/ q8 M) S; w. j0 k4 }. \) {" u% b
/ p! J4 {  u( v0 n- w        // 计算当前空闲的Char在缓冲中是第几个
0 ?# x, r1 w$ D% o        int at = it-_vBuf.begin();
1 V; R' F, b( z% `) h
2 [+ ?! A1 A( |        // 得到空闲位置的坐标5 h) O  e0 D2 L: x4 _9 O* ^* C' e
        tX = (at % _RowNum) * _TextSize;
; U0 H+ z& f  R8 f6 o& w        tY = (at / _RowNum) * _TextSize;
! @, Q! Z) F1 m' p1 {. \. m2 c7 w, U) _2 R5 |" u
        // 设置这个Char为使用中
8 E- T: t: V& h; `9 K! Q        (*it).hz = w;7 M/ [5 m- N! ^+ O# k; B; e* U
4 u/ ^$ h% l! W; h
        RECT rect = {0, 0, _TextSize, _TextSize};
4 n- R. T+ \/ s0 u        char sz[3] = {c1, c2, '\0'};
* ~! h! D4 V' F8 j5 e9 Z        // 填充背景为黑色(透明色)
# r' S' V, c' e" @4 y9 h) }        FillRect( _hDc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );
! n; f4 V8 ]' `6 W        // 往hBitmap上写字# C# {) q, T( `$ U: `/ e4 g. w
        ::TextOut( _hDc, 0, 0, sz, c1 & 0x80 ? 2 : 1 );- K' F5 W6 c1 i7 {5 [
        
) d  O' |$ H$ _, M% E        // 锁定表面, 把汉字写入纹理, 白色的是字(可见), 黑色为背景(透明), y; Y8 [# E) ?( C  ?
        D3DLOCKED_RECT d3dlr;
( ?# a, k9 P" B) T$ `        _pTexture->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);# C  j' r  u) s4 g) v
        BYTE * pDstRow = (BYTE*)( (WORD *)d3dlr.pBits + tY * _TextureSize + tX );/ @6 ^6 I: p2 Y3 e9 m* h
        
  L+ M) {* G9 K& `+ `        for (DWORD y=0; y<_TextSize; y++)
- w" V/ ?* f( [' M/ P        {
0 k2 F2 s/ \, K% D0 m7 H            WORD * pDst16 = (WORD*)pDstRow;0 ^2 N5 g9 z4 c) F/ O- J- z
            for (DWORD x=0; x<_TextSize; x++)
5 C5 e8 m- g: p) u: a2 V3 p            {
$ p( S. U* |; A7 w                BYTE bAlpha = (BYTE)((_pBits[_TextSize * y + x] & 0xff) >> 4);& s0 {* q/ g/ ]( T
                if (bAlpha > 0). e% Z& g3 o7 F; Y$ k" S
                    *pDst16++ = (bAlpha << 12) | 0x0fff;
0 X0 L6 _, \5 o: U" R1 o                else/ Q  a4 g; F8 _, Z
                    *pDst16++ = 0x0000;5 }- K/ B: q6 j1 y
            }5 g3 i- Y: M& `6 M7 A
            pDstRow += d3dlr.Pitch;. X7 e: P" J& N/ {% j, H4 g
        }
. a, V1 }/ w  Z; ~7 T. \        _pTexture->UnlockRect( NULL );
; Q# r6 ^# a: H    }5 a( F0 V. J; m
    else  F: z  p) a! f# T
    {
/ U# B+ {7 }$ C6 S        // 计算当前空闲的Char在缓冲中是第几个! E: I* |9 X& d6 d+ i! \6 b1 f
        int at = it-_vBuf.begin();! b! z& K1 ~8 x% I
+ H7 e) s# b! L7 ^8 n+ ?9 I
        // 得到这个字的坐标
$ Y/ p- [% h; ]4 N# V* A: P8 U        tX = (at % _RowNum) * _TextSize;( U0 X3 g8 ~% c
        tY = (at / _RowNum) * _TextSize;6 W7 M2 t9 P6 |( @/ r
    }
$ o+ Q9 g! P( \( l( P$ _}& {- y& m( K2 v: H; T2 s" Y& y4 Q0 N, \
以上代码中的注释已经很清楚了,相信无须我多言。这里唯一需要声明的是:原来所定义的Char结构是这样的! P: P9 y' k% A& n( y
struct Char{8 ^0 y: m1 O: }, S6 [
  char hz[3];   // 保存汉字) c6 F% u* [! z9 _
  int frequency;// 使用频率; M7 k6 ^7 u7 g3 U! r6 P% d
  RECT rect;    // 这个字对应位图的区域0 [; X/ K- L0 }
  Bool isUsing; // 是否使用
- q4 f4 P& |- u2 M1 c, s1 a}: Z! `# T2 l0 ?/ p2 H) Y/ 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。
4 M4 J" m/ u* D6 [- f: u! d& u" {: G然后对于RECT rect,因为没有了int frequency,意味着一旦将汉字写入到Texture,其位置就不会变动了。所以,很容易根据find函数操作后的iterator,直接计算出这个汉字所在Texture的位置。这样,RECT rect也不再必须。$ U& X! w0 r, `! e/ P# r
而bool isUsing,它本身就是个鸡肋,要也可以,这样结构更加清晰。不过,直接通过观察WORD hz为0或非0,即可实现isUsing的作用了。% M/ V: E. w+ U0 J) g
为什么要对结构Char这么精雕细琢呢?( r8 u7 i# B+ ~( y; j4 [3 a: j
1.    既然没有必要的东西,就应该删除4 D. R7 v3 s' }2 g" Y
2.    Char结构的大小越大,vector所要求的内存越大3 S, q% U' M1 p& O0 X
3.    小的结构,find可以更快地查找出所结果
& M( ~3 T3 `' t0 p- i7 z+ c* w为什么find会正常工作呢?这里我要大概地讲一下find是如何查找出所需的位置的:它只是简单地使用while从vector的begin一直遍历到end,逐个判断,直到找到为止。find要求必须实现自己的operator ==(),进一步跟踪到find的源码中,发现也是这样。于是前面的结构Char变成了现在这样:$ }: x5 D3 M0 L: w4 n
    struct Char{2 J" x. P- E: e
        WORD    hz;                // 文字- D% `: J8 A+ m  p& _6 R% v  x
) B$ Z1 v& u8 [+ ~( `
        Char() : hz(0) {}/ s: e, E' v& t7 l6 k
2 G% f1 o7 y+ P! o; U. F
        // 用作查找文字9 E( ]6 k( R+ [# |8 V
        inline bool operator == ( WORD h ) const
& f  z7 A- c! d% ]( f% K$ U( a( Q        {1 g6 g& i$ Z+ |* _" a
            return hz==h ? true : false;; o8 c$ S4 Y) K3 X8 F
        }
* f# Q* E. S( v* P    };
4 |4 h5 E8 Z& o- Z$ Y+ R8 l; Q% f是不是很简单?^___^% G$ h5 A, D9 s* n3 o

$ |/ q1 f! T+ [+ q+ j' V终于到了显示的函数了:
% z( n; q/ p; N// 得到文字在纹理中的位置
# i2 w( [: R6 I% G7 @( ~: Ubool CFont::
; _: V' N0 F$ U) _. @/*-------------------------------------------------------------) }& Z2 E: I- N1 a
char szText[]  ---  显示的字符串2 D  j1 F! w( F* Z- J6 L
int x           ---  屏幕坐标x
  c% G; F4 \3 `9 J) Oint y           ---  屏幕坐标y1 k- g3 _' `; B6 F
D3DCOLOR       ---  颜色及alpha值3 j, J/ _0 ^$ d! q: V; c
int nLen       ---  字符串长度
0 f5 W/ ?- P8 l/ l+ a9 k& e4 gfloat fScale   ---  放大比例7 l$ ]6 B8 c, e, g
-------------------------------------------------------------*/; U! b" [4 V1 _: u. Q5 M8 ?  Y4 ~
TextOut( char szText[], int x, int y, D3DCOLOR color, int nLen, float fScale )  O3 \6 R) \0 Q/ V: \% w! P
{
" q8 q* d2 a8 d+ ~( e9 K7 ]2 O- Z    Assert( szText!=NULL );
& W5 ]* M( r8 k$ N& k. e6 J+ t" ~! N7 w9 c6 S0 l- a
    float sx = x, sy = y,1 A+ b0 F* N+ ^
          offset=0, w=0, h=0, tx1=0, ty1=0, tx2=0, ty2=0;
5 p. G; E5 ~& r% Y1 |    w = h = (float)_TextSize * fScale;+ [6 z( b1 l9 w  s* u3 l2 \, S

" ^0 a& t% F& G  a% b    char ch[3] = {0,0,0};
- r+ n: c  z/ ~) n8 g  \    FONT2DVERTEX * pVertices = NULL;2 x/ o& ^- j" `2 D
    UINT wNumTriangles = 0;* @$ u) r; s- }8 `  Z" J5 w0 J7 P
    _pVB->Lock(0, 0, (BYTE**)&pVertices, D3DLOCK_DISCARD);) a# F* V( V" m) J4 _) l
; z. [& Y9 k' O% a  K
    if ( -1 == nLen ||                // 默认值-1  a2 P5 `+ t8 j2 K% v" x
         nLen > lstrlen( szText ) ) // 如果nLen大于字符串实际长度, 则nLen=实际长度
6 J4 s7 P9 J" Y; s3 @" l& z        nLen = lstrlen( szText );5 o& I/ Z+ c* A) c* D3 x$ r+ S
    for (int n=0; n<nLen; n++ )/ |  R' X: t1 x
    {
( {. W# R2 \. o; E        ch[0] = szText[n];! O+ O6 n; J! K* ^2 A
6 y2 {( i8 I) G6 N
        if ( ch[0]=='\n' )
8 t+ `, P" |9 T, u        {' |$ [3 \# T/ E/ v
            sy+=h;6 r! x  V  v: A9 ^  R) _+ [
            sx=x;, j0 _, a0 T- D# R( {
            continue;0 T) l+ p" j$ J5 F& C
        }% b; X2 S6 H% p" H  x

; k* t# [1 D% \7 O+ }( [  p        if ( ch[0] & 0x80 )( B- e3 F7 l8 u  Y" G
        {+ P8 Z# @3 W; n  P  I2 Y' I
            n++;( d; g' J: u; A3 E0 m
            ch[1] = szText[n];
) g0 V9 x; d; r* x. q( t0 i            offset = w;
3 P9 j; `4 u+ x' g, g9 q        }  p2 x, Y: L  D" ?. K
        else
; V  ~- w" |0 Y8 o( f# d        {
+ P* W+ d* }8 c5 f3 e            ch[1] = '\0';# x% y) K9 Y* E& ~
            offset = w / 2 ;
7 h6 H* X, a- o+ k# X( _) S1 j" i  a        }
; B( K. G% u/ c; P; m  V$ E% s+ e, ]' o0 P, O. U  l5 |
        int a, b;) B6 N( C' I# |, Q& l1 \
        Char2Texture( ch[0], ch[1], a, b );7 g  Y3 C. m& M) Z
   
$ J/ D4 E' K1 ]/ T! i9 f/ p' r        // 计算纹理左上角 0.0-1.0
: h, g1 R+ I! D: m& C% k        tx1 = (float)(a) / _TextureSize;
& T- ^* t! G4 @4 A        ty1 = (float)(b) / _TextureSize;. j& M5 B$ s3 O2 s  N3 M/ C) n. O
        // 计算纹理右上角 0.0-1.0
# ?' I2 g8 J& T* \# d* q' N        tx2 = tx1 + (float)_TextSize / _TextureSize;
6 t( L* u2 h- J- q4 A6 M+ L0 G- W        ty2 = ty1 + (float)_TextSize / _TextureSize;9 R) K& w4 q' |3 [
) w& I0 K1 D+ }7 V( d
        // 填充顶点缓冲区0 X6 N* f. P, i& p1 g( P; F5 e; G
        *pVertices++ = FONT2DVERTEX(sx,        sy + h, 0.9f, color, tx1, ty2);
& c' K: s4 k) o        *pVertices++ = FONT2DVERTEX(sx,        sy,        0.9f, color, tx1, ty1);6 m/ k4 s; o/ p
        *pVertices++ = FONT2DVERTEX(sx + w, sy + h, 0.9f, color, tx2, ty2);
) T5 ]1 x5 o& f3 J  J/ N! U5 R        *pVertices++ = FONT2DVERTEX(sx + w, sy,        0.9f, color, tx2, ty1);5 }- E3 k/ O3 ?+ e
        *pVertices++ = FONT2DVERTEX(sx + w, sy + h,    0.9f, color, tx2, ty2);2 y( m7 U& j0 i' t) V+ W
        *pVertices++ = FONT2DVERTEX(sx,        sy,        0.9f, color, tx1, ty1);
3 [8 X' P" |4 I% S; p3 ]. {, ?+ n9 Z
        wNumTriangles+=2;* o2 U( P' y( d" z; K, G% V4 ]* ?

. u( E' V# ^) }6 R        sx+=offset;    // 坐标x增量
$ t: b; ~; Q' g8 W4 @5 z7 Z    }
0 _8 m1 c4 d' Z2 d" ]    _pVB->Unlock();
, u5 f) C8 Q- w. f* `    _pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, wNumTriangles );
9 ^& x( [7 q: R5 B* Y  C( j" J" Y
    return true;! Y/ w' Z; L( P8 m7 T
}
6 c+ ?% E6 _, i4 [! G( {0 Q2 }9 d结束语: f& \, Q4 M, Q$ ~+ U
记得有一句名言: Keep it simple and stupid.在实现功能的同时,保持代码简单、清晰是非常重要的一件事。相信在往后的日子里,在不论是别人阅读或是你自己回顾的时候,你都会发现一如既往地遵守这个守则,是多么得重要!
/ ~* S9 J- D) h$ m# d+ q+ b5 U! U相信通过上面我那无数的废话,加上代码中还算足够的注释,聪明的你一定能够明白这其中的原理了吧。如果以上的内容还不足以让你完全搞清楚的话,你可以登录我的主页:
$ I' E/ r; @8 I$ Z5 r炎龙工作室
  j( ]# b% B' t# Y上面不仅包括了上面所写的程序代码,还有一个用来演示效果的一个很简单的demo。% g7 S& k( t' l" b* I
说明,以上所实现的CFont是包含在我的游戏引擎中的一个部件,而目前已经实现的部件包括有:
: o7 ]3 H! V7 x7 u' K$ w1.    CGameFrame(游戏框架类)  -----  封装了窗口及D3D设备的建立,需要派生出自己的子类: l- z" u' X% ]3 K9 `
2.    CAudio和CSound(声音类) -----  支持wav/mid/mp3的播放
3 B2 ?3 \8 ]% w6 R: t3.    CDirectInput(控制类)    -----  键盘、鼠标操作
7 U& X& K. M; Y( B4 n" |  J% W, o4.    CDirectShow(视频类)     -----  支持avi/mpg/mov等的播放
/ v# j  K  W- M+ D% L* r; {7 v5.    CSpriteX(精灵类)        -----  方便游戏中对精灵的控制* S4 V  N+ ~# a  ]; ?
6.    CFont(字体类)           -----  中英文字体的显示
: h  `/ Z7 h2 I5 I8 ]5 }7 v7.    CTimer(时间类)          -----  高精度时间的控制+ ?& r) P# J  b1 i& C; x8 h
8.    FPS(fps 类)             -----  fps的计算! T' k" _$ M' [% i5 ]
9.    LOG(日志类)             -----  游戏中的错误反应以及状态记录
9 V) z" W9 ]/ d% x最重要的是,这个Game Engine完全是开放源代码的。关于更新的情况、版本说明以及源码下载,请随时关注我的主页!
2 c/ s$ j# a" D; _! S4 O接下来,我将会继续完善这个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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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