博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
XNA游戏开发之字符篇
阅读量:6831 次
发布时间:2019-06-26

本文共 14748 字,大约阅读时间需要 49 分钟。

摘要:

游戏中开发不同于一般应用程序的开发,它更注重于界面美观,我们需要在游戏界面设计中花费大量的时间以便使它看起来更炫、更酷,当然这其中就少不了游戏中的字符文本,那么如何制作出漂亮的游戏文本呢?今天我们就一起来看一下。

内容:

在XNA中2D文本的绘制方式种类比较多,这有助于我们制作出更美观的文本效果,下面我就逐一来看一下。

一、SpriteFont

这种方式在XNA游戏开发中应该算是最基本的一种形式,使用方法就是在游戏对应的Content项目中添加SpriteFont文件(右键Add—New Item—Sprite Font)。之后你会看到生成了一个XML格式的文件:

ContractedBlock.gif
View Code

在这个文件中定义了你使用的字体类型、字体大小、字符间距等信息。值得一提的是上面的字体区间,它的意思是指你在游戏中使用的的字符范围,从上面的值可以看出是ASCII的32-126,具体对应字符如下:

ASCII码

字符

ASCII码

字符

ASCII码

字符

ASCII码

字符

ASCII码

字符

ASCII码

字符

ASCII码

字符

ASCII码

字符

32

[空格]

33

!

34

"

35

#

36

$

37

%

38

&

39

'

40

(

41

)

42

*

43

+

44

,

45

-

46

.

47

/

48

0

49

1

50

2

51

3

52

4

53

5

54

6

55

7

56

8

57

9

58

:

59

;

60

<

61

=

62

>

63

?

64

@

65

A

66

B

67

C

68

D

69

E

70

F

71

G

72

H

73

I

74

J

75

K

76

L

77

M

78

N

79

O

80

P

81

Q

82

R

83

S

84

T

85

U

86

V

87

W

88

X

89

Y

90

Z

91

[

92

\

93

]

94

^

95

_

96

