***万物皆虚,万事皆允 ——《刺客信条》***
继续Directx3D游戏开发之旅:
字体:
在游戏中,经常要向用户显示一些文本信息。所以就需要我们创建字体和显示字体了,通过几天学习,发现文本上的处理算是所有处理里面最让人头疼的了,尤其是汉字编码,是采用utf-8进行字符编码还是utf-16进行编码,貌似大多数采用utf-16都可以基本解决问题,但不排除其他情况,可能还需要考虑utf-32(即为一个字DWORD),来进行处理。
所以在游戏中开发自己的字体的时候,应多给予此方面的考虑
下面开始学习DirectX为我们提供的字体参考样例,方便学习创建属于自己的字体。
利用ID3DXFont接口
在DirectX中D3DX库提供了接口ID3DXFont,该接口的实质上还是使用GDI(graphics device interfere)接口来绘制文本,所以该接口在速度性能上效果不太好,但可以Directx为其提供了一些API,方便处理一些复杂的字体和格式
创建两步走:
1、创建一个ID3DXFont接口对象
2、利用DrawText绘制文本
创建一个ID3DXFont接口对象:
先利用D3DXCreateFontIndirect函数来创建一个ID3DXFont接口对象。
先看看这个函数的简介:
HRESULT D3DXCreateFontIndirect(
__in LPDIRECT3DDEVICE9 pDevice, //设备与字体结合
__in const D3DXFONT_DESC *pDesc, //D3DXFONT_DESC的结构对象
__out LPD3DXFONT *ppFont //即返回创建好的字体
);
//接下来看看D3DXFONT_DESC这个结构体,需要填充哪些信息
HRESULT D3DXCreateFont(
__in LPDIRECT3DDEVICE9 pDevice, //设备与字体结合
__in INT Height, //字体高度
__in UINT Width, //字体宽度
__in UINT Weight, //字体的权重,是否加粗,范围(0~1000)
__in UINT MipLevels, //The number of mipmap levels(纹理映射等级,一般设置为D3DX_DEFAULT)
__in BOOL Italic, //设置是否为斜体字体,true代表是,反之亦然
__in DWORD CharSet, //字体的字符集,一般设为默认DEFAULT_CHARSET
__in DWORD OutputPrecision, //指定了窗口应该尝试匹配所需的字体大小和字体的特点与实际,一般设置为0
__in DWORD Quality, //指定Windows如何应所需的字体和一个真正的字体匹配。它适用于光栅字体
__in DWORD PitchAndFamily, //Pitch and family index
__in LPCTSTR pFacename, //设置字体的风格
__out LPD3DXFONT *ppFont //其余参数设置完后,返回一个指针指向一个LPD3DXFONT接口,表示所创建的字体对象
);
绘制文本:
通过调用方法DrawText便可轻松完成字体的绘制。
INT DrawText(
[in] LPD3DX10SPRITE pSprite, //指定字符串所属的ID3DXSprite对象接口,可设为默认值0或NULL,表示在当前窗口中绘制字符串
[in] LPCTSTR pString, //指向将要绘制的字符串的指针
[in] INT Count, //统计字符串中的字符个数,若该值为-1,则认为参数pString指向一个以NULL结尾的字符串。
[in] LPRECT pRect, //指定字符串绘制的矩形区域的位置
[in] UINT Format, //指定字符串在pRect指定的矩形区域中的格式化方法。
[in] D3DXCOLOR Color //设置文本颜色
);
Item | Description |
---|---|
DT_BOTTOM | 将文本对齐到矩形的底部,但是该值必须与 DT_SINGLELINE进行配合使用 |
DT_LEFT | 向左对齐 |
DT_RIGHT | 向右对齐 |
DT_TOP | 将文本对齐到矩形的顶部 |
还有其余个别参数,及不一 一列举了
例如:绘制一个简单文本
ID3DXFont* Font = 0;
DWORD FrameCnt = 0;
//创建字体
D3DXFONT_DESC lf;
ZeroMemory(&lf, sizeof(D3DXFONT_DESC));
lf.Height = 25; // in logical units
lf.Width = 12; // in logical units
lf.Weight = 500;
lf.MipLevels = D3DX_DEFAULT;
lf.Italic = FALSE;
lf.CharSet = DEFAULT_CHARSET;
lf.OutputPrecision = 0;
lf.Quality = 0;
lf.PitchAndFamily = 0;
strcpy_s((char *)lf.FaceName,sizeof("Times New Roman"), "Times New Roman"); // font style
// 基于“IF”创建字体格式.
if (FAILED(D3DXCreateFontIndirect(Device, &lf, &Font)))
{
::MessageBox(0, L"D3DXCreateFontIndirect() - FAILED", 0, 0);
::PostQuitMessage(0);
}
开始绘制字体:
bool Display(float timeDelta)
{
if (Device)
{
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
Device->BeginScene();
RECT rect = { 0, 0, Width, Height };
//绘画出字体
Font->DrawText(
NULL,
L"Hello World ",
-1,
&rect,
DT_TOP | DT_LEFT,
0xff000000);
Device->EndScene();
Device->Present(0, 0, 0, 0);
}
return true;
}
利用CD3DFont
在DirectX SDK中提供了一个类——CD3DFont类,该类可以借助Direct3D和映射有纹理的三角形来绘制文本,由于该类使用的是Direct3D而并非GDI,所以其绘制速度会比前面的ID3DFont要快得多,但该类也有极大的缺陷,就是不支持ID3DFont所支持的那些复杂的字体和格式,而且尤其是对汉字字符非常抵触,如果要想让其既支持各种复杂的字体和格式,又要让其能支持汉字字符的编码,这就得需要自己重写或者自己封装一个属于自己的字体类。
下面来看看使用的实例:
方法还是和前面一样,首先创建对象
先来看看,该类构造函数的原型
CD3DFont(const TCHAR * strFontName,DWORD dwHeight,DWORD dwFlags=0L);
//strFontName:表示一个以NULL结尾的字符串,它指定了字体的名称
//dwHeight:所创建字体的高度
//dwFlags:创建标记,参数可以是D3DFONT_BOLD、D3DFONT_ITALIC、D3DFONT_ZENABLE的组合,自己表示没怎么用过这些组合。
实例化对象,进行初始化
CD3DFont* Font = 0;
Font = new CD3DFont(_T("Times New Roman"), 16, 0);
Font->InitDeviceObjects(Device);
Font->RestoreDeviceObjects();
接下来,咱们绘制文本,在CD3DFont类中有个绘制函数提供使用
HRESULT CD3DFont::DrawText(FLOAT x,FLOAT y,DWORD dwColor,const TCHAR * strText,DWORD dwFlags=0);
//x,y表示文本绘制起点的x坐标和y坐标
//dwColor表示文本的颜色
//strText:表示指向所要绘制文本的指针
//dwFlags:绘制的标记
调用绘制函数
Font->DrawText(10,10,0xff000000,"Hello World");
差点忘了,该文件需要包含D3DFont.h头文件
#ifndef D3DFONT_H
#define D3DFONT_H
#include <tchar.h>
#include <D3D9.h>
// Font creation flags
#define D3DFONT_BOLD 0x0001
#define D3DFONT_ITALIC 0x0002
#define D3DFONT_ZENABLE 0x0004
// Font rendering flags
#define D3DFONT_CENTERED 0x0001
#define D3DFONT_TWOSIDED 0x0002
#define D3DFONT_FILTERED 0x0004
//-----------------------------------------------------------------------------
// Name: class CD3DFont
// Desc: Texture-based font class for doing text in a 3D scene.
//-----------------------------------------------------------------------------
class CD3DFont
{
TCHAR m_strFontName[80]; // Font properties
DWORD m_dwFontHeight;
DWORD m_dwFontFlags;
LPDIRECT3DDEVICE9 m_pd3dDevice; // A D3DDevice used for rendering
LPDIRECT3DTEXTURE9 m_pTexture; // The d3d texture for this font
LPDIRECT3DVERTEXBUFFER9 m_pVB; // VertexBuffer for rendering text
DWORD m_dwTexWidth; // Texture dimensions
DWORD m_dwTexHeight;
FLOAT m_fTextScale;
FLOAT m_fTexCoords[128 - 32][4];
// Stateblocks for setting and restoring render states
LPDIRECT3DSTATEBLOCK9 m_pStateBlockSaved;
LPDIRECT3DSTATEBLOCK9 m_pStateBlockDrawText;
public:
// 2D and 3D text drawing functions
HRESULT DrawText(FLOAT x, FLOAT y, DWORD dwColor,
const TCHAR* strText, DWORD dwFlags = 0L);
HRESULT DrawTextScaled(FLOAT x, FLOAT y, FLOAT z,
FLOAT fXScale, FLOAT fYScale, DWORD dwColor,
const TCHAR* strText, DWORD dwFlags = 0L);
HRESULT Render3DText(const TCHAR* strText, DWORD dwFlags = 0L);
// Function to get extent of text
HRESULT GetTextExtent(const TCHAR* strText, SIZE* pSize);
// Initializing and destroying device-dependent objects
HRESULT InitDeviceObjects(LPDIRECT3DDEVICE9 pd3dDevice);
HRESULT RestoreDeviceObjects();
HRESULT InvalidateDeviceObjects();
HRESULT DeleteDeviceObjects();
// Constructor / destructor
CD3DFont(const TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags = 0L);
~CD3DFont();
};
#endif
源文件过长就放码云,或者源程序中
运行效果图:
利用D3DXCreateText函数
这个函数和前面两种略有区别,前面两种都只涉及到x和y坐标,而没涉及到z坐标,而此函数就涉及到了,采用3D网格来进行创建文本。
首先来看看此函数的原型,看看咱们怎么使用
HRESULT D3DXCreateText(
LPDIRECT3DDEVICE9 pDevice, //指向与网格相关的设备
HDC hdc, //设备环境句柄,包含了用来创建网格字体的相关信息
LPCTSTR pText, //确定所要生成文本的字符串的指针
FLOAT Deviation, //字体轮廓的最大弦偏差,该值必须>=0
FLOAT Extrusion, //z轴负方向度量的字体深度
LPD3DXMESH* ppMesh, //返回创建的网格
LPD3DXBUFFER *ppAdjacency, //返回创建网格的邻接信息
LPGLYPHMETRICSFLOAT pGlyphMetrics //包含字体的度量数据
);
关于填充LOGFONT结构
typedef struct tagLOGFONT {
LONG lfHeight; //高度
LONG lfWidth; //宽度
LONG lfEscapement; //角度
LONG lfOrientation; //方向
LONG lfWeight; //权重 0~1000
BYTE lfItalic; //是否为斜体
BYTE lfUnderline; //是否有下划线
BYTE lfStrikeOut; //指定删除线字体
BYTE lfCharSet; //字符集,ANSI还是unicode等
BYTE lfOutPrecision; //输出精度
BYTE lfClipPrecision; //裁剪精度
BYTE lfQuality; //指定Windows如何应所需的字体和一个真正的字体匹配。它适用于光栅字体
BYTE lfPitchAndFamily; //Pitch and family index
TCHAR lfFaceName[LF_FACESIZE]; //指定字体的风格,且长度不能超过32个字符
} LOGFONT, *PLOGFONT;
如何使用?
创建
HDC hdc = CreateCompatibleDC(0);
HFONT hFont;
HFONT hFontOld;
LOGFONT lf;
ZeroMemory(&lf, sizeof(LOGFONT));
lf.lfHeight = 25; // in logical units
lf.lfWidth = 12; // in logical units
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = 500; // boldness, range 0(light) - 1000(bold)
lf.lfItalic = false;
lf.lfUnderline = false;
lf.lfStrikeOut = false;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfOutPrecision = 0;
lf.lfClipPrecision = 0;
lf.lfQuality = 0;
lf.lfPitchAndFamily = 0;
strcpy_s((CHAR *)lf.lfFaceName,sizeof("Times New Roman"), "Times New Roman"); // font style
hFont = CreateFontIndirect(&lf);
hFontOld = (HFONT)SelectObject(hdc, hFont);
D3DXCreateText(Device, hdc, L"Direct3D",
0.001f, 0.4f, &Text, 0, 0);
SelectObject(hdc, hFontOld);
DeleteObject(hFont);
DeleteDC(hdc);
绘制
Text->DrawSubset(0);
效果:
试试自己的弹幕效果:
资源下载:
码云下载:
https://gitee.com/lengya/codes/gfcmp0wk3j7vy4haer1bo37