为了绘制一个三角形基元,要定义一个VertexPosiitonColor或
VertexPositionTexture类型的数组来存放三角形的每个角。前者是绘制具有位置和颜色的顶点,后者是绘制具有位置和纹理的顶点。接着,创建一个VertexBuffer对象来储存这些点。
要将这些定义后的点绘制在屏幕上,一个关键对象是BasicEffect,它是XNA中的基本着色器。简单地说,XNA 3D中的一切都必须使用效果(一般是高级着色器语言,即High Level Shader Language, HLSL)来绘制。HLSL使用C语言为基础,他允许开发人员方便地创建强大的效果,比如水面波纹、反射表面和其他华丽的效果。开发人员可以自定义效果,也可以使用XNA默认的BasicEffect来绘制图像。
实际在屏幕上绘制三角形的动作是通过一个
GraphicsDevice.DrawUserPrimitives
该方法第一个参数是用于绘制三角形的方法,XNA允许用三种不同的方式绘制基元三角形:三角形列表,三角形strip,三角形fan。
三角形列表获取指定的前三个顶点,并根据它们绘制一个三角形。然后,每一组额外的3个顶点都将绘制一个额外的三角形。
三角形strip也会根据指定的前3个顶点构造一个三角形,但每个额外的顶点都会连接它之前的两个顶点构成一个新三角形。
三角形fan也是使用前3个顶点构造一个三角形,每个额外的顶点也会构造一个新三角形,但使用的是新顶点、它之前的顶点以及第一个顶点。
知道了如何绘制三角形后,DrawUserPrimitive方法的后3个参数会告诉设备要绘制的点集,要从哪个顶点开始绘制以及要绘制多少个基元。
3平移和旋转
在3D视图中,要平移一个物体,可以使用Matrix.CreateTranslation方法来进行。该方法返回一个Matrix对象
“平移”(translation)是按照一个向量指定的方向移动物体或者点。因此,CreateTranslation方法要获取一个Vector3参数。
为了能够使用Matrix.CreateTranslation方法,需要创建一个world对象来记录物体在3D世界中的位置,它是Matrix类型的。然后用矩阵乘法将world对象与Matrix.CreateTranslation方法返回的对象相乘获得物体在世界中的新的位置。之后通过effect效果的World属性将该位置应用到屏幕绘制上。
旋转物体有几种不同的方法,一种是通过Matrix.CreateRotationX、
Matrix.CreateRotationY、Matrix.CreateRotationZ方法分别围绕X,Y或Z轴旋转。或者通过Matrix.CreateFromYawPitchRoll方法是物体同时可围绕三个坐标轴进行旋转。
当然,在实际游戏中,不管是摄像机还是物体,都不可能只绕坐标轴旋转,这就会使用到Matrix.CreateFromAxisAngle方法,它时方法调用对象能够围绕指定的轴旋转特定的角度。
旋转带来的另一个问题就是所谓的“隐面消除”(backface culling)。在
3D图形中,“消除”是对屏幕上绘制的物体的数量进行限制从而提升性能的一个过程。简单的说,隐面消除只是绘制基元面向摄像机的那一面。在真正的游戏中,物体都是由3D模型构成的,这些物体实际是包裹着空白空间的一层“皮肤”,皮肤本身则有一系列三角形构成。没有人会关心皮肤的内在空间是什么样子的,这就是隐面消除的作用,只绘制摄像机应该看到的部分。
XNA默认消除逆时针绘制的基元。在必要时,也可以关闭隐面消除功能。只要将光栅的隐面模式CullMode该为None即可。
4 3D模型
创建3D游戏不可缺少的一部分就是使用3D模型,可以说,3D模型就是3D游戏的核心。而我们不可能通过绘制基元的方式创建3D模型,这对游戏开发来说是不现实也不可行的。可以通过第三方建模软件如3D Studio Max, Maya, Blender, Lightwave和Modo来创建复杂的3D模型。XNA支持.X和.FBX文件格式的3D模型。在XNA项目中加载和绘制这些模型文件,不必指定每个单独的顶点和纹理。这样就可以集中精力解决模型的移动、旋转、操作以及其他与游戏相关的问题。
XNA中用来代表模型的类是Model类。模型的绘制是需要一些技巧的。每个模型对象都可以包含多个对象,这些对象成为mesh,它们存储在模型中。XNA的Model类用ModelMesh对象来表示这些mesh。Model类的Meshes属性包含一个ModelMesh对象列表。
mesh中可包含在绘制该mesh时使用“材质”(material),包括颜色和纹理等。mesh的各个部件不要求以相同方式上色或添加纹理。可以在一个mesh中使用多个材质。为了存储这些数据,mesh由多个“部件”(part)构成。ModelMesh类的MeshParts属性存储着一个ModelMeshPart对象列表。每个ModelMeshPart对象都包含绘制该部件所需的材质和一个Effect对象。
ModelMeshPart所用的Effect默认为BasicEffect类型。开发人员也可以定制自己的效果文件用于不同的mesh对象。
每个ModelMesh都有一个“变换”(transformation),能将mesh移到模型中的适当位置。这使得模型在3D坐标系中改变位置后,它的各个部件仍然能够自动改变它们的相对位置。
5 3D碰撞检测
3D空间下的碰撞检测与2D空间下的碰撞检测既有相似点又有区别。在2D空间下,最简单的碰撞检测方法使用包围框或包围盒(bounding-box),而在3D空间下使用的是包围球(bounding-sphere)。原理是一样——检查物体的包围球是否和另一个物体的包围球发生碰撞。
在XNA中使用模型时,包围球是作为模型的一部分生成的。每个模型都包含一个或多个ModelMesh对象,而每个ModelMesh都有一个名为BoundingSphere的属性,定义了围绕模型那个部分的包围球。
需要注意的一点时,向模型应用一个平移或缩放时,模型的BoundingSphere不会受到影响。因此,为了使用模型中指定的BoundingSphere,必须向它应用
相同的平移和缩放。
当同一3D空间中的两个模型发生碰撞时,每个模型的碰撞算法会遍历它自身的所有ModelMesh。针对每个ModelMesh,都遍历另一个Model的ModelMesh,看两者的BoundingSphere是否发生碰撞。比较两个对象的包围球之前,方法必须使用Transform方法向BoundingSphere应用世界矩阵,这会将包围球移动(和旋转)到它的模型所移动(和旋转)到的位置。
四 小结
本文通过简单的说明介绍了利用微软的XNA游戏编程框架将如何开发基本的2D游戏与3D游戏,以及在开发过程中的一些重要概念和技术。XNA作为一款跨平台游戏编程框架,能够轻松实现windows,Xbox360和windows phone的跨平台开发,为开发者提供了莫大的便利。利用XNA的优秀特性和框架,开发人员能够开发出功能强大的2D与3D游戏。有了内容管道和各种类的封装,开发人员在开发过程中将把主要精力集中在游戏逻辑的实现上,从而以高效的方式创造出优秀好玩的游戏。