第3章辅助类XNA.docx

上传人:b****8 文档编号:10155828 上传时间:2023-02-08 格式:DOCX 页数:41 大小:286.36KB
下载 相关 举报
第3章辅助类XNA.docx_第1页
第1页 / 共41页
第3章辅助类XNA.docx_第2页
第2页 / 共41页
第3章辅助类XNA.docx_第3页
第3页 / 共41页
第3章辅助类XNA.docx_第4页
第4页 / 共41页
第3章辅助类XNA.docx_第5页
第5页 / 共41页
点击查看更多>>
下载资源
资源描述

第3章辅助类XNA.docx

《第3章辅助类XNA.docx》由会员分享,可在线阅读,更多相关《第3章辅助类XNA.docx(41页珍藏版)》请在冰豆网上搜索。

第3章辅助类XNA.docx

第3章辅助类XNA

第三章:

辅助类

3.0概览

在Helper命名空间,我通常放许多有用的小工具和类,这些类随着时间的过去变得相当有用。

对于一个单一的工程项目,它可能不是最重要或最有价值的代码,但是因为它能被一遍又一遍的使用,无论引擎的其他部分是否改变,助手类将是你的引擎和工程项目里最稳固的部分。

大多数助手类不处理太多的游戏程序,并且对其他工程项目甚至网站都有用。

你正好看过前面的章节,你不借助任何助手类就能写一个完整的游戏,甚至根本不用任何额外的文件或类。

但是当工程项目变得越来越大,你将会看到有许多重复的样式和相似的问题发生,这些问题过去已经被解决过了。

通常,在XNA中大多数重用的功能是图形组件。

在下一章节,特别是本书的第二部分,你学到更多关于游戏组件和图像引擎。

因为现在这个章节保持聚焦在非常基本的功能,如记录信息,访问游戏内容,做有效的单元测试,怎样产生随机数,以及许多其他较小的问题。

为了让这一章有一点让人更兴奋,你也创建下一个小游戏,只是为了娱乐。

不是所有的助手类将被使用,但是build一个游戏引擎不是一个简单的任务。

因为这个原因,本章和下一章从助手类和游戏组件开始,这样在本书第二部分中开发你的图像引擎就会容易一点儿。

请注意我没有用这种方式编写本章呈现的辅助类;它们是过去几年进化过程中的产物。

如论何时需要它们,只要添加功能或者你自己的助手类。

如果你一旦解决一个问题,并且认为它可能不会再次有用,只要移除它的位置。

但是如果你发现自己多次拷贝方案到一个新的工程项目,或者也许是另一个类,你应该认真考虑提取逻辑并且把它们放到一个特殊的类。

另外,您将学习到更多有关前几章中使用过的内容管道的知识,来支持Xbox360。

直接加载纹理或者shader,而不使用内容管道也可行,但是所有这些方法只能工作在windows平台。

如果你想要创建一个跨平台游戏,你应该确保一切编译和工作在windows平台和Xbox360平台都正常。

贯穿本书你将持续这么做。

本章节末尾,你将快速开发一个有趣的Breakout克隆游戏。

它比前面章节简单得多,因为你将首先编写所有的辅助类。

例如,借助SpriteHelper类渲染精灵是非常容易的。

在下一章节,你能找到更多的辅助类和游戏组件来改进Pong游戏和Breakout游戏。

3.1掌握内容管道(ContentPipeline)

就像你在第一章学到的,内容管道通常用来导入游戏资源,诸如纹理、shaders、阴影和声音文件。

不像任何其他非VisualStudio(或XNAStudio)支持的文件,你只要把添加它们到工程项目,内容文件会被处理,并且然后会被编译为二进制内容文件,这些文件能被你的游戏加载(见图3-1)。

在过去游戏编程不得不编写自己的导入器来加载游戏内容数据或者使用一种可利用的格式,诸如在DirectX中.x文件格式的模型文件。

但是常常,这种可利用的格式是不能胜任的,太慢,或者当试着给游戏添加新特性时就是不可更改。

这是为什么几乎每一款商业游戏都有自己的文件格式的原因,并且在其后台自定义逻辑编程。

只有游戏开发者知道内部格式的规划是有益处的,并且他们能所心所欲地经常扩展或改变它。

但是,在游戏中要用这种途径得到内容,通常有许多工作要做。

加载纹理通常不是那么复杂,因为有许多库存在,并且即使当你编写自己的文件格式,它基本上也只包含被作为24bit或32bit色彩值储存的像素。

如果你试图使用压缩或者硬件压缩纹理,诸如DXT格式,就变得有点儿难了,但是DirectX有一整套的有用方法来协助你处理。