`

97

a

98

b

99

c

100

d

101

e

102

f

103

g

104

h

105

i

106

j

107

k

108

l

109

m

110

n

111

o

112

p

113

q

114

r

115

s

116

t

117

u

118

v

119

w

120

x

121

y

122

z

123

{

124

|

125

}

126

~

   

当然它几乎涵盖了所有常用英文字符。之所以要定义这个区间主要是为了减少游戏资源,毕竟在一个游戏中并不是所有的字符我们都要用到。到这里可能会有朋友问,既然如此我要是使用中文怎么办,这个区间肯定不够啊,中文有两万多个字符,定义这个区间的意义也不大啊?

事实上我们如果需要用到中文字符的话一般并不是修改这个区间(当然修改它是可以做到的,但是占用资源十分大,毕竟字符太过了),而是通过Font Description Processor来处理。具体做法就是:准备一个txt文件,其中存放我们游戏中要用到的中文字符,例如我们建立一个FontDescription.txt文件,里面写上"中文字体"四个字存放到游戏的Content项目中;接着在解决方案中添加一个Content Pipeline Extension Library(4.0)类型的项目,然后编写一个类继承于FontDescriptionProcesor,重写Process方法。在这个方法中我们读取外部的一个txt文件,当然你也可以直接写到代码中或存储到其他位置,然后将txt文件中的字符(当然我们这里是中文字符了)读取到FontDescription中,具体代码如下:

ExpandedBlockStart.gif
 
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
Microsoft.Xna.Framework;
using
Microsoft.Xna.Framework.Graphics;
using
Microsoft.Xna.Framework.Content.Pipeline;
using
Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using
Microsoft.Xna.Framework.Content.Pipeline.Processors;
using
System.IO;
using
System.Text;
using
System.ComponentModel;
namespace
ContentPipelineExtensionDemo
{
[ContentProcessor(DisplayName
=
"
ContentPipelineExtensionDemo.MyContentProcessor
"
)]
//
这个名字将用于SpriteFont的Content Processor属性
public
class
MyContentProcessor : FontDescriptionProcessor
{
private
string
fDescription
=
@"
../XNAGameFontContent/Fonts/FontDescription.txt
"
;
//
注意这里的路径,因为FontDescription.txt文件在XNAGameFontContent项目的Fonts文件夹中
public
override
SpriteFontContent Process(FontDescription input, ContentProcessorContext context)
{
string
path
=
Path.GetFullPath(fDescription);
context.AddDependency(path);
string
content
=
File.ReadAllText(path,Encoding.UTF8);
//
FontDescription.txt文件必须保存成utf-8格式,此处也需使用utf-8读取
foreach
(
char
c
in
content)
//
读取文件中字符,存放到FontDescription中
{
input.Characters.Add(c);
}
return
base
.Process(input, context);
}
}
}

做完上面两步之后,此时文件目录结构如图:

文档结构图

这里需要注意两点:FontDescription.txt文件必须保存成utf-8;由于文件在Content项目中默认XNAGameFontContent中的文件都需要进行编译,编译时会自动检测里面的文件类型,而txt文件不属于这其中任何类型,因此我们需要修改它的BuildAction属性为None。接下来我们在XNAGameFontContent下面中添加对ContentPipelineExtensionDemo生成的dll文件的引用,然后在XNAGameFontContent项目上右键选择Project Dependencies,弹出如下图窗口:

项目依赖窗口

在Project项中选择XNAGameFont,Depends on中勾选ContentPipelineExtensionDemo,确定,以此添加游戏项目对内容管道扩展的依赖(这样一来就可以修改SpriteFont文件的Processor为我们自定义的扩展内容)。最后我们在XNAGameFontContent项目中添加SpriteFontForChinese.spritefont文件(上面txt中只是定义了中文字符的范围,在这里可以指定字体类型、字体大小等信息),文件内容如下:

ExpandedBlockStart.gif
 
<?
xml version="1.0" encoding="utf-8"
?>
<
XnaContent
xmlns:Graphics
="Microsoft.Xna.Framework.Content.Pipeline.Graphics"
>
<
Asset
Type
="Graphics:FontDescription"
>
<
FontName
>
华文行楷
</
FontName
>
<
Size
>
30
</
Size
>
<
Spacing
>
0
</
Spacing
>
<
UseKerning
>
true
</
UseKerning
>
<
Style
>
Regular
</
Style
>
<
CharacterRegions
>
<
CharacterRegion
>
<
Start
>
</
Start
>
<
End
>
~
</
End
>
</
CharacterRegion
>
</
CharacterRegions
>
</
Asset
>
</
XnaContent
>

修改文件的Content Processor属性为我们自定义的ContentPipelineExtensionDemo.MyContentProcessor(这就是上面添加依赖关系的原因),然后我们就可以在游戏中使用我们文本中的中文汉字了。具体使用时的代码如下:

ExpandedBlockStart.gif
 
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
Microsoft.Xna.Framework;
using
Microsoft.Xna.Framework.Audio;
using
Microsoft.Xna.Framework.Content;
using
Microsoft.Xna.Framework.GamerServices;
using
Microsoft.Xna.Framework.Graphics;
using
Microsoft.Xna.Framework.Input;
using
Microsoft.Xna.Framework.Input.Touch;
using
Microsoft.Xna.Framework.Media;
namespace
XNAGameFont
{
public
class
MyGame : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont sf1;
//
SpriteFont文本
Vector2 sfPosition;
//
SpritFont文本所在位置
SpriteFont sfChinese;
//
SpriteFont中文文本
Vector2 sfChinesePosition;
//
SpriteFont中文文本的显示位置
public
MyGame()
{
graphics
=
new
GraphicsDeviceManager(
this
);
Content.RootDirectory
=
"
Content
"
;
graphics.PreferredBackBufferWidth
=
480
;
graphics.PreferredBackBufferHeight
=
800
;
TargetElapsedTime
=
TimeSpan.FromTicks(
333333
);
}
protected
override
void
Initialize()
{
sfPosition
=
new
Vector2(
130
,
200
);
sfChinesePosition
=
new
Vector2(
160
,
400
);
base
.Initialize();
}
protected
override
void
LoadContent()
{
spriteBatch
=
new
SpriteBatch(GraphicsDevice);
sf1
=
Content.Load
<
SpriteFont
>
(
@"
Fonts/SpriteFont
"
);
sfChinese
=
Content.Load
<
SpriteFont
>
(
@"
Fonts/SpriteFontForChinese
"
);
}
protected
override
void
UnloadContent()
{
}
protected
override
void
Update(GameTime gameTime)
{
if
(GamePad.GetState(PlayerIndex.One).Buttons.Back
==
ButtonState.Pressed)
this
.Exit();
base
.Update(gameTime);
}
protected
override
void
Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.DrawString(sf1,
"
SpriteFont
"
, sfPosition, Color.White);
spriteBatch.DrawString(sfChinese,
"
中文字体
"
, sfChinesePosition, Color.Red);
spriteBatch.End();
base
.Draw(gameTime);
}
}
}

运行效果如图:

基本SpriteFont和中文字符扩展后的运行效果

二、SpriteFontTexture

由于SpriteFont文本对于显示效果的调整很有限,因此XNA又对其进行了扩充。SpriteFontTexture事实上是以图片来作为XNA的字符集,我们实现只要制作好相关字符的图片,然后像使用SpriteFont一样使用就可以了。当然使用起来很简单,主要问题就是如何来制作图片字库了。这个不用担心,很多牛人早已经想过这类问题了,下面我们看几种这类工具:

2.1 ttf2bmp

是一个制作字符库的简单工具,并且它是开源的,有了它我们就可以轻松制作图片字符库了。工具如下图:

ttf2bmp工具截图

我们将需要的字符编码范围确定下来,在Min char中输入最小字符编码,在Max char中输入最大字符编码,然后点击Export就可以生成类似于下面的图片:

ttf2bmp生成的字符集图片

将上图添加到XNAGameFontContent下面的Fonts文件夹中,接下来就可以写代码使用了,当然这时我们的字符图片在XNAGameFontContent下面中默认的是Textrue图片类型,还需要修改图片的Processor属性为Sprite Font Texture - XNA Framework。具体使用代码如下:

ExpandedBlockStart.gif
 
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
Microsoft.Xna.Framework;
using
Microsoft.Xna.Framework.Audio;
using
Microsoft.Xna.Framework.Content;
using
Microsoft.Xna.Framework.GamerServices;
using
Microsoft.Xna.Framework.Graphics;
using
Microsoft.Xna.Framework.Input;
using
Microsoft.Xna.Framework.Input.Touch;
using
Microsoft.Xna.Framework.Media;
namespace
XNAGameFont
{
public
class
MyGame : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont sfTexture;
//
Sprite Font Texture文本(ttf2bmp制作)
Vector2 sfTexturePosition;
//
Sprite Font Texture文本位置
public
MyGame()
{
graphics
=
new
GraphicsDeviceManager(
this
);
Content.RootDirectory
=
"
Content
"
;
graphics.PreferredBackBufferWidth
=
480
;
graphics.PreferredBackBufferHeight
=
800
;
TargetElapsedTime
=
TimeSpan.FromTicks(
333333
);
}
protected
override
void
Initialize()
{
sfTexturePosition
=
new
Vector2(
60
,
350
);
base
.Initialize();
}
protected
override
void
LoadContent()
{
spriteBatch
=
new
SpriteBatch(GraphicsDevice);
sfTexture
=
Content.Load
<
SpriteFont
>
(
@"
Fonts/SpriteFontTexture
"
);
}
protected
override
void
UnloadContent()
{
}
protected
override
void
Update(GameTime gameTime)
{
if
(GamePad.GetState(PlayerIndex.One).Buttons.Back
==
ButtonState.Pressed)
this
.Exit();
base
.Update(gameTime);
}
protected
override
void
Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.DrawString(sfTexture,
"
Sprite Font Texture
"
, sfTexturePosition, Color.Yellow);
spriteBatch.End();
base
.Draw(gameTime);
}
}
}

运行效果如图:

xnaGameFont_screen_spritFontTexture_ttf2

2.2 SpriteFont2

准确的来说应该是对ttf2bmp的扩展,默认的ttf2bmp生成的字体效果比较单一(当然也可以利用一些图形处理工具(如:Photoshop)来对生成的图片进行处理),SpriteFont2则可以制作出更炫的效果,例如字体填充色、边框、投影、发光等。下面是工具的截图:

SpriteFont2工具截图

使用这个工具我们制作下面一张Sprite Font Texture图片:

SpriteFont2生成的字符集图片

然后添加到XNAGameFontContent项目的Fonts文件夹,当然别忘了修改片的Processor属性为Sprite Font Texture - XNA Framework。然后再就可以在程序中使用我们制作的字体:

ExpandedBlockStart.gif
 
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
Microsoft.Xna.Framework;
using
Microsoft.Xna.Framework.Audio;
using
Microsoft.Xna.Framework.Content;
using
Microsoft.Xna.Framework.GamerServices;
using
Microsoft.Xna.Framework.Graphics;
using
Microsoft.Xna.Framework.Input;
using
Microsoft.Xna.Framework.Input.Touch;
using
Microsoft.Xna.Framework.Media;
namespace
XNAGameFont
{
public
class
MyGame : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont sfTextureExtend;
//
Sprite Font Texture文本效果扩展(spritefont2制作)
Vector2 sfTextureExtendPosition;
//
Sprite Font Texture Extend文本位置
public
MyGame()
{
graphics
=
new
GraphicsDeviceManager(
this
);
Content.RootDirectory
=
"
Content
"
;
graphics.PreferredBackBufferWidth
=
480
;
graphics.PreferredBackBufferHeight
=
800
;
TargetElapsedTime
=
TimeSpan.FromTicks(
333333
);
}
protected
override
void
Initialize()
{
sfTextureExtendPosition
=
new
Vector2(
100
,
330
);
base
.Initialize();
}
protected
override
void
LoadContent()
{
spriteBatch
=
new
SpriteBatch(GraphicsDevice);
sfTextureExtend
=
Content.Load
<
SpriteFont
>
(
@"
Fonts/SpriteFontTexture2
"
);
}
protected
override
void
UnloadContent()
{
}
protected
override
void
Update(GameTime gameTime)
{
if
(GamePad.GetState(PlayerIndex.One).Buttons.Back
==
ButtonState.Pressed)
this
.Exit();
base
.Update(gameTime);
}
protected
override
void
Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.DrawString(sfTextureExtend,
"
SFT 2
"
, sfTextureExtendPosition, Color.White);
spriteBatch.End();
base
.Draw(gameTime);
}
}
}

运行效果:

SpriteFont2制作的字符集运行效果图

2.3 XNA Bitmap Font Plus

不是一个直接按照字符编码范围生成相关字体图片的工具,它的字体来源于图片或者剪贴板,这里我们主要说如何从剪贴板加载字体,首先我们在PowerPoint中按顺序编辑好我们常用的字符,就是ASCII从32-126之间的字符:

ExpandedBlockStart.gif
 
!
"
#$%&'()*+,-./
0123456789
:;
<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\]
^
_
`abcdefghIjklmno
pqrstuvwxyz{
|
}
~

