基于XNA的游戏编程文档格式.docx
《基于XNA的游戏编程文档格式.docx》由会员分享,可在线阅读,更多相关《基于XNA的游戏编程文档格式.docx(11页珍藏版)》请在冰豆网上搜索。
XiaZhichao
ZhejiangUniversity,P.R.China
2012
摘要
本文介绍了基于微软XNA技术的游戏开发基本技术和框架。
XNA是微软提出的一项新的游戏开发框架,旨在为开发者提供一个统一的开发平台,并能将制作的游戏应用在3种平台上,即PC,Xbox360和WindowsPhone,达到“3屏1云”的效果。
文章的第一部分将介绍XNA与传统ManagedDirectX的区别和XNA的新特性。
文章的第二部分,将介绍使用XNA实现2D游戏开发的基本思想和实现过程。
在文章的第三部分将介绍在XNA中开发3D游戏应掌握的基本3D图形学知识,以及用XNA开发3D游戏的基本过程和方法。
关键词:
XNA,游戏开发,2D游戏开发,3D游戏开发
Abstract
ThispaperintroducesthebasicgamedevelopmentandarchitecturebasedontheXNAtechnology.XNAisMicrosoft'
snewgamedevelopmentframework,aimedatprovidingaunifiedplatformfordevelopers,andmakesagameapplicationon3platforms,namelyPC,Xbox360andWindowsphone,toachieve"
3screen1cloud"
effect.ThefirstpartofthearticledescribesthedifferencebetweentraditionalmanagedDirectXandXNAandalsoXNA'
snewfeatures.Thesecondpartofthearticle,describesthebasicideaofusingXNAtodevelop2Dgameandtheimplementationprocess.InthethirdpartofthearticledescribesdevelopmentintheXNA3Dgameshouldhaveabasicknowledgeof3DgraphicsbyusingXNA,aswellasthebasicprocessandmethodofusingXNAtodevelop3Dgames.
Key:
XNA,gamedevelopment,2Dgamedevelopment,3Dgamedevelopment
一引言
XNAGameStudioExpress(简称XNA)是微软于2006年8月发布的最新集成开发环境,仅支持C#语言开发。
XNA中,X代表“跨平台”(Cross-Platform),即XNA可以为传统Windows平台,Xbox360和WindowsPhone三种平台开发游戏,N代表“下一代”(Next-generation),A代表“架构”(Architecture)。
微软为游戏开发者提供XNA的第一目的就是使游戏编程更加简化容易,它将检查显卡、创建Device设备、消息事件处理、纹理导入等工作统统交由XNA完成,而程序员的工作就是编写游戏的逻辑代码。
而XNA的第二目的就是实现跨平台开发游戏,即使用XNA编写的游戏代码可以同时在Windows系统和微软游戏机Xbox360上运行。
在介绍XNA与ManagedDirectX的区别之前,首先需要介绍一下ManagedDirectX。
DirectX是微软公司推出的运行于Windows操作系统的多媒体API,其中Direct3D支持2D、3D图形程序开发。
以往开发DirectX程序主要使用C++语言,相较于VB语言,用C++开发DirectX应用程序难度较大。
而DirectX概念众多,许多概念又不容易理解,所以用C++开发DirectX游戏难度较大。
而C#语言是一种现代的、面向对象的语言,它简化了C++语言在累、命名空间、方法重载和异常处理等方面的操作,它摒弃了C++的复杂性,更少出错。
2002年微软推出ManagedDirectX,也称作DirectX的托管版本,用类重新封装了DirectX的函数库,支持用C#和VB.NET语言开发DirectX程序,极大地简化了DirectX的程序设计。
一般认为,和C++相比,C#语言程序运行速度要慢许多。
但实际上经过测试,用C#语言写得3D程序的运行速度已接近于C++语言的3D程序运行速度,因此一些商业游戏已经使用C#进行创建。
并且,大多数游戏可以使用C#或同时使用C#和C++进行创建。
使用C#语言编写3D游戏程序可以降低开发难度,提高开发效率,这也是ManagedDirectX使用C#进行开发的关键所在。
而XNA是基于DirectX的游戏开发环境,是微软对于ManagedDirectX的修正和扩充版本。
XNA仍然以DirectX9.0C为基础,和ManagedDirectX有许多共同点,两者是相同的,有很好的相容性。
但XNA和ManagedDirectX不兼容,最主要的区别是不支持固定功能流水线,仅支持可编程流水线;
不再采用左手系统,而采用右手坐标系系统;
不包括ManagedDirectX中的一些类。
目前最新的XNA版本是XNA4.0,支持.NETFramework4.0。
在XNA4.0中,仍然使用和XNA3.x一样的代码从内容管道中获取内容,内容管道(ContentPipeline)是XNA中用于将游戏资源整合到统一格式下的工具,能够使开发工作更加简洁和高效。
在图形配置方面,XNAFramework支持在具有不同硬件能力的多样化设备上进行开发,包括支持DirectX10和更高版本的图形卡、XBOX360和WindowsPhone7系列。
在以前的XNA中,必须在运行时处理这些硬件上的差异,为了简化在不同设备上的开发,XNA4.0引入“配置”(profile)的概念。
这些配置允许开发人员支持一组特定的图形API,从而针对特定的硬件进行开发。
XNA4.0有两种配置:
Reach和HiDef。
HiDef为高端硬件设计,Reach为支持更大范围的硬件设备而设计。
选择使用Reach配置些游戏,会牺牲一些较强大的图形API,但能保证游戏在大量设备上运行。
HeDef设计支持当今最强大的图形设备,它面向的是Xbox360硬件和至少支持DirectX10的Windows计算机。
在以前版本的XNA中,BasicEffect类只提供非常基本的效果,它的目的是即时没有深入掌握复杂的着色器(shader)代码,也能顺利地构建新游戏。
但随着不支持定制着色器的WindowsPhone7的问世,情况发生了变化,在Reach和HiDef两种配置中,添加了新的可配置效果,诸如:
基本效果(BasicEffect)、双重纹理效果、alpha测试效果、皮肤效果(模型批量渲染)和环境贴图效果。
XNA4.0中的另外几项新特性是针对WindowsPhone7的,如XNA4.0中的标量(scalar)允许开发人员在写游戏时不必担心本机分辨率或屏幕方向,XNA会自动处理屏幕分辨率的缩放。
再如利用Microsoft.Xna.Framework.Input.Touch命名空间中的类就可以使用WindowsPhone设备的多点触摸输入功能。
这个命名空间添加了TouchPanel类和TouchLocation类,允许从输入设备访问触点。
此外,XNA4.0还支持两种音频处理方式,一种是用传统的“Microsoft跨平台音频创建工具”(MicrosoftCross-PlatformAudioCreationTool,XACT),另一种是利用简化API来处理游戏中的音频。
二用XNA开发2D游戏关键技术
1游戏类Game1类
XNA游戏开发中一个非常重要的类型是Game1类型,所有的XNA游戏项目(2D游戏和3D游戏)都会拥有Game1对象,并通过它来处理游戏逻辑和运行游戏。
Game1类中提供了2个类级变量、1个Game1构造器和5个其他方法。
第一个类级变量为graphics,它的类型是GraphicsDeviceManager。
这个对象使得开发人员能够访问PC、Xbox360和WindowsPhone7上的图形设备。
GraphicsDeviceManager对象有一个GraphicsDevice的属性,代表机器上的实际图形设备。
由于这个图形设备对象充当着XNA游戏和机器上显卡之间的桥梁,所以在XNA中,任何绘制在屏幕上的图像都是通过该对象完成的。
另一个变量是SpriteBatch类的一个实例。
这是一个“精灵”对象。
在计算机图形术语中,精灵(sprite)被定义成“能集成到更大场景中的2D或3D图像”。
2D游戏是通过在场景中绘制多个精灵(玩家、敌人、场景或道具等)来构建的。
Game1类中的Initialize方法用于初始化变量以及与Game1对象相关的其他对象。
图形设备对象将在这里实例化,并可在此方法中利用该对象初始化依赖于其设置的其他对象。
之后是LoadContent方法,在该方法中会加载游戏需要的全部图形内容和其他游戏中的资源文件,包括图片、模型(.X和.FBX格式)、声音等。
在初始化游戏资源后,Game1对象会进入游戏循环状态。
几乎所有的游戏都采用了某种形式的游戏循环。
Update方法和Draw方法负责游戏循环。
当游戏在运行时发生的所有操作包括逻辑的变更和状态的改变都是在Update中完成,这些操作包括:
移动对象、检查碰撞、更新分数、检查游戏结束逻辑等。
而Draw方法则负责将游戏中的所有“精灵”对象绘制在屏幕上。
最后,当游戏结束时,会调用Game1类中的UnloadContent方法来释放所有游戏加载进内存的资源。
2游戏轮询
游戏开发中一个很重要的概念就是“轮询”。
这个概念是游戏程序区别于普通基于事件响应的应用程序的关键所在。
所谓“轮询”,就是游戏程序在运行时,将会进入一个循环过程,由开发人员来决定在每个循环过程中应该如何改变游戏状态(gamestatus)。
为了能够改变游戏状态,开发人员需要在Update方法中负责更新所有与游戏相关的事件,包括对象在屏幕上的位置、分数、动画序列等,还可以检查用户的输入、检测碰撞以及调整AI算法。
游戏状态通常分为:
启动画面(splashscreen),进行游戏(playingthegame)和游戏结束画面(end-gamescreen)。
状态还有可能发生细微的变化,如用户可能获得某种形式的法宝(power-up),使他们进入无敌态或被动态等等。
如上所述,通常在Update中修改游戏状态,在Draw方法中利用这些状态绘制与这个特定状态关联的不同图像、场景或其他信息。
一个典型的XNA的游戏生命周期包括Initialize、LoadContent、Update、Draw和UnloadContent。
其中Initialize和LoadContent负责初始化游戏对象并且载入游戏资源。
Update和Draw方法负责游戏轮询,Update会告诉游戏是否继续进行游戏,调用Draw方法,或者直接结束游戏,调用UnloadContent来释放所有游戏资源,这项工作一般是由XNA自动完成的。
3修改游戏
前面说过,Game1类通过Update方法改变游戏状态,但最终一切游戏中的对象都必须通过Draw方法绘制到屏幕上。
Draw方法的目的是以层叠的形式调用GameComponent和其他方法中的Draw方法。
Draw方法是个虚方法(virtualmethod),它能够被子类所重写,这就意味着子类(游戏对象)能够自定义显示方式,从而让游戏的显示多样化。
下面来讨论下帧和帧频。
XNA的游戏循环默认是每秒运行60次,这就说明每秒游戏都要进行60次Update和Draw方法的调用。
为什么要进行这么多次调用?
这是因为人眼能分辨的最小帧频时24fps,也就是说,如果一段连续的画面能够在一秒钟显示24次以上,人眼就分辨不出它是连续的动画还是静止的图片。
我们通常用fps来表示帧频,24fps就表示每秒24帧,即每帧的时间大约是41.667ms,而60fps就表示每帧的时间大约是16.667ms。
XNA的默认帧频时60fps,即每秒在屏幕上绘制60次,这样就能形成动态的效果。
而Draw方法正是通过GameTime类型的参数来确定游戏的帧频。
那么该如何添加和绘制精灵(sprite)呢?
首先我们需要在Game1类中添加一个新类型Texture2D,用它来代表2D图像。
在XNA中,所有图形、场景、效果都是通过内容管道(contentpipe)加载,它能够统一文件的格式,使程序能够轻松的识别内容,当然,内容管道还能加载声音文件、3D模型、字体等。
创建好Texture2D对象并且通过LoadContent方法将图形加载到内容管道后,就可以在Draw方法中绘制精灵了。
Draw方法绘制2D图形是通过SpriteBatch对象完成的,它被定义为一个精灵画刷,并且成对出现,由它的Begin方法开始,End方法结束,所有绘制方法都应在它们中间完成。
而SpriteBatch的Draw方法是一个重载方法,它可以接受许多不同的参数,基本的参数有texture、position和color,它们指定了精灵——也就是图形对象,应该绘制在屏幕中的位置,以及该为它们填充什么颜色。
当然,你完全可以选择其他重载版本来更加细化地绘制图形,比如通过rotation参数来旋转图形,用scale参数来改变图形比例,用rectangle参数来绘制指定位置的矩形,通过layerDepth参数改变层深度等等。
当然,在绘制图形前,别忘了修改Update方法改变游戏状态,否则图形不会发生改变。
42D动画
XNA中实现2D动画的一个普遍方法是通过“精灵表(spritesheet)”来实现。
精灵表是一张m行n列的图像矩阵,其中的每个元素都是一个动画中的一帧,通过Update方法来不断更新矩阵中的图像帧并通过Draw方法显示在屏幕上从而实现动画效果。
为了能够提取精灵表中的各个图片,需要知道如下参数:
1.精灵表中每张图片(或者帧)的高度和宽度;
2.精灵表的行、列总数;
3.对精灵表中的当前行、列进行标示的一个索引。
然后,在Draw方法中的绘制方法如下:
spriteBatch.Draw(texture,Vector.Zero,
newRectangle(currentFrame.X*frameSize.X,
currentFrame.Y*frameSize.Y,
frameSize.X,
frameSize.Y),
Color.White,0,Vector2.Zero,1,SpriteEffects.None,0);
注意该方法中Rectangle对象,它的构造函数的前两个参数用于获取精灵表中指定位置的图像的坐标,后两个数用于确定绘制图像的大小。
但要让图像最终动起来,还需要在Update方法中改变索引,改变索引的方法如下:
++currentFrame.X;
if(currentFrame.X>
=sheetSize.X)
{
currentFrame.X=0;
++currentFrame.Y;
if(currentFrame.Y>
=sheetSize.Y)
currentFrame.Y=0;
}
首先改变索引的列坐标,当列坐标越界时,则将列坐标重置为0,同时增加行坐标,以绘制精灵表的下一行。
最后,如果行坐标越界,则将行坐标归0,从帧索引(0,0)开始从头绘制整个动画序列。
5游戏组件
游戏开发中的另一项非常重要的概念是游戏组件,通过游戏组件的形式将游戏的各个组成部分有条理的组合在一起。
试想一下,如果将所有的游戏逻辑实现都放在Game1类中,这将是件非常可怕的事情,而且这也会使C#语言完美的面向对象特性变得毫无用武之处。
幸好XNA提供了一种相当好的方式将代码的不同逻辑部分集成到应用程序中。
CameComponent类允许以模块方式将任意代码插入应用程序,并将那个组件自动集成到游戏循环的Update调用中,即,一旦执行游戏的Update调用,所有关联的GameComponent类也会自动调用它们的Update方法。
同时,GameComponent类中会自动集成Initialize、LoadContent、Draw等方法,这些和Game1类是一样的。
但是如果希望创建的游戏组件同时与游戏循环的Draw方法相关联,是组件同时具有绘制能力,就要使GameComponent从DrawableGameComponent派生。
有了GameComponent类后,可以以它为模板创建一个SpriteManager类,用来管理所有在游戏中用到的对象。
然后分别创建不同的具有继承层次的Sprite游戏精灵类来完成不同的游戏对象的不同的Update和Draw方法,最后再通过SpriteManager类统一管理这些游戏对象,添加它们之间的游戏逻辑。
6碰撞检测
一个游戏的用户体验是好是坏,主要依赖于碰撞检测。
如果没有碰撞检测,游戏中的对象,主角也好,场景也罢,都无法进行信息的交互,那么这个游戏也就没有可玩性了。
但在设计算法是也要适可而止,因为算法愈精确,游戏玩起来可能就愈加“艰难险阻”。
所以,在设计碰撞检测算法时,要在准确性和性能之间取得一个平衡。
实现碰撞检测最简单、最快捷的方式之一就是使用“包围框”(bounding-box)算法。
也就是在屏幕上围绕每个物体“画”一个框(隐形的框),再检测这些框本身是否相交,从而触发碰撞算法。
2D游戏中包围框算法的实现要用到XNA中的一个类Rectangle,顾名思义,即矩形类,该类提供了一个Intersects方法用于判断两个矩形是否相交。
可以先用不同的矩形类将所有应该检测碰撞的对象包装起来,再在游戏循环中检测它们是否相交,也就是碰撞,然后完成一些碰撞后的逻辑。
当然,为了精确碰撞检测算法,就得为图像添加多个更为精确的小的包围框。
但也不能强求碰撞检测要百分百的准确,只要玩家察觉不到差别就可以了,因为越精确的碰撞检测会造成性能的下降。
试想一下,两个图像一开始只用一个包围框分别包裹起来,那么检测碰撞只要计算两次就可以识别。
但如果将每个图像用5个小的包围框包裹起来,那么每次检测就要耗费25次运算,并且当包围框越多,对象越多的时候,这种运算的开销会更大。
所以再考虑到性能的前提下,包围框并不是做得越精细越好,只要能体现出碰撞效果适可而止即可。
三用XNA开发3D游戏的关键技术
1坐标系与摄像机
处理3D图形是,第一个根本的区别是增加了额外的一维。
XNA的2D编程类似于在一块画布上作画,屏幕左上角的坐标是(0,0),X轴向右为正,Y轴向下为正。
而3D绘图更像是用一台手持摄像机拍摄视频。
3D空间的定位要依赖于3维坐标系统。
不同于2D绘图,3D绘图需要两个基本组件用于绘制一个场景:
将物体放到世界中,再将摄像机放到世界中,并指定摄像机对准哪个方向。
只有摄像机看到的物体才会在屏幕上可见。
与ManagedDirectX不同,XNA采用的是右手坐标系。
定义好坐标系后,就必须确定摄像机对象。
定义摄像机时,它的位置、指向等属性存储在Matrix(矩阵)对象中。
XNA的摄像机由两个Matrix对象组成:
试图矩阵和投影矩阵。
视图(View)矩阵用于实现世界坐标向摄像机坐标的转换,它容纳的信息决定了摄像机在世界中的位置、它的指向以及它自己的摆放方向。
投影(projection)矩阵用于实现摄像机坐标系向屏幕坐标系的转换,它容纳的信息决定了如何在屏幕上观察3D世界,具体如何观察要取决于摄像机的属性,包括视角、视距等。
为了创建试图矩阵,要使用Matrix的静态方法CreateLookAt。
该方法返回一个Matrix对象。
另外,该方法接受3个Vector3类型的参数,Vector3是用来表示3D坐标的类型。
第一个参数是cameraPosition,用来创建摄像机的位置;
第二个参数cameraTarget,用来确定摄像机指向目标位置的坐标,即指向;
第三个参数是cameraUpVector,用来代表哪个方向是“上”的Vector3对象。
尤其要注意第三个参数,它决定的摄像机的摆放姿势,是竖直、水平或是倾斜,它和cameraTarget共同决定了摄像机的指向和如何摆放。
要创建投影矩阵,要使用Matrix的另一个静态方法Matrix.CreatePerspectiveFieldOfView。
该方法也返回一个Matrix对象,并接受4个float类型的变量,分别是:
fieldOfView,它决定了摄像机的视角,以弧度为单位,一般为45°
,或π/4;
aspectRatio,它是摄像机的宽高比,一般设置为屏幕的宽高比;
nearPlaneDistance,它是摄像机观察一个物体的最近视距(再近就看不到了);
farPlaneDistance,它是摄像机观察一个物体的最远视距(再远就看不到了)。
投影矩阵够早的是摄像机的视锥或视野,它定义了3D空间中的一块区域,在这块区域内的物体都会被摄像机捕捉到并显示在屏幕上。
要在XNA的3D游戏中构建摄像机需要创建一个Camera(摄像机)类,在这个类中定义试图矩阵和投影矩阵以确定一个摄像机。
2绘制基元
3D绘图中最基本东西就是三角形,它是3D绘图最主要使用的一种“基元”(primitive)。
只要三角形画得足够多,任何形状都能够呈现。
将足够多的小三角形紧凑地拼接在一起就能够形成一个光滑的表面。
为了绘制一个三角形基元,要定义一个VertexPosiitonColor或VertexPositionTexture类型的数组来存放三角形的每个角。
前者是绘制具有位置和颜色的顶点,后者是绘制具有位置和纹理的顶点。
接着,创建一个VertexBuffer对象来储存这些点。
要将这些定义后的点绘制在屏幕上,一个关键对象是BasicEffect,它是XNA中的基本着色器。
简单地说,XNA3D中的一切都必须使用效果(一般是高级着色器语言,即HighLevelShaderLanguage,HLSL)来绘制。
HLSL使用C语言为基础,他允许开发人员方便地创建强大的效果,比如水面波纹、反射表面和其他华丽的效果。
开发人员可以自定义效果,也可以使用XNA默认的BasicEffect来绘制图像。
实际在屏幕上绘制三角形的动作是通过一个GraphicsDevice.DrawUserPrimitive