另一方面,加载3D模型数据要复杂得多,特别是在XNA,在这里你不仅必须要有几何数据,而且渲染一个3D对象需要数个shader,然后当然,材质数据将告诉着色器shader它的颜色、纹理以及一些要用到的参数。

在DirectX中,大多数指南和范例只是使用.x文件格式,但是.x格式可能对于许多工程项目还不够胜任。

尤其是,你如果使用normalnapping(法线圆锥曲面叶)和需要包含切线的几何数据,.x文件将不是非常有用了。

你将不得不在你的应用程序中产生切线,并且这样做还可能引入运行问题。

例如,我的一个老游戏,RocketCommander,就有这个问题,并且它需要一个复杂的模型加载过程和切线再生过程。

其他游戏数据像加载声音文件(.wav),shader(.fx),或者自定义数据(例如.xml)可能是简单的,因为你的游戏或者你正在使用的框架提供了充分的帮助类来快速加载这一切,但是然后,在另一个平台运行同样内容的游戏,你可能遇到问题。

例如,你可能在Windows平台使用ACPCM声音文件,并且使用编译过的PixelShader1.1文件,或者只是加载一对.jpg文件作为纹理,但是在Xbox360上不支持ACPCM;声音要么是PCM,要么是Xbox专有的自定义XMA格式。

Shader代码必须是Xbox360接受的格式,加载纹理的工作也可能有差别。

如果更多的平台在未来被支持,这个问题甚至将变得更复杂。

为了简单化游戏内容的加载,XNA现在允许你直接把原始的内容文件放到XNAStudio工程项目,并且它们将被处理和编译为当前所选平台的正确输出格式。

例如,你的声音文件能基于你的XACT项目设定来进行处理——你将拥有不同的输出格式和压缩格式。

但是所有的原始声音文件在wavebank上都是同样的,仅仅需要在某一位置被更新。

这个主意很好,但是它需要所有的原始内容文件格式都被支持,这是不切合实际的因为可利用的文件格式如此之多,而你不知道哪一些将被使用。

例如,你的一个图像艺术师可能使用Photoshop并且储存.psd文件,而其他团队可能使用Gimp或Paint-Shop或仅仅是Windows绘图板。

大约有数以千计的其他图形工具和程序。

另外,你实际上不知道要提取哪一个数据;很多格式有“多层”结构,并且可能艺术家想要每一层都可利用,或者干脆合并一切图层。

受支持的文件格式

取代干脆放下一切,使用一个可利用的处理程序,和被支持的格式,或者如果你认为你需要,试着写你自己的内容处理程序(见第七章):

纹理格式:

.dds,.png,.jpg,.bmp,.tga-基本上每一个你都能用.NETFramework或DirectX加载。

输入格式通常为了最佳质量,应该是不被压缩的。

被高度压缩的.jpg文件是坏的,尤其是在游戏中你使用DXT再次压缩它们。

换句话说,对你的输入文件可以使用合适的输出压缩(如使用DXT压缩的.dds文件),并且再次为内容属性进行同样的设定,离开它们那个途径(在我的所有项目中,我用这个方式处理了大多数内容)。

声音和音乐格式:

.xap(XACTAudioProject)---在XACT中,你只能导入.wav文件,但是你能设置许多效果和参数,以及选择windows平台上的ACPCM压缩或者Xbox360平台上的XMA。

阅读更多的相关内容在第九章。

3D模型格式:

.fbx和.x模型文件----.x文件以来自DirectXSDK的许多范例和指南而知名。

DirectX提供了一些易于加载.x文件的类。

大多数.x文件在XNA下也应该工作得很好;主要的区别是DirectX的.x文件通常不使用shader,而XNA总是使用shader。

为了从3DStudioMax导出模型,使用PandaDirectXExporter。

你可以在http:

//www.andytather.co.uk/Panda/directxmax.aspx找到PandaExporter插件。

.fbx是一种较新的文件,最初由Alias开发,Alias是Maya的制作商,Maya也是一个3D建模工具。

Alias被Autodesk--3DStudioMax和许多CAT程序的制造商收购了。

.fbx意思是“Universal3DAssetExchange”,并且是Autodesk免费的跨平台内容交换格式。

在新版本的3DStudioMax9它默认被包含,并且Maya也支持它。

还有许多其他的3D内容建造程序支持导入和导出.fbx格式。

在XNA,它对于活动的模型、骨骼和蒙皮特别有用。

为此它支持更多选项,但它对shader特别糟糕,因为没有材质或任何shader设置能被导出。