然后调整字符的样式,修改字符间距:

调整过的字符在powerpoint中截图

接着 ctrl+c复制,此时字符信息复制到了剪贴板,打开XNA Bitmap Font Plus,它会自动加载剪贴板的内容,如下图:

粘贴过之后打开XNA Bitmap Font Plus后的截图

当然此时我们需要调整字符行数、偏移量和间隔等信息,点击Generate Bitmap Font按钮查看效果是否符合我们的要求:

调整后的XNA Bitmap Font Plus截图

当调整好之后,我们就可以点击Save Image As…按钮来保存生成的图片:

XNA Bitmap Font Plus生成的字符集图片

最后将图片添加到XNAGameFontContent项目的Fonts文件夹,修改图片的Processor属性为Sprite Font Texture - XNA Framework。就可以在程序中使用我们制作的字体:

ExpandedBlockStart.gif
 
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
Microsoft.Xna.Framework;
using
Microsoft.Xna.Framework.Audio;
using
Microsoft.Xna.Framework.Content;
using
Microsoft.Xna.Framework.GamerServices;
using
Microsoft.Xna.Framework.Graphics;
using
Microsoft.Xna.Framework.Input;
using
Microsoft.Xna.Framework.Input.Touch;
using
Microsoft.Xna.Framework.Media;
namespace
XNAGameFont
{
public
class
MyGame : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont sfTexturePlus;
//
Sprite Font Texture文本(xna bitmap font plus制作)
Vector2 sfTexturePlusPosition;
//
Sprite Font Texture 文本位置
public
MyGame()
{
graphics
=
new
GraphicsDeviceManager(
this
);
Content.RootDirectory
=
"
Content
"
;
graphics.PreferredBackBufferWidth
=
480
;
graphics.PreferredBackBufferHeight
=
800
;
TargetElapsedTime
=
TimeSpan.FromTicks(
333333
);
}
protected
override
void
Initialize()
{
sfTexturePlusPosition
=
new
Vector2(
40
,
350
);
base
.Initialize();
}
protected
override
void
LoadContent()
{
spriteBatch
=
new
SpriteBatch(GraphicsDevice);
sfTexturePlus
=
Content.Load
<
SpriteFont
>
(
@"
Fonts/SpriteFontTexture3
"
);
}
protected
override
void
UnloadContent()
{
}
protected
override
void
Update(GameTime gameTime)
{
if
(GamePad.GetState(PlayerIndex.One).Buttons.Back
==
ButtonState.Pressed)
this
.Exit();
base
.Update(gameTime);
}
protected
override
void
Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.DrawString(sfTexturePlus,
"
Sprite Font Texture 3
"
, sfTexturePlusPosition, Color.White);
spriteBatch.End();
base
.Draw(gameTime);
}
}
}

