XNA高级编程第14章调整和改编赛车游戏.docx
- 文档编号:9008926
- 上传时间:2023-02-02
- 格式:DOCX
- 页数:45
- 大小:500.33KB
XNA高级编程第14章调整和改编赛车游戏.docx
《XNA高级编程第14章调整和改编赛车游戏.docx》由会员分享,可在线阅读,更多相关《XNA高级编程第14章调整和改编赛车游戏.docx(45页珍藏版)》请在冰豆网上搜索。
XNA高级编程第14章调整和改编赛车游戏
第14章:
调整和改编赛车游戏
14.0概览
终于到本书的最后了,只剩最后一章了。
你将了解赛车游戏所有的细节。
本章你将学习所有的游戏屏幕、游戏逻辑、记分系统以及一切你您需要的东西。
场景和赛道的大多数渲染技术已经在第12章讨论过了,但本章会增加一点阴影。
因为上一章已讨论过物理学和汽车控制,现在应该可以插入此代码,并开始测试游戏本身。
本章的主要目的是让你熟悉赛车游戏和底层代码。
在本章最后你应该知道一切以创建自己的改编赛车游戏。
游戏改编可以很简单,只需改编图形和三维模型,也可以改变整个游戏的逻辑,使它不再是一个赛车游戏。
例如RocketCommander也是可以改编的,但游戏逻辑和渲染代码是固定的。
你可以或多或少地改变模型,并新增一点游戏逻辑。
尽管大多数RocketCommander改编版本看起来完全不同(见图14-1),但游戏逻辑是相似的。
RocketCommander的游戏规则是保持前进并避免与其他物体(如小行星)碰撞,同时也收集物品,如生命值,水果,比萨饼等等。
有些改编版本有更快的游戏速度,有些则慢得多,侧重于解决智力题,比如收集正确的水果(比萨指挥官和水果指挥官)。
好在改编RocketCommander并不难,因为改变原始版本相对容易。
如果有一些不同的三维模型,则改变它们是容易的。
改变游戏逻辑也不难,因为你只需关心Mission类和Player类,其余的类只是用于菜单和图形。
14.1游戏构思
在你开始游戏屏幕和游戏逻辑之前,快速浏览一下这个游戏的原始构思,这样你能理解为什么某些东西实现了而有些却没有。
在游戏的主要功能实现后,一些部分才在后面添加。
这方面的一个例子是阴影映射,这始终是游戏引擎的复杂部分,因为你要花费大量的时间进行调整,直到它看起来正确。
还有些东西必须跳过或故意排除在外,因为它们实在太复杂,在短时间内很难实现。
最好的例子是道路上的障碍物(例如柱子),如果玩家不小心驾驶就会撞在障碍物上(见图14-2)。
图14-2
这一想法的主要问题是如何有效率地设置障碍栏。
这个游戏没有关卡编辑器或赛道编辑器。
所有赛道都是由采样点生成的,很难在3DSMax中设置障碍物。
测试这些物体并更新碰撞检测系统会产生更多的问题。
在早期测试版中我使用了道路立柱,并将赛道放在立柱上(见图14-3)以测试这个使用障碍物的想法,但它看上去并不十分令人信服,而且只在赛道上开车已经是够难的了。
一个更好的解决办法是添加如道路标志,交通灯,垃圾箱等可以破坏的物件,但上一章中你已经看到物理引擎不能够处理许多不同的物体,它只检查汽车与护栏的碰撞,并进行相应的的物理计算,仅此而已。
图14-3
经过几次单元测试后,我决定放弃这一想法,将精力花在游戏的其他部分。
我还添加了隧道和一种简单的方法来设置道路附近的棕榈树,标志,路灯等,并添加一个很好的技术,它能在赛道附近自动添加场景物体。
下列的文字直接来自于最初的游戏构思,这个构思确定于2006年8月,是在2006底开发这个赛车游戏之前。
请注意,这个构思写于第一个XNA的beta版本发布以前,那时我还不知道XNA的许多细节。
游戏理念
这个赛车游戏是一个简单的3D赛车游戏。
玩家能看到前面的赛车并能使用Xbox手柄或鼠标或键盘直接控制赛车。
游戏的主要目标是尽可能快的到达终点并绕过所有障碍物。
碰撞不会伤害玩家,但赛车会减速导致完成时间大大增加。
玩家不会游戏结束,他只需花很长时间完成赛道。
有3个不同难度的赛道,每个赛道都有一个排行榜。
(注:
这本书中的赛车游戏版本没有实现多个轨道,也没有最高分数或奖杯,请查看XNA的赛车游戏初学者工具包获取更多细节和附加功能)
这个游戏的图形(见图14-4)借鉴了如赛道狂飙,GT赛车,极品飞车等类似的游戏。
显然,这些游戏花了很长时间开发并有很漂亮的图像,这在短时间内是不可能完成的,因此这个游戏着重于创建一个简单易学的、只有基本功能的赛车游戏。
主要图形的主题是位于城市(显示街道,一些简单的建筑物,以及一些树木)。
地面使用一个简单的二维图形,赛道是从二维图像自动生成的(使用特定间隔的点),使用高程图创建山较难,而创建赛道更难。
(请注意,这一想法后来被放弃,现在游戏中导入的是真正的三维数据。
)
图14-4
在游戏中没有其他汽车(如赛道狂飙),这使得代码简单,只需检查的与关卡中物体的碰撞。
游戏的基本版只有一个主菜单,三个简单的单人赛道和每个赛道的排行榜(显示完成的时间)。
更多的图形设置(雪,沙漠,草原等等),更多的赛道,改编版本或多人游戏代码会在以后制作(例如在XNA社区)。
由于开发时间短,所以只保留了最基本的构思:
控制汽车通过赛道。
一个简单的物理系统被实现,每帧检测与场景中的静止物体的碰撞。
其他构思在基本游戏中被放弃。
这不仅简化了游戏,但简化了对技术和代码的理解。
这个游戏是从后面看,在前面显示汽车,赛道在中间,一些物品和天空在远处。
天空是由一个天空立方体贴图实现的。
接下来是开发时间,所有的功能,哪个项目文件被使用,shader如何工作,音效如何使用。
当讨论到使用的技术时会变得更有趣一点。
微软公司的赛车游戏初学者工具包使用C#和XNAGameStudioExpress开发。
使用XNAAPI和托管代码有助于编写和阅读代码、执行如载入纹理、使用shader、只用一行代码显示模型等强大的功能。
图形都是.DDS格式的文件。
所有模型(汽车,树木,Effects等等)都在3DStudioMax8中建模并导出为.x文件。
模型不使用动画,这样更容易理解代码(注:
我后来发现XNA不支持动画模型)。
这个游戏不使用任何现有的引擎或其他复杂的框架,这样做的话那些非游戏程序员会不熟悉它,只会使想要看源代码的人困扰。
游戏由以下几个基本部分组成:
●Helper类处理纹理加载,模型加载,游戏屏幕处理,XNA管理,处理控制等。
●Shader类处理渲染模型(主要是汽车)和带有shader效果的场景。
(注:
我很快发现我低估了场景物体的数量)
●Pre和post-screenshader类处理渲染天空和增加光晕效果、运动模糊和色彩校正。
●主菜单类处理开始游戏,检查排行榜和退出游戏。
●排行榜屏幕显示排行榜,并提交给一个在线服务器。
排行榜还保存在本机上,也可通过这个类查看。
(注:
由于XNA不支持网络,而我想为PC和Xbox360使用相同的代码,所以我放弃了将排行榜发送到在线服务器。
)
●在Mission类中玩游戏。
这几乎就是所有的游戏构思了,当得到第一个XNAbeta版本后我立即开始了编程。
当然开发过程中还有许多问题,但借助于单元测试和游戏构思的不断更新,开发并不难。
最难的部分是场景和赛道(见第12章)和第13章的物理系统。
阴影映射对大多数没有经验的编程这也不是一件容易的事,但几个月后我在另一个引擎中实现了阴影映射,回过头来看未完成的赛车游戏,我确信没有阴影映射它不会很好看。
附加功能
如果你看以下整个赛车游戏的类视图(见图14-5),它看起来相当复杂。
最大的帮助是RocketCommander的项目,在它的单元测试和代码帮助下,我能很快地测试自己的想法,模型和渲染技术。
后来当XNA引擎完成后,我就可以复制代码,并在新引擎中继续测试。
例如,我很早就发现从二维图像产生赛道并不聪明,造成的问题比解决的更多。
实现基本赛道不难,但当需要处理三维顶点是,我很高兴RocketCommander游戏中有一些托管代码让我可以加载.x文件并使用它们的顶点生成赛道。
但是XNA不支持访问或加载导入的3D模型的顶点,你可以编写自己的内容导入器,但要做太多的工作。
后来我改变了赛道的导入方式并添加了许多功能(隧道,道路宽度,道路物体、棕榈树等)。
在3DStudioMax中赛道被直接导出为Collada格式,然后以二进制格式(更快地装载)导入到游戏引擎中。
图14-5
以下的功能是从以前的项目(主要来自于RocketCommander)中导入的:
●Helper类,如Log类处理调试,StringHelper类或其他如RandomHelper,Vector3Helper,或ColorHelper类等重要的类。
●该图形引擎是从头写起的,但通过扩展XNA类的功能,使用新的类处理纹理,材质,模型,字体的基本思路是来自与RocketCommander。
XNA中的大多数类简单得多,一开始做得并不多,比如说Model类,只加载了一个模型并通过几行代码显示它。
但后来的模型渲染性能提高了很多(将在本章后面讨论),Model类的内在逻辑完全改变了。
模型已不再直接被渲染,取而代之,每个三维模型的shader和网格被收集并在每帧末所用使用相同shader、材质、网格数据的网格被集中渲染。
通过这种方式在Xbox360游戏机上能使性能提高200%-300%。
借助于特殊模型类的抽象,使用模型的代码也无需改变,它会变得更快!
●游戏屏幕逻辑100%地来自RocketCommander。
我很喜欢这个主意,而且实现起来很简单。
●shader和其他图形类如ParallaxMapping,PostScreenGlow,PreScreenSkyCubeMapping和LensFlare类也是从RocketCommander导入的,但经过一段时间后只有LensFlare和SkyShader被留了下来,因为其他shader改变得太多了。
无需为每一个新的材质编写一个新的shader,作为替代,编写一个更一般的ShaderEffect类,现在所有shader都是从这个类继承的。
这简化了一些shader,并使像NormalMapping或ParallaxMapping变得过时了,因为它们现在直接工作自ShaderEffect类,而ShaderEffect类可以处理所有shader参数。
PostScreenGlowshader继承自PostScreenMenu,这个新的shader仅用于菜单。
●对于单元测试也采取了类似的做法,但NUnit和TestDriven.NET都被排除在外以简化Xbox360的开发,因为TestDriven.NET在VisualStudio的Express版本不能工作。
当然这个游戏也有一些新的功能,不光是阴影映射,还使用了以下几个类:
●RenderToTexture:
这个类服务于post-screenshader,但需要进行改进以支持阴影映射,这要求有不同的表面形式以获得更好的精度。
这意味着,对普通的渲染目标你只能使用与后备缓冲相同的格式。
这最有可能是R8G8B8X8,这意味着每个颜色通道使用8位而有8位是保留的,因为alpha通道对后备缓冲是无意义的,但32位优于24位,这使得在32位平台上更难读取像素数据。
总之,阴影映射需要每像素更多的精度而不是很需要颜色。
阴影映射的每个像素的深度值必须介于0和1之间,并越精确越好。
良好的格式是R32F,如果R32F无法使用则R16F也可。
但是,这些格式都必须进行不同的初始化,而RenderToTexture会为你处理。
它也会根据用户的选择决定阴影映射大小是2048×2048,1024×1024或在速度较慢的电脑上只使用512×512。
如果R32F和R16F都不能使用,它也支持回退到32位颜色格式。
●ShadowMapBlur:
这个类可以帮助您模糊阴影映射。
所使用的技术类似于PostScreenGlowshader的发光技术。
它通过两个步骤获取输入数据并模糊。
模糊不仅使阴影更柔和更真实,也修正某些阴影映射错误,这些错误被模糊掉了。
这种技巧在XNAShooter中不使用,因为如果在背景上有很多小物体和很多阴影重叠区和非阴影区,看起来不太好。
在赛车游戏看上去好得多,特别是汽车上的阴影有一些错误变得更加光滑。
其他场景物体大多相距遥远,它们的阴影不会和其他物体重叠,这可以让你使用更好的模糊值。
●ShadowMapShader:
这是阴影的主要类。
它初始化所有渲染目标,使用的纹理,阴影映射shadr,它处理ShadowMapBlur类。
关于阴影映射技术的更多内容将在本章后面讨论后。
其他的游戏功能,比如环形赛道和场景渲染引擎已在第12章讨论过。
它们包含在TrackLine类和Track类早期单元测试中,使游戏更有趣。
我真的不记得为什么游戏构思没有提及游戏屏幕。
好像只说过:
主菜单允许开始任务,查看排行榜,并退出游戏。
代码后部是一个非常复杂的gamescreen命名空间(见图14-5),它比原计划支持更多的游戏屏幕。
请注意,本章中某些游戏屏幕没有在代码中实现(例如,赛道或汽车选择屏幕),他们只用在XNA框架的赛车游戏初学者工具包中。
最主要的原因是这里你只有一个赛道和一辆汽车,如果没有什么可以选择,那么实现它们是毫无意义的。
你可以在,这个游戏的官方网站找到一个更复杂的版本,并获取更详细的信息。
14.2游戏屏幕
赛车游戏中有很多不同的游戏屏幕,这些都是由RacingGame类中的gameScreens堆栈管理的。
本节介绍游戏中使用的大部分屏幕和对应的功能。
大多数游戏屏幕相当简单,但其他的有点复杂并实现了一个单元测试,通过单元测试能更好地了解这个类。
例如,Credits屏幕是相当简单的,它只显示一个背景纹理,但主菜单很复杂并具有所有能进入另一个屏幕的按钮。
Options屏幕介绍了许多新的控制选项,这些都要进行测试,这个类里有一个单元测试能帮你完成这个过程。
所有的游戏屏幕类都继承于IgameScreen接口(见图14-6),您也许还记得在第8章的RocketCommander中也使用过。
几乎没有改变,但更简单了。
您现在只有一个叫做Render的方法,它没有参数并返回一个布尔值。
该代码和你在XNAShooter中使用的几乎一样。
如果Render方法返回true意味着您可以返回到以前的游戏屏幕,一旦推出最后一个游戏屏幕则游戏推出。
通常游戏屏幕返回false,因为玩家再次进入游戏屏幕后不会立即退出。
图14-6
由于这个简单的接口,所以所有的游戏屏幕的类视图几乎是一样的,它们都只有一个Render方法。
有些类还会有一些私有的辅助方法,但它们大多是非常简单的。
一些更复杂的游戏屏幕还有单元测试用以测试是否所有内容和功能都能正确实行。
处理整个游戏的GameScreen类是复杂的。
不像XNAShooeter或RocketCommander,所有游戏代码的的处理和渲染是在Landscape和model类中的。
游戏逻辑在Player类中被处理,这个有点复杂,但你已经在上一章学习了所有的CarPhysics类的基本物理知识和ChaseCamera类。
看看图14-7了解一下游戏屏幕的基本概况。
最复杂的类显然是Mission类,它处理游戏过程和游戏逻辑。
它不包含所有的游戏代码,有些是在Player类和RacingGame类中被处理的。
图14-7
启动屏幕
启动屏幕(见图14-8)是较容易的一个类,它只是等着玩家按下手柄上的Start。
如果没有手柄,Space或Esc键或鼠标左键也可让玩家继续。
这个类唯一令人感兴趣的地方就是让“PressStarttocontinue”文字闪烁的代码。
图14-8
///
///Rendersplashscreen
///
publicboolRender()
{
//Thisstartsbothmenuandingamepostscreenshader!
BaseGame.UI.PostScreenMenuShader.Start();
//Renderbackgroundandblackbar
BaseGame.UI.RenderMenuBackground();
BaseGame.UI.RenderBlackBar(352,61);
//ShowPressStarttocontinue.
if((int)(BaseGame.TotalTime/0.375f)%3!
=0)
BaseGame.UI.Logos.RenderOnScreen(
BaseGame.CalcRectangleCenteredWithGivenHeight(
512,352+61/2,26,UIRenderer.PressStartGfxRect),
UIRenderer.PressStartGfxRect);
//Showlogos
BaseGame.UI.RenderLogos();
//Clickingorpressingstartwillgotothemenu
returnInput.MouseLeftButtonJustPressed||
Input.KeyboardSpaceJustPressed||
Input.KeyboardEscapeJustPressed||
Input.GamePadStartPressed;
}//Render()
RenderMenuBackground方法有点复杂。
它用来显示菜单背景,即显示出汽车通过一条赛道。
汽车是由计算机控制的而相机只是跟随它。
代码不是很复杂:
//[FromRenderMenuTrackBackground(),whichiscalledby
//RenderMenuBackground(),bothlocatedintheUIRendererclass]
//[SomecodetocalculatecarPos,carMatrix,etc.]
//Putcamerabehindcar
RacingGame.Player.SetCameraPosition(
carPos+carMatrix.Forward*9-carMatrix.Up*2.3f);
//Forrenderingrotatecartostaycorrectlyontheroad
carMatrix=
Matrix.CreateRotationX(MathHelper.Pi/2.0f)*
Matrix.CreateRotationZ(MathHelper.Pi)*
carMatrix;
RacingGame.Landscape.Render();
RacingGame.CarModel.RenderCar(
randomCarNumber,randomCarColor,carMatrix);
借助于Landscape类的Render方法和Model类的RenderCar方法您不必担心渲染场景、赛道或其他东西。
相机矩阵可以确保你在正确的位置上观看,而赛车通过汽车矩阵在赛道上行驶。
所有其他游戏屏幕菜单也使用RenderMenuBackground方法显示屏幕背景,但在一些游戏屏幕上不太明显,因为你把一个较暗的纹理放在前面(例如,在Credits屏幕上很难看到背景)。
这只是一个背景效果,当你正式开始玩游戏时你可以看到更多的屏幕。
主菜单
主菜单(见图14-9)比其他菜单屏幕复杂点,但即使这样这个类也只有约250行代码。
除了启动屏幕,其他屏幕都是从这里开始的。
最重要的选项是开始游戏和观看highscores(头两个按钮)。
图14-9
这个类最酷的功能是菜单按钮的动画。
每个按钮获得一个介于0和1之间的浮点值,其中0代表按钮尺寸的最小可能值而1代表最大。
当鼠标悬浮或用手柄、键盘选择按钮时,它将慢慢变大直到达到1.0。
当你离开按钮,它又缓慢变小。
第一个按钮初始设为1,其他按钮设为最小尺寸(0)。
///
///Currentbuttonsizesforscalingup/downsmootheffect.
///
float[]currentButtonSizes=
newfloat[NumberOfButtons]{1,0,0,0,0,0};
然后在Rnnder方法中处理按钮选择和缩放。
为了确保你会在同时悬浮在一个以上的按钮,这里为鼠标使用了一个辅助变量。
如果不使用鼠标选取菜单按钮,这个变量将不会被使用。
//Littlehelpertokeeptrackifmouseisactuallyoverabutton.
//Requiredbecausebuttonsareselectedevenwhennothoveringover
//themforGamePadsupport,butwestillwantthemouseonlyto
//beappliedwhenweareactuallyoverthebutton.
intmouseIsOverButton=-1;
//[alittlelaterinthecode...]
for(intnum=0;num { //Isthisbuttoncurrentlyselected? boolselected=num==selectedButton; //Increasesizeifselected,decreaseotherwise currentButtonSizes[num]+= (selected? 1: -1)*BaseGame.MoveFactorPerSecond*2; if(currentButtonSizes[num]<0) currentButtonSizes[num]=0; if(currentButtonSizes[num]>1) currentButtonSizes[num]=1; //Usethissizetobuildrect RectanglethisRect= InterpolateRect(activeRect,inactiveRect,currentButtonSizes[num]); RectanglerenderRect=newRectangle( xPos,yPos-(thisRect.Height-inactiveRect.Height)/2, thisRect.Width,thisRect.Height); BaseGame.UI.Buttons.RenderOnScreen(renderRect,ButtonRects[num], //Makebuttongrayifnotselected selected? Color.White: newColor(192,192,192,192)); //Addbordereffectifselected if(selected) BaseGame.UI.Buttons.RenderOnScreen(renderRect, UIRenderer.MenuButtonSelectionGfxRect); //Alsocheckiftheuserhoverswiththemouseoverth
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- XNA 高级 编程 14 调整 改编 赛车 游戏
![提示](https://static.bdocx.com/images/bang_tan.gif)