diff --git a/docs/12-game-hud.md b/docs/12-game-hud.md index 105ae16..55572eb 100644 --- a/docs/12-game-hud.md +++ b/docs/12-game-hud.md @@ -1,25 +1,24 @@ # 游戏HUD(Game HUD) -本章中我们将为游戏实现一个HUD(平视显示器)。换句话说,就是在三维场景上用一组二维图形和文本显示相关信息。我们将创建一个简单的HUD,接下来将说明一些如何显示这些信息的基本方法。 +在本章中,我们将为游戏创建一个HUD(Heads-Up Display,平视显示器)。换句话说,就是一组用于在三维场景上,随时显示相关信息的二维图形和文本。本例中将创建一个简单的HUD,这可为我们展现一些显示信息的基本技术。 -当你查看本章的源代码时,还将看到我们重构了一些代码,特别是`Renderer`类,以便为HUD渲染做好准备。 +当你查阅本章的源代码时,还将发现我们对源代码做了一些小的重构,特别是`Renderer`类,以便为HUD渲染做好准备。 ## 文本渲染 -创建HUD所要做的第一件事是渲染文本。为了实现它,我们将把包含字母字符的纹理的纹理映射到一个矩形中,该矩形将被分割为一组表示各个字符的片段。之后,我们将使用该纹理绘制文本。所以第一步是创建含有所有字符的纹理,你可以使用很多程序来做,例如[CBFG](http://www.codehead.co.uk/cbfg/)、[F2IBuilder](http://sourceforge.net/projects/f2ibuilder/)等等。现在我们使用Codehead’s Bitmap Font Generator(CBFG)。 +创建HUD所要做的第一件事是渲染文本。为了实现它,我们将储存字符的纹理映射到一个方形中,该方形将被分割为一组表示每个字符的片段。之后,我们将使用该纹理在屏幕上绘制文本。所以首先创建含有所有字母的纹理,这项工作可以使用很多软件来做,例如[CBFG](http://www.codehead.co.uk/cbfg/)、[F2IBuilder](http://sourceforge.net/projects/f2ibuilder/)等等。本例使用Codehead的位图字体生成器(Codehead’s Bitmap Font Generator,CBFG)。 -CBFG有很多设置,例如纹理大小、字体类型、要使用的反走样等等。下图是我们将用来生成纹理文件的配置。在本章中,我们将假设文本编码为ISO-8859-1格式,如果需要处理不同的编码格式,则需要稍微修改代码。 +CBFG允许你配置很多选项,如纹理大小、字体类型、要使用的抗锯齿等等。下图是本例将用来生成纹理文件的配置。在本章中,我们将假设文本编码为ISO-8859-1,如果你需要处理其他的字符集,则需要稍微修改代码。 ![CBFG配置](_static/12/CBG.png) -当你设置CBFG的所有配置后,可以导出为多种图片格式。现在我们将它导出为BMP文件,然后再转换为PNG文件,以便将它作为纹理加载。当转换为PNG格式时,我们也可以将黑色背景设置为透明,也就是说,我们将黑色的Alpha值设置为0(可以使用GIMP这样的工具)。最后,你会得到与下图类似的东西。 +当设置好CBFG的所有选项后,可以将其导出为多种图片格式。现在我们将其导出为BMP文件,然后再转换为PNG文件,以便将其作为纹理加载。当转换为PNG格式时,我们也可以将黑色背景设置为透明,也就是说,我们将黑色设为Alpha值等于0(可以使用GIMP这样的工具来实现)。最终你会看到类似下图所示的结果。 ![字体纹理](_static/12/font_texture.png) -如上所试,所有的字符都排列在图像中。现在图像有15列和17行。通过 -As you can see, the image has all the characters displayed in rows and columns. In this case the image is composed by 15 columns and 17 rows. By using the character code of a specific letter we can calculate the row and the column that is enclosed in the image. The column can be calculated as follows: $$column = code \space mod \space numberOfColumns$$. Where $$mod$$ is the module operator. The row can be calculated as follows: $$row = code / numberOfCols$$, in this case we will do a integer by integer operation so we can ignore the decimal part. +如你所见,图像中的所有字符都以行和列的形式排列。在本例中,图像由15列和17行字符组成。通过使用特定字符的编号,我们可以计算其对应储存在图像中的行和列。所在列的计算方法为:$列数 = 字符编号 \space mod \space 列总数$,其中$mod$是取余运算符,所在行的计算方法为:$所在行 = 字符编号 / 行总数$。在本例中我们将整数除以整数,以便忽略小数部分。 -我们将创建一个名为`TextItem`的类,它将储存渲染文本所需的内容。这是一个简化的实现,不考虑多行文本等特性,但它能在HUD中显示文本信息。下面是这个类的实现。 +我们将创建一个名为`TextItem`的新类,它将储存渲染文本所需的图元。这是一个不考虑多行文本的简化实现,但是它能在HUD中显示文本信息。下列代码是该类的声明与构造函数: ```java package org.lwjglb.engine; @@ -53,31 +52,31 @@ public class TextItem extends GameItem { } ``` -这个类继承了`GameItem`,这是因为我们希望改变文本在屏幕上的位置,也可能需要缩放和旋转它。构造函数接收腰显示的文本和用于渲染的纹理数据(包括图像数据和行列数目)。 +这个类继承了`GameItem`类,这是因为本例希望改变在屏幕上文本的位置,也可能需要缩放和旋转它。构造函数接收要渲染的文本和用于渲染的纹理的文件和相关数据(储存图像的文件及行列数)。 -在构造函数中,我们加载纹理图像文件,并调用一个方法来创建一个`Mesh`实例用于模拟文本。让我们看看`buildMesh`方法。 +在构造函数中,我们加载纹理图像文件,并调用一个方法来创建一个`Mesh`实例为文本建模。让我们看到`buildMesh`方法: ```java private Mesh buildMesh(Texture texture, int numCols, int numRows) { byte[] chars = text.getBytes(Charset.forName("ISO-8859-1")); int numChars = chars.length; - List positions = new ArrayList(); - List textCoords = new ArrayList(); + List positions = new ArrayList<>(); + List textCoords = new ArrayList<>(); float[] normals = new float[0]; - List indices = new ArrayList(); + List indices = new ArrayList<>(); float tileWidth = (float)texture.getWidth() / (float)numCols; float tileHeight = (float)texture.getHeight() / (float)numRows; ``` -代码创建了用于储存Mesh的位置、纹理坐标、法线和索引的数据结构。现在我们不使用照明,因此法线数列是空的。我们要做的是构造一组字符片段,每个字符片段代表一个字符。我们还需要根据每个字符片段对应的字符来分配适当的纹理坐标。下图表示了文本矩形和字符片段的关系。 +代码创建了用于储存Mesh的位置、纹理坐标、法线和索引的数据结构。现在我们不使用光照,因此法线数列是空的。我们要做的是构造一组字符片段,每个字符片段代表一个字符。我们还需要根据每个片段对应的字符来分配对应的纹理坐标。下图展现了文本矩形和字符片段的关系: ![文本矩形](_static/12/text_quad.png) -因此,对于每个字符,我们需要创建由两个三角形构成的字符片段,这两个三角形可以用四个顶点(V1、V2、V3和V4)定义。第一个三角形(左下角的那个)的索引为(0, 1, 2),而第二个三角形(右上角的那个)的索引为(3, 0, 2)。纹理坐标是基于与纹理图像中每个字符相关连的行和列计算的,纹理坐标的范围为[0,1],所以我们只需要将当前行或当前列除以总行数和总列数就可以获得V1的坐标。对于其他顶点,我们只需要适当加上行宽或列宽就可以。 +因此,对于每个字符,我们需要创建由两个三角形构成的字符片段,这两个三角形可以用四个顶点(V1、V2、V3和V4)定义。第一个三角形(左下角的那个)的索引为(0, 1, 2),而第二个三角形(右上角的那个)的索引为(3, 0, 2)。纹理坐标是基于与纹理图像中每个字符相关的行列计算的,纹理坐标的范围为[0, 1],所以我们只需要将当前行或当前列除以总行数和总列数就可以获得V1的坐标。对于其他顶点,我们只需要适当加上行宽或列宽就可以得到对应坐标。 -下面的循环语句块就创建了与显示文本的矩形相关的所有顶点、纹理坐标和索引。 +下述的循环语句块创建了与渲染文本矩形相关的所有顶点、纹理坐标和索引。 ```java for(int i=0; i