运行效果:

xnaGameFont_screen_spritFontTexture_xnaB

下面给出所有以上这几种字体综合到一起的代码:

ExpandedBlockStart.gif
 
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
Microsoft.Xna.Framework;
using
Microsoft.Xna.Framework.Audio;
using
Microsoft.Xna.Framework.Content;
using
Microsoft.Xna.Framework.GamerServices;
using
Microsoft.Xna.Framework.Graphics;
using
Microsoft.Xna.Framework.Input;
using
Microsoft.Xna.Framework.Input.Touch;
using
Microsoft.Xna.Framework.Media;
namespace
XNAGameFont
{
public
class
MyGame : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont sf1;
//
SpriteFont文本
Vector2 sfPosition;
//
SpritFont文本所在位置
SpriteFont sfChinese;
//
SpriteFont中文文本
Vector2 sfChinesePosition;
//
SpriteFont中文文本的显示位置
SpriteFont sfTexture;
//
Sprite Font Texture文本(ttf2bmp制作)
Vector2 sfTexturePosition;
//
Sprite Font Texture文本位置
SpriteFont sfTextureExtend;
//
Sprite Font Texture文本效果扩展(spritefont2制作)
Vector2 sfTextureExtendPosition;
//
Sprite Font Texture Extend文本位置
SpriteFont sfTexturePlus;
//
Sprite Font Texture文本(xna bitmap font plus制作)
Vector2 sfTexturePlusPosition;
//
Sprite Font Texture 文本位置
public
MyGame()
{
graphics
=
new
GraphicsDeviceManager(
this
);
Content.RootDirectory
=
"
Content
"
;
graphics.PreferredBackBufferWidth
=
480
;
graphics.PreferredBackBufferHeight
=
800
;
TargetElapsedTime
=
TimeSpan.FromTicks(
333333
);
}
protected
override
void
Initialize()
{
sfPosition
=
Vector2.Zero;
sfChinesePosition
=
new
Vector2(
0
,
100
);
sfTexturePosition
=
new
Vector2(
0
,
200
);
sfTextureExtendPosition
=
new
Vector2(
0
,
300
);
sfTexturePlusPosition
=
new
Vector2(
0
,
400
);
base
.Initialize();
}
protected
override
void
LoadContent()
{
spriteBatch
=
new
SpriteBatch(GraphicsDevice);
sf1
=
Content.Load
<
SpriteFont
>
(
@"
Fonts/SpriteFont
"
);
sfChinese
=
Content.Load
<
SpriteFont
>
(
@"
Fonts/SpriteFontForChinese
"
);
sfTexture
=
Content.Load
<
SpriteFont
>
(
@"
Fonts/SpriteFontTexture
"
);
sfTextureExtend
=
Content.Load
<
SpriteFont
>
(
@"
Fonts/SpriteFontTexture2
"
);
sfTexturePlus
=
Content.Load
<
SpriteFont
>
(
@"
Fonts/SpriteFontTexture3
"
);
}
protected
override
void
UnloadContent()
{
}
protected
override
void
Update(GameTime gameTime)
{
if
(GamePad.GetState(PlayerIndex.One).Buttons.Back
==
ButtonState.Pressed)
this
.Exit();
base
.Update(gameTime);
}
protected
override
void
Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.DrawString(sf1,
"
SpriteFont
"
, sfPosition, Color.White);
spriteBatch.DrawString(sfChinese,
"
中文字体
"
, sfChinesePosition, Color.Red);
spriteBatch.DrawString(sfTexture,
"
Sprite Font Texture
"
, sfTexturePosition, Color.Yellow);
spriteBatch.DrawString(sfTextureExtend,
"
SFT 2
"
, sfTextureExtendPosition, Color.White);
spriteBatch.DrawString(sfTexturePlus,
"
Sprite Font Texture 3
"
, sfTexturePlusPosition, Color.White);
spriteBatch.End();
base
.Draw(gameTime);
}
}
}