.fbx格式的另一个问题是缺少格式的规格说明书,并且为了访问SDK你必须加入AutodeskDeveloperNetwork支付一年一度的会员资格金,真是吸血。

如果你看看其它交换格式像Collada,你能看到它们要开放得多并且可扩展,因为它们不只是被一个公司开发,许多新附件和特色时常会被添加。

在过去Collada不支持shader设定,但是当前版本对3D数据支持很好;你能输出tangent切线,shader设定,和其他一切你在游戏中需要的东西。

不幸的是,Collada不被XNA支持,并且我不能说服Microsoft包含它。

XNARacer最初所有的模型、赛道、地形数据都使用Collada文件,但是后来全更改了以支持内容管道contentpipeline。

其它格式——你能导入自定义文件格式,例如xml文件、二进制文件,甚至写你自定义的处理程序。

如果你有一个较大的项目,并且它值得你努力;或者你需要一个特殊的模型格式,并且它尚不被XNA支持,自定义格式就可能很有用。

例如,Quake3/Doom3使用md3/md5文件,你如果只是用来导入一些模型测试和随便玩玩,一个md5导入器会很棒。

万一你的游戏有较多的内容文件或者一些自定义数据,你要么决定写一个自定义处理程序,并且在工程项目中使用被导入的以及被编译的数据,要么就按照旧方式自己加载内容文件。

例如,在本书最后一章你将编写一个竞速游戏,它会使用一个从位图文件导入的,带有地形高度值的地形图。

处理位图文件,以及为游戏输出地形高度数据将是可能的,但是这有太多的工作——仅仅加载高度数据则要简单得多,并且只需要一次。

内容管道的优缺点

内容管道的另一个缺点是,被编译过的内容不能再一次被修改。

一旦你启动游戏,或者把你的游戏部署于客户端电脑或Xbox360,所有的内容文件只包含被编译过的数据。

比如说你已经写了一个粒子编辑器,它使用了支持所有粒子的shader。

当编辑器在运行的时候,如果你想要动态的改变纹理、shader,以及其他粒子设置,你将不得不重新加载纹理、shader等等。

但是因为你首先需要在XNAStudio中有内容被编译,你必须停止你的应用程序,添加所有的文件到你的XNAStudio工程项目,重编译并且等待,直到所有内容被重新构建(build),然后再次启动。

尤其是测试、调整特效或粒子的情形,可能非常恼人并且会严重拖慢你的工作进程。

只是动态的加载纹理、shader和粒子设置,以及像那样的程序不使用内容管道可能会更好。

最后,这里有一个诀窍,我会用在大多数有许多内容文件的工程项目中:

编译所有的内容文件,并且确定你不会非常频繁地修改它们(仅仅好几天改一次)。

现在,你可以使用一个虚拟工程项目来编译你所有的游戏内容,并且拷贝所有编译过的内容文件到你真正的工程项目中。

尤其当使用单元测试和章节最后谈论的敏捷方法学的时候,你将每天成百次的启动应用程序,并且每一次运行都要尽可能地快。

内容管道也有优点,那就是编译过的数据(.xnb文件)无法被除了XNA引擎之外的其它任何程序读取,并且其加载过程也通常快得多,因为所有数据都已经是游戏所需要的确切的格式。

例如,纹理总是被储存为DXT文件,并且使用了假设你在内容属性中指定的mip-maps。

这样,游戏就会在一次快速调用中加载texture纹理数据,然后把它发送给图像卡渲染,这又是一个快速处理过程。

这对于3D模型数据更加重要。

如果您看一看游戏RocketCommander并分析一下,你会发现加载3D模型和产生所有的附加数据以及切线,会花掉大部分的初始化时间(超过了90%);然而XNA游戏有10倍的模型数据大小,加载快得多。

能尽可能快地加载所有数据对Xbox360控制台来说,也是一件好事;控制台游戏通常拥有很短的加载时间。

处理内容目录

OK,您已经学习了很多使用内容管道的优势和不足;此时要聚焦在游戏编程和日常问题上了。

如果您看一看RocketCommander游戏和RacingGame游戏的内容文件夹(如图3-2),会发现RocketCommander有一大堆文件夹,然而XNARacer只使用了两个简单的文件夹。

看图的样子,你可能会以为RocketCommander的内容文件要多很多,但实际上XNARacer使用的3D模型文件数量几乎是RocketCommander的10倍,并且还包含更多的纹理、音乐和声音文件。

您或许要问为什么RocketCommander使用这么多的文件夹。

