讲了一些TTF的知识,也许对汉化有用
7 X+ B* Z+ r3 }5 _
+ M1 I- U" L( k6 A! e# J" n! _" l+ x9 f: y, X7 d8 l
游戏中汉字显示的实现与技巧
* D. ]8 H, W- l8 ]; \作者:炎龙工作室 千里马肝. y8 A) r% O5 l ?% d% z; ?
版本:v1.0
d7 A; w$ M6 `* j& E# K4 h4 i最后更新日期:2002-3-30; \6 I3 X) f3 q X0 V4 s/ L, ^
绪言
* t! Y/ p+ N% ?6 O. q- E在游戏中,因为我们是中国人麻,通常都需要显示汉字,比方说交待剧情。而对于文字的显示,英文的显示要较其简单得多,因为只有26个字母,就算再加一些标点、符号什么的,用一张位图,就可以足以显示所有的单词了,而相关实现技巧,也比较轻松。
6 E4 x) T# ?$ x8 y而中文的显示方法,要复杂得许多。记得原来在DOS下,汉字的显示都是读的UCDOS的点阵字库,而点阵字库的读取方法,在UCDOS SDK中都有源代码可以参考。但是自从Windows操作系统开始,我们开始了解到一种更好的字库,它就是TTF。2 \7 c9 y* l1 k8 z. W7 V' B7 ?
注:以下我所指的开发环境,除非明确说明,默认的平台是VC6.0+DirectX8.1,使用D3D来加速2D。然后使用的STL是用的SGI实现的那一套STL。
3 x4 D; @8 c" s, f3 \( Y( C点阵字库, y# K) U- F4 O+ O
包括现在,有很多游戏都还是使用的点阵字库。因为操作起来比较方便,加上这方面的经验已经积累了好几年了。通常如果只是一种字体就可以满足需要的话,它会是一个比较好、快的解决办法。但是它有3个缺点:( V: D" [7 K& M: o5 f
1. 如果放大显示,不做处理的话,显示出来的汉字,是很难看的。8 k$ b! U8 E, o
2. 像是UCDOS所提供的点阵字库,只有24点阵的有几种字体,如:宋体、黑体、揩体…,而16点阵的好象就只有宋体一种。" A$ K2 M) N& E7 e& f [3 y6 R9 R
3. 点阵字库,通常是有版权的,尤其是第三方制作的汉字库(如:方正)。: w9 K# \. f2 S3 F5 r$ \ N
在这样的情况下,当我们写好这样的一个显示函数,就算是解决了如:放大、快速显示等问题的话,可供选择的字体还是太过于局限了。所以,在字体的要求比较强的情况下,点阵字库并不是一个好的解决方法,他不够灵活。尽管我们对于它的操作是如此得熟练,可以写出优美的代码来展示我们的编程技巧。3 K9 X. ^- d2 S$ s" v5 b
TTF8 R3 Y u6 i2 t
TTF是True Type Font的简称。在Windows\Fonts目录下面,我们可以看到许多后缀为ttf的文件,它就是接下来我们接下来所要谈到的。( Q, v/ W0 X0 O6 v& P+ Z( T% ~3 u
TTF是一种矢量字库。我们经常可以听到矢量这个词,像是FLASH中的矢量图形,在100*100分辨率下制作的flash,就算它放大为全屏,显示出的画面也不会出现马赛克。所谓矢量,其实说白了就是用点和线来描述图形,这样,在图形需要放大的时候,只要把所有这个图形的点和线放大相应的倍数就可以了。而且,在网站上有很多的TTF字库可以下载,或者你可以去买一些专门的字库光盘。然后在你发行你精心制作的游戏时,可以顺便捎上这些后缀为.ttf的文件就行了。包括Quake这样的惊世之作,也都是用的TTF字库。
$ K: \. w) J" W% L9 ]' {2 F. J这样,我们就可以解决点阵汉字的一些问题。通过TTF,我们在字体的质量和字库的数量上获得了暂时性的胜利。! ^% G. C5 ^; ?4 t
字库的读取和显示
% c9 V6 y8 g0 m5 `先前谈到点阵字库,只需要很简单的一些操作,就可以显示出想要的汉字。下面我给出一个读取hzk16的函数,它需要一个Surface以供显示用:% J( w; M+ @+ ^
#include <io.h>6 s5 K$ U8 b. G+ c% _9 M9 G
#include <stdio.h>
5 G8 Z+ S: N3 s! d0 p#include <conio.h>
+ B- X# t8 `9 d
8 a+ v# }& b* }( y4 N// 读取16x16
; K. M& N4 n f! F# S6 Rvoid DispHZ16(int x, int y, BYTE *Str, LPDIRECTDRAWSURFACE surf)
; E9 L$ I% M( }{
* G! c, x$ D6 |) i5 i const int Mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };* H1 D$ _1 o3 x0 r7 w
FILE *HzkFp;4 R4 `' T- i" p$ Y }. U' N( A5 ~/ n
WORD i, j, k=0, m;7 G% ?% D. I& P" J5 r
WORD HzNum;' n* D- z( l" P/ N8 B# A
WORD QuHao;
2 f+ ^% t8 z) W! _ WORD WeiHao;
8 W, {# l5 t! Y' p$ ?: E* U% J long offset;
6 V9 _' y& ?6 k* ?, |$ Z# }0 o& c* O BYTE dotBuffer[32];
/ \1 N; ~2 @& v7 Z7 _1 D/ B: Q
7 q. r6 u( y! P& s: d: ? HzkFp = fopen("HZK16", "rb");
# G% W" d* M: m t! _0 G5 I6 N7 J 6 S3 W" m! k7 [! v ]
HzNum = strlen((const char *)Str)/2;
0 Y/ p& Q( i. {- E i1 ~' w' o* `2 f3 ^5 F3 b+ U
DDSURFACEDESC ddsd;1 }1 f& t: t, g2 z/ F
LPWORD lpSurface;
' \/ X* K. D4 I" W7 R HRESULT ddrval;
9 U( c, v8 V! S* B& x( f
3 e' M( G* c& X% u ddsd.dwSize = sizeof(ddsd);
* [( e! \' S+ v1 ^ ( p. _6 }" n$ l3 @/ b: e5 A
while((ddrval=surf->Lock(NULL, &ddsd, 0, NULL))==DDERR_WASSTILLDRAWING);: q; T9 B9 z( c# W, N0 B
if(ddrval == DD_OK)$ { ^* u0 a6 i4 f
lpSurface = (LPWORD)ddsd.lpSurface;
4 c; M8 k0 G& y0 v* f
9 V( c# [+ v3 d7 E" ^: C# o. }. l for(i = 0; i<HzNum; i++)3 X' J: @( }$ @: c, a
{
" h+ w5 s! V! V QuHao = Str[i*2]-160;
$ S" U! r& S$ a+ [% S" X& D WeiHao = Str[i*2+1]-160;
$ D$ y- Z) W; |: _+ s3 ~' E' s! m5 x: z" q. Z: e
offset = ((QuHao - 1) * 94 + (WeiHao-1))*32;
" ?) z2 [/ }& [) h& B$ x/ l% ^% K6 z% V' t, s2 U
fseek(HzkFp, offset, SEEK_SET);
' c- w* k6 F, H& s Z: j) n- e4 y9 a fread(dotBuffer, 32, 1, HzkFp);
# I/ n& D# B: u3 L
. R, q- @. K+ M( {# @ for(j=0;j<16;j++) + Y! M$ |# g' Z
for(k=0;k<2;k++)
5 \0 l" N4 ]) u9 W for(m=0;m<8;m++) : l: j& o0 N& Q. O' {
if(dotBuffer[j*2+k] & Mask[m]). W2 p7 p! g/ e' s' o
{
0 U3 I+ j# p8 \8 b lpSurface[ddsd.lPitch*(y+j+1) + x+k*8+m] = 0x000000;
6 x% s' h( a ^4 G) q }
2 U. z# g2 K! e3 ^7 W$ U x+=16;
; ?' ?6 _/ |9 c9 w8 @( J }. y' J- }, c' L8 b, X3 S
- e% `( X, \7 q1 o5 o
surf->Unlock(NULL);
( X/ m* |- I. `+ w, j9 f v8 i0 L0 d u
fclose(HzkFp);4 x' V( I _# x
}
3 f1 R X. }* I% ^% M8 l其实原理很简单:
+ ~0 p$ K; C) R. s4 E1. 打开字库
5 P% [ ^# @& w2. 计算字符串长度(这个函数只支持中文),并且Lock Surface3 o& K7 W4 B+ H1 ?9 L
3. 依次计算出每个汉字所对应的区码和位码(汉字的第1个字节是区码,第2个字节就是位码),然后通过公式计算出这个汉字在字库中的偏移量:
/ j% l: [1 O& Goffset = ((QuHao - 1) * 94 + (WeiHao-1))*32; Z5 A/ d5 k0 |3 n5 ]7 X
4. 读出一个32个字节的点阵
) G$ P2 R" U( K9 x3 w5. 绘制到Surface上
8 K* f# h/ a! K9 ~* S以上只是16*16点阵字库的显示方法,24*24的读取方法与之类似,大家可以参照相关资料来书写出自己的代码。
* H1 Y( s4 u& V f ?4 @/ i* A& Z' I z( a9 |4 o; Z: x$ A( h
如何显示TTF字库呢,有很多种手段,下面我按从简单到复杂的的顺序依次介绍:
! H6 p4 h- @6 s6 U, u1. 使用Windows API,也就是大家所熟悉的TextOut。通过它,还需要一个HDC(设备句柄),我们就可以随意地在屏幕任何地方显示出文字了。9 o8 X& y {4 K, ?! t8 n7 s
2. 在http://www.freetype.org,有一个FreeType的免费库,而且是OpenSource的。它目前有2个版本:1.0和2.0。其区别在于,1.0只能读取TTF格式的,而2.0支持更多的文件格式,在使用它之前请详细阅读所要遵循的Licence,以下是摘自FreeType2.0对字库的支持列表:( t9 r; o u& N8 Q, @
o TrueType fonts (and collections)
+ ]) j* ]1 `1 o! J9 Eo Type 1 fonts - y/ u' D% P$ F5 L' S" j
o CID-keyed Type 1 fonts 1 w" l% b) n" V# J' ~; r# p8 {
o CFF fonts
7 ]+ B* } e5 N+ I1 wo OpenType fonts (both TrueType and CFF variants)
, r$ S$ J) f2 d7 Q" t& ?o SFNT-based bitmap fonts
5 F, z1 n) B. @5 s6 E3 `, @o X11 PCF fonts % U5 t$ g3 X0 D* |4 G
o Windows FNT fonts
+ r8 M, D, n3 f- h1 F8 F& z3. 自己研究TTF的格式,然后自己来操作。
$ K7 z/ f. }, S% D' L4 O晕" T( M# }" P0 p
....... ╮╮ 4 k4 l% h' d1 V3 Q% L
\█/倒! ' s9 e& [( w6 `% c( V, i( I& ~
●' B- ?* d' X- R' v
虽然我们想要把每一件事情都做好,但是也不是每一件事情都要亲历亲为。如果你非要这样,也行^____^,但是过不了多久,你就会陷入泥沼,到时候你会发现自己的热情正在慢慢被磨灭,什么叫做抓狂,相信你很快就会知道^_^。
/ D* e% b$ R2 m, ]0 \4 n0 w% A1 w% U. B0 L4 n5 X
在有多种选择可以取舍的情况下,我们需要考虑一下,对比一下各种解决方法的优劣。
% p3 u. r( G4 C( P4 W `3 {1 W3 ?% c' T/ l
: q) [1 M4 F9 t% l, g5 u在DirectDraw时代,我们都不自觉地喜欢上了GetDC,因为……多方便啊。可是现在已经到了DirectX8.1时代了(我要使劲地摇那些还沉醉于DirectX7中,为如何在使用alpha时提升那可怜的1、2个fps的朋友们:醒醒,该起床了!),HDC已经被M$列为禁用品。怎么办呢?是的,你可能已经想到了,我们还一直保存着窗口的hWnd呢,可以通过它来得到hdc,从而调用那些需要hdc的API,可是,这样做是更为愚蠢的,这样对你是没有一点好处的,不信,你就试试吧。有一句话,请牢记:要想你的游戏有更快的速度的话,请不要再去碰HDC了。
/ L0 ]; S+ g! U我们非常清楚hdc是一个超慢的解决办法,它无法在我们的高速游戏中满60分及格。下面来看看FreeType,它更像是一个Service。它的解决方法是,先通过一系列的初始化和设置,告诉FreeType字体的名字和大小等,然后它会动态地申请一个Graphic,再把我们要显示的字画到这个Graphic上,你还可以把它保存为tga格式。不过我们最终所想要的不是这个,所以可能我们还需要从这个Graphic上逐点读取或者用CopyRect,然后再画到我们的画面上。其实它已经是很方便的了,可是需要你去学习如何配置和使用它,这是很花时间的一件事情,而且它最大的优点是可以跨平台,我们需要它吗?如果有一个更为简单的办法,像是如果Textout不是那么慢的话,就好了……3 h/ {# v0 t' {9 p4 I- B# X, ^0 S0 u" Y
在这里,顺便谈一下另2个字体显示类:ID3DXFont和CD3DFONT。可能早就有人会说怎么在上面的列表中没有它们?原因我会在下面慢慢地说明:
( f6 a9 }7 C V0 @' E0 gID3DXFont,它存在于D3DX库中,一个现成的字体类,不过对于它的处理方法……我实在不敢恭维,就引用一位大师所说的话来表达我的看法吧: 在内部实现中, ID3DXFont::DrawText()函数确实做了我上面讨论的工作,先建立一张GDI兼容的位图,把文本绘制到位图上,而后把位图拷贝到纹理贴图上去,最后把纹理渲染到屏幕上。这样你就聚齐了所有的龟速的原始GDI函数,还包括了一大堆的额外开销 — 最终,这个函数比原来GDI的DrawTextEx()函数要慢上超过六倍……9 J* \0 z0 i! B0 B
CD3DFONT,是由M$在D3D的框架代码中提供。不过它只能显示英文,有很多朋友通过自己定制和修改这个类,来实现自己的中文显示。不过效果都不是很好。其实原理,跟ID3DXFont的方法差不多,不过处理方法要聪明了一点。
; T# i: Y6 _! S分析与思考# F3 D. y- ?% Z1 G7 q8 ^
那么我们应该怎么办呢?通常我们会幻想,如果可以像处理英文那样,把所有的汉字都保存在一张位图里,该有多好。这样,显示的速度就不是问题了,直接可以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 A @6 \8 d/ `! \8 {7 ^汉字虽然很多,但是常用的汉字,其实也就只有那么几百个。像这样的字:鬯、鞴,你一辈子会看到多少次呢?如果可以做一个类似于Cache的东西,保存着常用的那些个汉字,在需要显示的的时候,先在Cache中查找,如果有的话,就马上画上去;如果没有,就从字库中提取到Cache中。这样的话,在使用Texture来保存汉字的位图信息的同时,对于每个汉字,我们还要定义一个结构,然后用一个东西把它串起来,综合它们2个,也就实现了我们所要的Cache了。刚开始,我所定义的结构是这样的:
) o) q1 J& i2 ?3 ]' R) p' |8 Lstruct Char{
% m$ p: r' r" k+ ? char hz[3]; // 保存汉字
r) a" ?$ x( t- K8 h- v int frequency;// 使用频率- C: M- E, t. M- M4 ]3 E
RECT rect; // 这个字对应位图的区域/ F" m$ n* x P7 ~- d
Bool isUsing; // 是否使用4 P, c2 V/ i9 f5 ]& b; Q, ?6 @. ~
}( h% h- @* K# f2 f, F2 S' ~# k/ y
对于汉字和英文,我在这里大概地讲一下原理:汉字是由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’。
4 ?! e& w9 k0 l0 F9 n+ g接下来,对于使用char[3]来保存汉字,是否真的很合适呢?因为如果把它当作一个字符串来看的话,在查找时就需要使用 strcmp 来比较字符串了,这样一定是会影响速度的。如果不把它看作字符串(字符串的最后一个字节需要以’\0’结尾),只用char[2]的话,我们可以只是简单地调用宏MAKEWORD,把2个byte压成1个WORD。当把文字作为一个WORD来看的时候,这样查找比较时可以用WORD内建的==操作,这样要比调用strcmp函数要快得多。9 K' h1 q! U. p+ k1 a& h; l
int frequency用来标志每个WORD的使用频率。设想,如果一个字已经存在于Cache中,以后每对它调用一次,就让frequency++。这样做还有一个用意是,是否可以在一个合适的时候,以frequency为参照来对这整个Cache排个序,把常用的字放在前面。那么在显示时,可以先在Cache中查找所要显示的字是否已经存在于Cache中,如果有则直接显示,没有的话才需要采取某种手段将字加入到Cache中。一些常用的字(像:我、的、着、了、过……),使得显示的速度将会大大提高。; d8 r; v2 d, l/ r7 m2 ?. `
其实上面说了半天的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上的区域位置。
9 [/ K3 e( e9 W# H" k使用什么东西来把这n个Char串起来呢,一般会想到的是链表,原因无非有2个:1 随时有新的字加进来,而内存是不连续的 2 它几乎没有容量的限制(除非是内存用完了)。不过链表的访问速度是很慢的,如果使用像数组这样的东西就好了。仔细想想,在这里,我们用来存储的Cache,最大也就是256*256(理由上面说了),所以大小应该会是固定的。我们只需要在数组中的给每一个汉字加上一个标志,说明这个位置的使用情况。那么就使用数组吧,这样的话,访问的速度要更快一些,直接首地址+偏移量就够了,不必像链表,在查找时需要逐node访问。当然,我绝不会想到用new Char来申请这个数组。因为这样做实在没有必要,请不要过于迷信自己的能力,在STL中已经有vector了,为什么还要自己写呢?^_^最后的一个bool成员变量isUsing,也就是上面所说,用来标志使用情况的。8 X7 h" K9 B# N5 Z% t/ T
实际的操作
# M9 c& S$ y4 F; ~/ z7 u; V上面考虑了那么多,我认为都是实际操作之前所应该有的。先谈谈如何显示吧,因为在DirectX8.1中已经将DirectDraw和Direct3D融合为DirectGraphics了。所以无法像原来那样了…………哦,实在有太多东西要讲了,我还是推荐几篇文章给你吧^_^:1 d5 e% j) ^% w# ?/ q6 W- o
http://vip.6to23.com/mays/develop/directx/200201/Geczy3Din2D.htm& X8 ^/ f' e: r e% A& E3 X
http://vip.6to23.com/mays/develop/directx/200201/GESurface.htm6 ]6 x Q5 {3 M$ X
http://vip.6to23.com/mays/develop/directx/200112/2DGtoDX8.htm2 p o" d1 n% z. v8 k J. ^
http://vip.6to23.com/mays/develop/directx/200201/DX8adv2D.htm4 P+ X# X' f* E
接下来,我会假设你已经具备了在DirectX8.1中绘图的基本概念了,所以在你继续往下阅读之前,请务必先仔细阅读以上推荐的文章。5 x+ C5 A4 K+ z A; r, ~1 Y
前面提到,需要一个vector来对应Texture上各个位置文字的信息,上面已经创建了一个结构Char,则这个vector的定义为:- @: _" O ? a# \
vector <Char> _vBuf; // 记录缓冲中现有的文字情况* o1 e1 m+ b) R1 x/ \, } [( j
首先,由于可以利用硬件的放大缩小机能,所以字体的大小精度要求不是很高,只需要支持16*16和24*24大小的字体就可以了。我们需要一个这样的初始化函数:+ W5 P( Z8 T/ h5 h/ V' S
bool CFont::
( S) Y2 h6 s' W1 V! f' g8 {0 K7 B/*-------------------------------------------------------------
! F% q4 L+ ~5 j. C6 r6 n0 t* gLPDIRECT3DDEVICE8 pd3dDevice --- D3DDevice设备5 n1 C! J# J$ d8 K+ {' ]/ z# C& ^+ d
char szFontName[] --- 字体名(如: 宋体). i: o% w7 t) I: m9 A
int nSize --- 字体大小, 只支持16和24
! L& K: g2 M# iint nLevel --- 纹理的大小级别
2 P: k7 f5 m" _2 T-------------------------------------------------------------*/* T9 M8 e1 S- E7 v
Init( LPDIRECT3DDEVICE8 pd3dDevice, char szFontName[], int nSize, int nLevel )。
: R5 u, W1 C. p7 H8 e/ s
5 c$ {& b' T2 \0 N% V1 Z$ r% E在DirectX8.1中,由SetTexture(…)所贴的图的大小,也就是Texture的大小,是有大小限制的,长和宽都必须是2^n,而且位图越大,所花费的显存越大,这样留给其他显示用的显存就少了。所以,必须根据需求的不同,来自定Texture(也就是Cache)的大小。因为汉字点阵大小的原因,所以从实用角度而言(比方说只是显示fps或是短小的标题),开辟一个64*64大小的Texture,才能满足最低情况下的需要(这时如果选择16点阵的话可以存放16个汉字,24点阵可以存放7个,依次类推……)。
: K1 l. k/ q( Q# F根据设置,创建Texture:
: E9 W6 ?( }. A; b& f6 Q7 F _TextureSize = 32 << nLevel; // 纹理大小
: ?: N8 f/ f, q# ]3 Z8 m, G, Z _TextSize = nSize; // 文字大小2 Y$ t" q `, z& d8 h" `0 j" E
_TextureSize = 32 << nLevel; // 纹理大小, p. `0 [. Q0 M+ `
* _/ g2 L) @! U' E0 ^0 ]+ ] _RowNum = _TextureSize / _TextSize; // 计算一行可以容纳多少个文字 N3 y& d" u4 ?3 h% j! z
_Max = _RowNum * _RowNum; // 计算缓冲最大值
+ Y- X* P: v# { g6 u' K6 \3 j0 o5 V0 i* s
创建字体,还是需要使用Win32 API。也就是先创建一个HDC:# Q4 L) r( ?# W( M
_hDc = CreateCompatibleDC(NULL);
: F- y# P8 w) _0 F; u& v+ z) k! |8 Y
; P9 N, {& L" U5 a! C, ~然后创建一个BITMAP和一个FONT,将它们与HDC关联起来。
& y4 G- S/ j6 O3 O* h* p- ` LOGFONT LogFont;
" b! c; e8 i( p3 h, o( K K2 ~! R ZeroMemory( &LogFont, sizeof(LogFont) );* i, Z1 I% p5 {! c% Q, K" u
LogFont.lfHeight = -_TextSize;7 r' M8 n5 K% Y9 G3 `7 V: h% ~9 O
LogFont.lfWidth = 0;' y) a F- P" p4 i5 x6 h5 D" Y5 M
LogFont.lfEscapement = 0;
* V6 o% n0 D1 {9 L) o LogFont.lfOrientation = 0;5 q3 y2 b8 p) _; r! Y" x5 ]
LogFont.lfWeight = FW_BOLD;
; ?, v: p; l2 G- W3 Y9 v LogFont.lfItalic = FALSE;
9 i5 j2 J$ G$ T7 W8 ~$ f* K LogFont.lfUnderline = FALSE;
/ `$ Y1 t' Z; Y; q8 b0 \$ D* F* [9 } LogFont.lfStrikeOut = FALSE;
4 m' O b; } c4 ? LogFont.lfCharSet = DEFAULT_CHARSET;: B% L2 o9 G! Y& ^
LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2 z3 a. L$ F+ S, O! H LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; 2 \ b1 L% ^) q
LogFont.lfQuality = DEFAULT_QUALITY;- n) r0 P0 G4 e% D3 F) G) ~
LogFont.lfPitchAndFamily = DEFAULT_PITCH;
/ ^5 O/ \1 d* J; B! F lstrcpy( LogFont.lfFaceName, szFontName );
8 X( |& V/ o6 F3 D$ c: ~! q4 W" T 3 I* I" a6 U9 l
_hFont = CreateFontIndirect( &LogFont );" Q% D2 }1 V1 F* m5 c
if ( NULL == _hFont )
6 ^2 A8 z& i. I( g; s8 F# D0 ^ {
! w9 F9 x" _/ o6 Q* T9 k DeleteDC( _hDc );% ~' P; ~7 h% {/ q$ p
return false;
# N$ }. j* f# k6 w8 R2 G }* T# Y8 h, ^; t# B6 k
/ K$ f4 }; f$ M [/ j(只需要创建一个字体大小的BITMAP即可)
# e1 Z5 T1 e( q BITMAPINFO bmi; A+ ~( W# P; Q, D: ^
ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER));% U% [0 C0 G# I9 y7 K! K7 S
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
, M" \/ ?: q2 m2 ^/ B bmi.bmiHeader.biWidth = _TextSize;! C! e* v% y) _( q; u! z1 X8 q
bmi.bmiHeader.biHeight = -_TextSize;
2 A) i, Z, f, z9 T0 W3 G5 M6 T bmi.bmiHeader.biPlanes = 1;+ h/ _, {7 Q; b" Z! j8 p3 a! I
bmi.bmiHeader.biBitCount = 32;2 f* {7 y" R$ W& T: {$ H
bmi.bmiHeader.biCompression = BI_RGB;7 F; E% W" L4 f6 S. ?
# o1 `9 Z( U. n ]+ w4 }7 m, x# L
(这里需要定义一个指针指向位图的数据:0 r. T6 a" {9 H/ {. q
DWORD * _pBits; // 位图的数据指针)
5 X7 Z$ y2 h( t2 I
* c5 t- F9 r' h' f% p _hBmp = CreateDIBSection( _hDc, &bmi, DIB_RGB_COLORS,* j9 a$ v( M& @$ P$ s, v" x
(void **) &_pBits, NULL, 0 );* r) a5 z0 l$ f& @. n; ~5 U
if ( NULL == _hBmp || NULL == _pBits ) g- [- T' B! F6 n
{
; ?0 t. t' F: c3 Y DeleteObject( _hFont );' J9 F4 H. l4 ?! U2 D
DeleteDC( _hDc );
% T9 Z2 c+ r1 R return false;( \$ k: L* A- S8 K4 O
}
6 o" w% E6 [) G: B$ N* W, N7 f: ~
' b; M" J; K+ U6 q4 e, b. d- [! x // 将hBmp和hFont加入到hDc. o1 {. ~* {/ `9 t4 w5 O
SelectObject( _hDc, _hBmp );
c7 n- D7 r" z, K% q4 C5 p SelectObject( _hDc, _hFont );
. I. K0 q3 ~! H- u
* o( @" V; H4 d! L5 }( n- O接着设置背景色和文字色:
' W9 G0 A3 i# P3 D, }5 o SetTextColor( _hDc, RGB(255,255,255) );
$ W% j( [, h0 k8 [8 l. _ SetBkColor( _hDc, 0 );) F% l: l: u6 @
% Y$ }7 x' a3 P% n
设置文字为上对齐:
& q; @) C' S! D- d# s! x SetTextAlign( _hDc, TA_TOP );
! Q6 H ~: u" H3 y% G5 i% N& T2 E+ @0 e! Q. L! @7 m+ |
创建Texture所需要的顶点缓冲:
0 k2 W8 E' Q: ` if ( FAILED( _pd3dDevice->CreateVertexBuffer( _Max * 6 * sizeof(FONT2DVERTEX),
8 F2 v# m, U/ h; c D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,
# d- S7 y; g2 r" ^4 d D3DPOOL_DEFAULT, &_pVB ) ) )
3 b+ L4 O. s: P2 f; i" c {
' I$ y0 S& u1 D/ c- n/ q& M( m" Y* E DeleteObject( _hFont );
1 E5 O$ \7 g- a# ~+ r& X* j DeleteObject( _hBmp );
, |7 N3 J" U2 k9 R0 X1 J DeleteDC( _hDc );
. ?2 C8 i- l0 Q* J& z& Y return false;
% n# j! F$ D) P7 G }
" o) s# m5 e2 ]0 Z, Z 8 j, S9 g+ e6 u, h% V
创建Texture7 y- Y7 {. ^. G/ i8 e3 s* l
if ( FAILED( _pd3dDevice->CreateTexture( _TextureSize, _TextureSize, 1, 0,
6 P# p& R4 O( n1 U4 y. a5 [: t% O1 O D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &_pTexture ) ) ): {+ W8 R6 C6 H+ h$ x2 n8 d; {1 E
{
- A' K2 M, I1 c" d- |9 c DeleteObject( _hFont );
' i7 i j2 D1 N: l/ g( N DeleteObject( _hBmp );
& U( g/ L6 t2 q/ d K DeleteDC( _hDc );3 J" P5 S: K2 ~
SAFE_RELEASE(_pVB);$ n7 k' R+ H$ o) O/ W( e; c
return false;2 x' U' }3 c3 a# F! P$ o
}
9 L$ C7 X( _, {4 f D8 ?5 J2 H a) b) T' E4 {( L# O
设置渲染设备的渲染属性:
; C7 o/ {# T. ~; | _pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );$ [# W% }* r4 ~8 K2 g# m
_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
, G. z# c5 j, J: J6 x! T _pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
: ^8 I/ g2 Z$ ^! O+ Z6 f$ ^ _pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );2 E7 J: n0 q/ G& W
_pd3dDevice->SetRenderState( D3DRS_ALPHAREF, 0x08 );
6 ?! f0 ]1 X- ], N7 W# f _pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
2 R) B S' t/ E t+ }( o% g _pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );/ @+ C$ j9 c5 E
+ K4 H7 D1 U5 M! ~& k/ i, D6 O _pd3dDevice->SetTexture( 0, _pTexture );, u% x4 c4 M8 k& o/ X
_pd3dDevice->SetVertexShader( D3DFVF_FONT2DVERTEX );
: N4 ] p4 [" c( _1 i' D _pd3dDevice->SetStreamSource( 0, _pVB, sizeof(FONT2DVERTEX) );3 n* W. d. H) |' B' \! C
2 X! v4 k4 f& w
设置缓冲的最大容量
& j5 j9 @- p2 u( i, e* U$ \0 G _vBuf.resize( _Max );
/ `. K- ^0 V( a. q, x1 D! T2 K! ?# n7 p9 j$ ]/ e+ o& }& S( l! R
这样,初始化完成了。接下来是如何把一个汉字写到Texture中,以及如何进行管理。定义函数:
. k! Z$ P9 t; ` I// 得到文字在纹理中的位置
* L- S+ _2 o6 O0 Y0 I& k% ovoid CFont::; X% |8 g4 }% B; ^1 K m
/*-------------------------------------------------------------* c9 @0 _, S. e( n8 N
char c1 --- 文字的第1个字节2 n- Y/ ]! G- E$ n* }
char c2 --- 文字的第2个字节8 v& v3 s) L! N; o6 ]. w3 X
int & tX --- 写入纹理中的坐标x
# W% @' O6 K' J( Rint & tY --- 写入纹理中的坐标y$ a$ k; M( X0 W0 C* [! ]
-------------------------------------------------------------*/
/ j' W% a$ x& s: ]6 W3 ~Char2Texture( char c1, char c2, int & tX, int & tY )
& D h; E% u% M/ X) x$ m* v{
" K, V/ q. n a4 F WORD w = MAKEWORD(c1, c2); // 把此字变为WORD
3 z# R; I- {5 r D2 x; }3 d+ Q vector<Char>::iterator it = find( _vBuf.begin(), _vBuf.end(), w );9 L8 c0 t: a* D' l( N* k
if ( it == _vBuf.end() ) // 如果没找到+ n7 ~+ d U. l% m$ c& }
{
$ e6 b, P5 }+ {2 _- l$ _3 b7 t it = find( _vBuf.begin(), _vBuf.end(), 0 ); // 查找空闲位置
% I" Q& y& _5 I3 n% d% w! V" U" G8 v if ( it == _vBuf.end() ) // 缓冲已满
8 j& d4 q2 A* x6 p {
1 d" u3 g" @+ w3 A2 t$ y for(; it!=_vBuf.begin(); it-- )
3 V) H# g4 R$ C" e {) R; `- @8 ]0 R
it->hz = 0;9 [/ |( o0 c6 H" e, A
}
4 k8 Q w5 q# y. K5 c/ X// Log.Output( "字体缓冲已满, 清空!" );
) V7 ]: k4 O9 r" O+ S+ Z3 `) V7 T }7 E' P" n; R& [+ x* w
. h! j( `* N6 _) Z# a
// 计算当前空闲的Char在缓冲中是第几个& [+ s$ L3 k1 w0 A6 N! Y$ g* V2 O
int at = it-_vBuf.begin();
# ]0 K0 a3 G0 r5 |; R0 U/ \' p! F W
// 得到空闲位置的坐标
2 d m: k. v1 J& z+ t tX = (at % _RowNum) * _TextSize;
b5 {) K/ p! V tY = (at / _RowNum) * _TextSize;
6 ~, e# r: h+ C0 A; h8 M' @
. A1 |& r7 S+ U8 Z! ]2 E // 设置这个Char为使用中8 t4 m- V7 i' Q4 Y3 B! y
(*it).hz = w;
0 o9 Z) ~8 p0 Z6 B
; S8 q. q: S6 V RECT rect = {0, 0, _TextSize, _TextSize};+ r. a4 I% V b/ u1 j0 j
char sz[3] = {c1, c2, '\0'};
7 B8 L( m8 l8 a9 s- Z // 填充背景为黑色(透明色)
5 n5 O# l4 a0 c3 p FillRect( _hDc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );; x5 M- O& v Y% L
// 往hBitmap上写字( `$ I/ q5 N2 B
::TextOut( _hDc, 0, 0, sz, c1 & 0x80 ? 2 : 1 );+ u: D. a' b* A
?4 ^; A. v3 x6 E5 F4 |$ B6 l // 锁定表面, 把汉字写入纹理, 白色的是字(可见), 黑色为背景(透明)
$ Q5 y7 p3 Q3 {9 T# V* H D3DLOCKED_RECT d3dlr;
- {) _) u) w/ n9 t _pTexture->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);3 C* t- @; i. u" Y& l' ?
BYTE * pDstRow = (BYTE*)( (WORD *)d3dlr.pBits + tY * _TextureSize + tX );
! J* B b) W) _' I ^7 u
5 J" ^9 Y$ f8 l/ x- I/ X for (DWORD y=0; y<_TextSize; y++)
% m5 D* v8 e9 L' T0 _ {
' m* h' G5 r5 o! u( u% h: P WORD * pDst16 = (WORD*)pDstRow;
+ f3 T! Y; w' v8 F- h9 w. C% u2 ` for (DWORD x=0; x<_TextSize; x++)- w& x( g, X7 B. C, Q+ t
{& s- T( W/ d) o) I* d. s
BYTE bAlpha = (BYTE)((_pBits[_TextSize * y + x] & 0xff) >> 4);
: P- x" y# L$ ]1 D" r if (bAlpha > 0)7 f$ z* K0 z2 P& @
*pDst16++ = (bAlpha << 12) | 0x0fff;
- _! n1 s1 ~# X else; Q7 R3 m- \( y+ z- `, A
*pDst16++ = 0x0000;
9 t# y; X# W" g: u0 {+ @ }9 a* Y7 F% T6 |8 Q0 `) l: O# Y
pDstRow += d3dlr.Pitch;: b6 Y5 p5 K% s, f6 K1 F
}! J% |5 P% Y# s' l
_pTexture->UnlockRect( NULL );* @% y& O, U' c0 h8 z, J
}3 y3 @9 |4 i9 l9 U6 d
else5 i0 h! P- y4 ~* Q ]
{
* ^! E' T: Q- g: @0 w // 计算当前空闲的Char在缓冲中是第几个
; y; M( t0 X* K* [0 q; { s, ]" E: J int at = it-_vBuf.begin();
5 h/ ] E$ ?3 K5 W% Y2 g& m, P; x6 r/ U
// 得到这个字的坐标 V0 w% H% P4 f4 b
tX = (at % _RowNum) * _TextSize;
) @7 [$ c: e( i( @ f6 V8 ~- r tY = (at / _RowNum) * _TextSize;( m2 x/ O+ ^: C. g l( ]
}& m+ C' p6 h7 V) v' q- a
}- R4 h: g* M0 Q
以上代码中的注释已经很清楚了,相信无须我多言。这里唯一需要声明的是:原来所定义的Char结构是这样的$ i- c6 D( M% ?7 ]
struct Char{
4 s/ B6 T& e$ X% z. a6 O8 R( P+ v char hz[3]; // 保存汉字( k# c* S2 s2 r' n! o9 }
int frequency;// 使用频率
1 J5 I4 }: [) r3 v( c RECT rect; // 这个字对应位图的区域
) b% t4 p0 W! `2 k Bool isUsing; // 是否使用
, E* b! c; ~4 G9 V( }}' d( Z! J, \& @5 J# F) t. g
后来因为将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。- r! a. y" ]; Q9 a0 A: t1 J
然后对于RECT rect,因为没有了int frequency,意味着一旦将汉字写入到Texture,其位置就不会变动了。所以,很容易根据find函数操作后的iterator,直接计算出这个汉字所在Texture的位置。这样,RECT rect也不再必须。 r( e- s1 B, v( j% q
而bool isUsing,它本身就是个鸡肋,要也可以,这样结构更加清晰。不过,直接通过观察WORD hz为0或非0,即可实现isUsing的作用了。* l& h- { N0 V1 w a, z
为什么要对结构Char这么精雕细琢呢?0 R4 \ r' t- s
1. 既然没有必要的东西,就应该删除) f/ M/ ^1 I1 C
2. Char结构的大小越大,vector所要求的内存越大. }6 I h }7 V& r
3. 小的结构,find可以更快地查找出所结果/ ]; N1 H" x0 W4 Q: m
为什么find会正常工作呢?这里我要大概地讲一下find是如何查找出所需的位置的:它只是简单地使用while从vector的begin一直遍历到end,逐个判断,直到找到为止。find要求必须实现自己的operator ==(),进一步跟踪到find的源码中,发现也是这样。于是前面的结构Char变成了现在这样:. r1 p; w d$ [5 X6 l3 W
struct Char{( t! L% `5 h2 R" m6 e! m; t
WORD hz; // 文字
: W8 X. m1 {8 \. C3 o, Z
! ~, C# c3 I C& x( k& Z4 ^ Char() : hz(0) {}
9 U4 l. J* i* Q9 ]' [) y; ?( p4 j, {! `( L" r- p1 r- P
// 用作查找文字
! b7 V5 W6 w2 l inline bool operator == ( WORD h ) const
* Q5 P9 q. `0 X k {! d1 B6 G, _. `* `- B/ }9 E
return hz==h ? true : false;7 h. X- K7 N$ O
}
' O" [6 o4 Y9 z };1 s A |9 M- C+ G
是不是很简单?^___^0 w' x) ?0 S. {7 a+ I
* F! f4 w6 j( h/ _8 Y8 d$ h6 c
终于到了显示的函数了:
V$ v1 h$ G& H0 T// 得到文字在纹理中的位置
4 R- e& n( `4 r/ u _bool CFont::
, U7 Q+ s9 F& ~- Q5 o4 V# }5 j/*-------------------------------------------------------------
" v- a% w. M. Uchar szText[] --- 显示的字符串# z& B0 k7 ~6 w6 w l& g! K3 g# {
int x --- 屏幕坐标x i7 j: G/ p; h3 }7 l0 {
int y --- 屏幕坐标y
9 I+ ]( x# H( q3 t- i$ W& p2 _D3DCOLOR --- 颜色及alpha值
8 g! O8 u, N9 J0 K; R5 Hint nLen --- 字符串长度
2 y" a/ b& D6 ~% ]float fScale --- 放大比例
# L7 }' N3 Z1 s/ q* Z-------------------------------------------------------------*/0 y/ J; T( W* ]1 c
TextOut( char szText[], int x, int y, D3DCOLOR color, int nLen, float fScale )
# b2 M# n6 _6 S{
0 a% K: X2 g+ ~; S# }' P Assert( szText!=NULL );
% X" E+ |$ E5 E$ y8 Z; R! z+ f w, d; Z9 J6 l# ]9 t7 j
float sx = x, sy = y,9 q! P% C! Y3 O3 x1 b, @. N
offset=0, w=0, h=0, tx1=0, ty1=0, tx2=0, ty2=0;/ k4 c' Q* Z: Q1 ^3 I$ k+ U: B
w = h = (float)_TextSize * fScale;* S( i% b) X( {% w$ G+ Z! ^
4 {& H" A2 x* @ B) [( N2 ? char ch[3] = {0,0,0};' s0 J" k" L' B7 @
FONT2DVERTEX * pVertices = NULL;) j" k' y2 o7 c0 e
UINT wNumTriangles = 0;- ~& e9 f4 p7 l; b$ ]
_pVB->Lock(0, 0, (BYTE**)&pVertices, D3DLOCK_DISCARD);1 _2 Y1 B Z/ C8 V% k! S: g Q
9 J7 d) T: H5 j+ { if ( -1 == nLen || // 默认值-1
7 O4 G+ g+ x, H& p nLen > lstrlen( szText ) ) // 如果nLen大于字符串实际长度, 则nLen=实际长度
. _: ]1 x( ~! O: v4 `: S( Q nLen = lstrlen( szText );) b' m) J' C' ^- M( w
for (int n=0; n<nLen; n++ )9 L) d' q) D; x4 l' n' e! ~
{
7 o; F1 c$ q5 q( e+ o0 o ~4 o; R ch[0] = szText[n];& ~5 a2 r3 W4 X9 @1 O/ u" L3 c7 D0 B
9 ^# @* G( T- Y) Z if ( ch[0]=='\n' )
' A4 B2 o3 J; ^. ^ {
- X* O$ C) Z; N# x5 A& Q sy+=h;
! C9 V* ~6 J8 y" k: L! i7 M sx=x;
$ _& e+ r1 ~; ?3 g) m: @ continue;7 f! `* Z# @2 y1 d
}
; ~* F; r& f6 Q1 a2 D
0 g# L# T% K4 k. q if ( ch[0] & 0x80 )
! u* Y$ Y( s/ ~. l! W9 a& j. { {. O; q2 G; x) m T
n++;5 `9 X9 H5 y* k2 }
ch[1] = szText[n];
) c4 X- |9 b+ a- Y3 m p offset = w;
* {3 F- M" j& @1 ^, {6 H6 X+ q9 n }
4 D5 H4 q3 o! q' e5 c1 U: v0 C else+ B3 A% y @8 i+ {
{
- [: `5 B. o1 P6 i ch[1] = '\0';- Z% I. ]/ z, B& s
offset = w / 2 ;
8 T) J! E6 x% [$ h2 k7 n8 Q( h }
! d! k% `6 v. ~1 c: P; V1 T; I- F/ S1 M: j, C
int a, b;
+ H' m; q$ j# @0 c& h Char2Texture( ch[0], ch[1], a, b );& v4 z, d! ?5 H, R
$ A4 m% e7 ]( u; d1 K# _ // 计算纹理左上角 0.0-1.0
1 H2 D5 F/ k' f& \+ b$ |9 K( i; n tx1 = (float)(a) / _TextureSize;
: ]% B3 f8 g9 I/ R/ ]0 E9 k ty1 = (float)(b) / _TextureSize;
6 x3 Y! T8 t8 r // 计算纹理右上角 0.0-1.0
+ x6 C& ~+ R: {5 J+ Q: {: g; F tx2 = tx1 + (float)_TextSize / _TextureSize;
& O0 t1 ?& @( a ty2 = ty1 + (float)_TextSize / _TextureSize;; W& q( \9 F( ]" y7 `' |, {- l% ^
7 |$ h. g2 e" n& Q/ o; |
// 填充顶点缓冲区
% u+ }9 ?* \+ n0 `, G | *pVertices++ = FONT2DVERTEX(sx, sy + h, 0.9f, color, tx1, ty2);0 [: N6 h0 ]; a1 ?
*pVertices++ = FONT2DVERTEX(sx, sy, 0.9f, color, tx1, ty1);
5 `2 [( \% n) [* z+ f *pVertices++ = FONT2DVERTEX(sx + w, sy + h, 0.9f, color, tx2, ty2);, T% H& v$ r! W
*pVertices++ = FONT2DVERTEX(sx + w, sy, 0.9f, color, tx2, ty1);
& b4 D; Z& [; K; ^ *pVertices++ = FONT2DVERTEX(sx + w, sy + h, 0.9f, color, tx2, ty2);' _+ x1 J8 S- |0 @0 s0 t# b
*pVertices++ = FONT2DVERTEX(sx, sy, 0.9f, color, tx1, ty1);
* t, u" `) T A, b1 J1 e$ l V( o( {& B8 t* E
wNumTriangles+=2;
" V" O. I% a8 Q" G( R3 A
5 x( _+ s( h+ j- x- Q sx+=offset; // 坐标x增量, w+ `6 r- M2 M+ z5 L+ ^
}
2 ^% I8 W r2 p _pVB->Unlock();! s* C! w4 a/ o# i, C& X( ^' v9 ?
_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, wNumTriangles );
8 g1 A( T$ x( O) o5 G) w
" k* Q# t" I# y return true;
K. P9 L* c; S6 _}
6 H2 S4 K$ x. d0 i! S结束语" R' A/ g c2 ~! S$ Z( k; ?
记得有一句名言: Keep it simple and stupid.在实现功能的同时,保持代码简单、清晰是非常重要的一件事。相信在往后的日子里,在不论是别人阅读或是你自己回顾的时候,你都会发现一如既往地遵守这个守则,是多么得重要!
. y V' y. N( T相信通过上面我那无数的废话,加上代码中还算足够的注释,聪明的你一定能够明白这其中的原理了吧。如果以上的内容还不足以让你完全搞清楚的话,你可以登录我的主页:
- w) b0 C2 M5 H" c炎龙工作室: l1 ?5 `" ?8 N1 J
上面不仅包括了上面所写的程序代码,还有一个用来演示效果的一个很简单的demo。! V# F" V+ E5 \" @
说明,以上所实现的CFont是包含在我的游戏引擎中的一个部件,而目前已经实现的部件包括有:
% V4 W3 ~8 i/ O9 I2 Y. o, [1. CGameFrame(游戏框架类) ----- 封装了窗口及D3D设备的建立,需要派生出自己的子类
# z& d1 P# v3 H6 Y- D! K2 a9 Q2. CAudio和CSound(声音类) ----- 支持wav/mid/mp3的播放7 i6 W0 c- N: p
3. CDirectInput(控制类) ----- 键盘、鼠标操作# j, }) V. A+ V6 V! g8 E* ]
4. CDirectShow(视频类) ----- 支持avi/mpg/mov等的播放
) t. k5 H) i1 W b5. CSpriteX(精灵类) ----- 方便游戏中对精灵的控制0 |5 O8 o0 ^* W5 P, _; h0 |2 w) @& @
6. CFont(字体类) ----- 中英文字体的显示
. y5 T$ R5 z8 p3 \7. CTimer(时间类) ----- 高精度时间的控制. A* y" U" {8 Z3 U4 |' b% e
8. FPS(fps 类) ----- fps的计算
) P) i* s9 ^0 `( ]9. LOG(日志类) ----- 游戏中的错误反应以及状态记录6 L1 r0 u# W" w6 G
最重要的是,这个Game Engine完全是开放源代码的。关于更新的情况、版本说明以及源码下载,请随时关注我的主页!
* R$ h k1 y4 |/ f z接下来,我将会继续完善这个Engine,可能加入的有:高效粒子系统、斜45度角地图…… |