最终效果:

最终综合效果图

OK,就到这里吧!

最后附上源代码(上面提到的几个工具,都可以点击相关链接下载):

转载地址:http://qnikl.baihongyu.com/

你可能感兴趣的文章
css中内容出现滚动时不影响布局解决办法
查看>>
jmeter与java_selenium自动化
查看>>
文件属性 权限的管理
查看>>
Linux 练习题-4网络 命令
查看>>
代码健康:如何利用代码审查的机会提升你的代码?
查看>>
CentOS7安装elasticsearch-head
查看>>
我的Linux之Linux系统一日游
查看>>
laravel 分页
查看>>
如何给在用的nginx添加新模块
查看>>
自然语言处理工具HanLP被收录中国大数据产业发展的创新技术新书《数据之翼》...
查看>>
单臂路由的配置
查看>>
jQuery相关面试题
查看>>
zabbix监控软件
查看>>
Linux搭建DNS服务
查看>>
前端开发程序员薪资报告:企业到底想要什么样的前端?
查看>>
JavaScript 代码整洁之道
查看>>
棋牌管理系统用例图
查看>>
解决embed标签显示在div上层,非设置z-index
查看>>
实现 node_modules 共享
查看>>
删除字符串开始及末尾的空白符,并且把数组中间的多个空格(如果有)符转化为1个...
查看>>