因为在这个游戏中没有使用内容管道,为了更有组织地维护这些内容,并且容易查找,游戏的每一部分都使用了文件夹。

例如,“Textures”文件夹包含了菜单和游戏界面使用的所有2D纹理,它的“Models”子文件夹包含了3D模型纹理,“Effects”子文件夹则包含了特效纹理,等等诸如此类。

在XNA中,您不能使用这样的目录结构,因为大多数内容文件,尤其是3D模型文件,可能需要递归地加载许多其它的内容文件(如图3-3)。

正如您所见,Apple模型是从Apple.x文件加载的,它又递归地加载Apple.dds、AppleNormal.dds和NormalMapping.fx。

内容处理器希望所有这些文件都放在同一个文件夹中,这迫使您使用一个文件夹来存放所有的3D模型文件、纹理以及它们使用的shader。

另外,大多数shader还会被其它3D数据使用,复制shader可能非常令人糊涂,把他们放到另一个文件夹中也是。

有时候您还需要为自定义3D数据加载纹理(比如,在游戏XNARacer中,guardrailholder模型和generatedguardrail对象使用相同的纹理)。

无论如何,记住每一块内容必须有唯一的名称是很重要的。

您不能拥有一个名为“Apple”的模型和一个名为“Apple”的纹理。

正如您在图3-3中看到的,在“InputFile”那一行只添加了一个Apple.x文件;所有的其它文件都是由模型处理器自动添加的。

另外,XNA足够聪明的会重命名所有的递归文件,因为它们通常会使用和模型文件名称相同的纹理。

递归文件的名称以“~0”结尾。

您也无法设置这些递归文件的内容属性,因为你不能把它们添加到工程项目中,所以要确保输入文件已经使用了正确的格式(在上述的例子中是DXT1和DXT5)。

导入并访问内容数据

现在您已经足够了解如何导入某些内容并在游戏中访问它们。

在前几章,您已经访问了一些内容文件,并快速地了解了内容管道。

现在,您将更进一步地注视实际的处理过程,以及如何使用内容文件。

在第七章,您会通过扩展X模型文件处理器(XModelFileProcessor)为您的图像引擎编写自己的内容处理器,并添加一些有用的特性。

回顾第一章您学习了如何添加纹理;只要选择一个纹理文件(.dds、.jpg、.bmp或.png)然后把它放到XNAStudio项目中。

现在你可以点击纹理,并且配置其ContentProcessor属性(如图3-4):

给texture设置正确的ContentProcessor模式很重要。

对于2D数据,像sprite、文本以及游戏使用的所有UI图像,通常最好使用32bppSprite格式(未压缩的,这意味着1024×1024分辨率及32bpp的纹理,需要4MB空间)。

在一个3D游戏中,3D纹理数据的使用要比2DUI纹理多得多,而且每一款游戏的纹理和级别会逐渐增大。

因为这个原因,保持纹理的小尺寸非常重要。

要使用硬件纹理压缩(hardwaretexturecompression),而不要降低纹理的分辨率以及使游戏看上去非常糟糕。

您可以为色彩纹理选择1:

6压缩率的DXT1格式,以及为包含Alpha信息(或compressednormalmaps)的纹理选择1:

4压缩率的DXT5格式。

这意味着在游戏消耗相同显存容量的情况下,DXT1压缩的纹理数量是未压缩纹理的6倍,而且还不会有太多的质量损失。

另一个技巧是在shader里合并纹理或者甚至生成纹理;例如,细节纹理可以改进地形的细节而几乎不消耗额外的显存。

对于模型文件,当前你只能使用XModelImporter或者FBXModelImporter(如图3-5)。

将来可能会有更多的格式可供使用。

如果你编写自定义的模型处理器,就像你将要在第七章做的那样,你能以你选择纹理处理器同样的方式选择模型处理器。

对于normalmapping你要选择来自于第七章的自定义XNARacerTangentModelProcessor。

在下面几章中只要许可默认值。

如果您按照前两章的方式来加载纹理,那么您或许已经知道了如何在XNA中加载内容。

纹理加载如下:

backgroundTexture=content.Load("CityGroundSmall");

3D模型也以相同的方式加载----只要改变Load方法的泛型参数类型:

appleModel=content.Load("apple");

显示模型稍微有点复杂;没有简单的绘制方法,你必须遍历所有的模型mesh并更新所有shader特效,然后再渲染每一部分。

更多细节参看第五章和第六章。

本书在第七章后面,您将看到一个新类专门加载和渲染模型,它使3D模型显示在3D世界中甚至简单到只需使用一行代码就实现:

appleModel.Render(Vector3.Zero);

现在您已经了解所有的关于内容管道的基础知识。

在即将到来的几章中,当您在图形引擎中使用自定义的TangentModelProcessor添加3D模型的时候,你将学习有关内容管道的更多内容,并且在第九章中你会深入学习XACT。

3.2LoggingErrorMessages记录错误信息

Debug游戏代码可能是非常复杂的,特别是如果您没有得到任何异常,但某些渲染循环却出错。

只设置几个断点并不够,尤其是如果游戏在运行一段时间之后遇到错误,Debug并不是正确的选择。

您想知道每一帧都运行了什么,但又不想逐步贯穿500帧去发现它。

对于这类问题,您可以仅仅抛出一些文本到控制台,不过这只能在VisualStudio中使用,而且当你下次启动项目时将会丢失所有的控制台内容。

在我做过的所有比较大型的项目中,一个最重要的类就是Log类,它只是给一个简单的文本文件写入消息、警告、错误或者Debug文本。

这个类本身很简短,也很简单,但如果你以正确的方式使用它,将会给您的debug调试和测试会话更令人愉快。

另外,还有更加高级的日志记录类和框架(loggingclassesandframeworks)可利用,诸如Log4Net,你可以在http:

//logging.apache.org/log4net/找到。

日志不仅仅只是给文本文件写入几行。

来自于应用程序的日志数据常常用来远程获取用户错误,借助一个WebService,你可以激活Windows错误事件,还可以做很多其他事情。

这些不是本书能涵盖的,因为这是一个非常复杂的话题。

对于本书中的简单游戏,使用Log类应该足够了。

先看一看Log类(在Breakout游戏中能找到一个更复杂的版本):

publicclassLog

{

#regionVariables

privatestaticStreamWriterwriter=null;

privateconststringLogFilename="Log.txt";

#endregion

它使用一个Log.txt文件来存储所有消息,并使用一个静态的StreamWriter对象,以便可以方便地在静态方法中访问。

#regionStaticconstructortocreatelogfile

staticLog()

{

//Openfile

FileStreamfile=newFileStream(LogFilename,FileMode.OpenOrCreate,FileAccess.Write,FileShare.ReadWrite);

writer=newStreamWriter(file);

//Gotoendoffile

writer.BaseStream.Seek(0,SeekOrigin.End);

//Enableautoflush(alwaysbeuptodatewhenreading!

writer.AutoFlush=true;

//Addsomeinfoaboutthissession

writer.WriteLine("///Sessionstartedat:

"+StringHelper.WriteIsoDateAndTime(DateTime.Now));

}//Log()

#endregion

在游戏运行的时候,枚举值FileShare.ReadWrite确保你总是可以从外部读写文件。

除此之外,要把writer设置到文件的末尾,AutoFlush属性能够确保写入新数据会被立即存储到日志文件中,最后再添加一点儿文本指示这次会话已经开始。

对于时间戳你将使用StringHelper类的一个辅助方法,你立刻就会学到这个类。

最后,这是该类的最重要的一个方法、也是您将一直调用的唯一方法:

#regionWritelogentry

staticpublicvoidWrite(stringmessage)

{

DateTimect=DateTime.Now;

strings="["+ct.Hour.ToString("00")+":

"+ct.Minute.ToString("00")+":

"+ct.Second.ToString("00")+"]"+message;

writer.WriteLine(s);

#ifDEBUG

//Indebugmodewritethatmessagetotheconsoleaswell!

System.Console.WriteLine(s);

#endif

}//Write(message)

#endregion

首先,在消息的前面加上一个简单的时间戳。

然后消息被写入Log.txt文件中,最后如果项目在debug模式,也把消息输出到控制台。

现在,只是通过添加下列代码行,当你每一次完成来自第二章的Breakout游戏的一个关卡,你就给Log.txt文件添加一个新行:

Log.Write("Level"+level+"completed.");

3.3UnitTestinginXNAXNA中的单元测试

在本书即将带来的工程项目进一步深入助手类的细节之前,本节简单讨论一下单元测试。

在上一章您已经学习了静态单元测试(staticunittest)。

静态单元测试非常适用于快速检测可视的结果、测试物理特性和控制器,以及快速构建游戏。

但是助手类和组件不需要用户输入,而需要你考虑它们的接口。

这是没有意义的,因为单元测试主要是为了改善你的应用程序的可维护性,并确保一切运行尽可能无错。

例如,可以调用下面的代码行来测试Log类是否工作:

FileHelper.DeleteFile(Log.LogFilename);

Log.Write("Ne

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 文学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1