`
tansitongba
  • 浏览: 484088 次
文章分类
社区版块
存档分类
最新评论

Silverlight.XNA(C#)跨平台3D游戏研发手记:(六)向Windows Phone移植之框架构建

 
阅读更多

海量的美术、庞大而繁杂的人员与资源配备使得网游和端游开发难度系数高居不下;移动开发时代的来临为游戏设计师们提供了第三条绿色通道,这是一次愈加趋近梦想的迅捷契机。

作为一个专情的人,深爱着C#,毋庸置疑的原因;于是,我也爱上了Windows Phone,爱上了C#在Silverlight.XNA中放荡的游走;因为它的存,使得代码移植在页游、端游与手游之间显得格外畅快淋漓。

今天,打开的不仅是一扇门,更是通往美丽新世界的崭新道路;握紧了,战士,你手中那无比锋利的C#,鞭笞吧!XAML,神秘的游戏世界正等待您来探索。

轻轻的,我步入了这个陌生而又激动的新圣域,困惑悄然而生:该如何开启Windows Phone游戏开发这个潘多拉之盒?

Sprite,精灵,永恒不变的游戏灵魂铸就者,生命万象之密匙;从精灵的起源去探究创世之初尤能缅怀我的虔诚。

翻开上帝之书MSDN,古老的文字向人类印示着Windows Phone游戏精灵的两种主要创生方式:Silverlight的UElement和XNA中的Texture2D。性能方面,后者绝对专业;不过相对于效率而言,前者则更为出色。彷徨中的我恍然大悟,其实一切真想早已被远古神器Visual Studio 2010暴露得一览无余,抹去岁月的尘土,赫然印着:基于Silverlight与XNA的无缝集成打造最完美之解决方案:

通过Silverlight(Blend)制作游戏界面,XNA实现游戏对象的绘制,双管齐下。开发者不仅能够延续传统.NET基于事件驱动的低耦合编程模式,同时也能享受到XNA高性能的图形绘制与渲染;正如MSDN所述,Silverlight与XNA的完美结合带来的是开发“效率”与“性能”质的飞跃

由此我们也不难看出,目前的Silverlight不论是作为浏览器插件,还是Windows Phone的主要开发工具,其与XNA融合构建.NET开发者最熟悉的事件驱动架构已成为主流趋势;本节作为系列Demo向Windows Phone平台移植的第一步,我将向大家详细讲解如何搭建游戏的主体框架。

一)配置开发环境

安装Windows Phone SDK 7.1

二)新建游戏项目

打开Visual Studio,点击文件->新建->项目->选择模板 Silverlight for Windows Phone中的“Windows Phone Silverlight和XNA应用程序”,这里我取名叫SLXnaGame:

三)分析解决方案

在解决方案管理器SLXnaGame项目中第一眼看到.xaml顿时泪流满面,无比熟悉的App.xaml以及主页面MainPage.xaml和游戏场景页面GamePage.xaml让所有Silverlight游戏开发者倍感亲切:

四)核心框架搭建

新项目默认为我们打开了MainPage.xaml的前端部分,除了左边垂直摆放着一个偌大的Windows Phone模型外,右边那一长串的xaml再熟悉不过了。由于SLG游戏以水平方向呈现效果更好,因此我们不妨对这个所见即所得的展示窗口进行一些调整,并以一张很炫的图片作为游戏的封面:

如上图,xaml代码中我们可以通过SupportedOrientations="Landscape" Orientation="LandscapeLeft" 设置Windows Phone模拟器横向显示;当然,如果不需要观看预览(比如后面讲到的GamePage),我们也可以在后台cs文件中编写this.SupportedOrientations = SupportedPageOrientation.Landscape; 实现同样的效果。另外,游戏中所有Silverlight控件所用到的图片资源均统一存放在SLXnaGame项目的(新建)Resource文件夹中,这样我们便可通过如下xaml代码实现图片装载:Source="/SLXnaGame;component/Resource/UI/FrontPage.jpg" 除此之外,为了构建更为灵活的游戏框架,同样可以仿造之前Silverlight游戏教程的做法,编写一个名为Global.cs的全局辅助类存放于SLXnaGameLib(控件类库)项目中:

Global
namespaceSlXnaDemoLib{

///<summary>
///全局(数据和方法)
///</summary>
publicstaticclassGlobal{

///<summary>
///主项目名
///</summary>
staticstringProjectName=Application.Current.GetType().Assembly.FullName.Split(',')[0];

///<summary>
///项目Resource资源路径
///</summary>
publicstaticstringProjectPath(stringuri){
returnstring.Format("/{0};component/Resource/{1}",ProjectName,uri);
}

///<summary>
///获取项目Resource中的位图
///</summary>
///<paramname="uri">路径</param>
///<returns></returns>
publicstaticBitmapImageGetImage(stringuri){
returnnewBitmapImage(newUri(ProjectPath(uri),UriKind.Relative));
}
}
}

注意,这里需要添加对System.Windows.dll动态链接库的引用:右键点击SLXnaGameLib项目中的引用->添加引用->选择System.Windows

到此为止,游戏初始界面制作完毕,按F5调试运行;正常情况下我们将看到前面精心设计好的游戏初始画面,此时细心的朋友肯定会注意到一个特殊的警告提示:

由于Silverlight与XNA的兼容模式是从7.1开始才有的新模板,其本质由7.0衍生而来。编译后会发现警告提示“无法引用项目‘SLXnaGameLib’”,但实际上SLXnaGame项目还是能够使用的,如果出现由于兼容问题导致可能出现的无法找到命名空间或类名,可删除对这个SLXnaGameLib的引用后重新再引用一次即可永久解决问题。此处稍作说明提醒大家无需紧张,框架之间的协调问题在后续版本中将进一步完善。

回到正题,接下来我们点击“点击开始”这个闪烁的按钮便会跳转到项目默认自带的第二个页面:GamePage.xaml。对于新手来说,MainPage.xaml是如何通过点击Button实现跳转的呢?机关就在MainPage.xaml左边的小箭头上:

熟悉Silverlight的朋友都清楚,Silverlight中的用户控件(页面)都是以两个文件partial的形式存在:前台(.xaml)和后台(.cs)。常规的做法是通过在前台注册Button的Click="Button_Click"事件,并于后台编写相应代码实现页面之间的点击跳转功能。

//简单的按钮单击事件处理程序可使我们转至第二页
privatevoidButton_Click(objectsender,RoutedEventArgse){
NavigationService.Navigate(newUri("/GamePage.xaml",UriKind.Relative));
}

接下来我们将目标转向第二个页面,首先打开GamePage.xaml,赫然写着“不需要XAML内容……”,其实我想说:…哥还是留个Canvas吧,哈哈。

继续打开GamePage.cs,终于来到了我们游戏框架的核心部分。默认的代码有些凌乱,稍做调整后我们不妨先对比一下它与标准的XNA游戏中的Game1.cs有什么区别:

做过XNA开发的朋友是否有种豁然开朗的感觉 (新手朋友们可以参考一下XNA的游戏开发机制)。把Silverlight.XNA(以下简称SL.XNA)中的OnNavigatedTo()和OnNavigatedFrom()分别看做是纯XNA中的LoadContent()和UnloadContent(),两者相似度几乎一模一样,只是SL.XNA模式通过一个timer实现了游戏的主循环;注意了,这个timer可是XNA线程框架中的GameTimer,因此我们无需担忧其性能方面的问题:

至于绘图方面,SL.XNA和纯XNA在代码方面几乎是无缝移植。比如我们希望绘制字体,完全可以一字不差的照搬现有的XNA教程中的字体示例;而音乐和音效的播放则同样,将mp3或wav等音频资源加入到SLXnaGameLibContent资源项目中,然后编写一样的代码实现一模一样的功能(注意,mp3和wav的资源存放形式不同,播放方式亦不同):

音乐播放
SoundEffectsound;
Songsong;

//构造函数
publicMainPage(){
InitializeComponent();
this.Loaded+=newRoutedEventHandler(MainPage_Loaded);
}

voidMainPage_Loaded(objectsender,EventArgse){
content=(Application.CurrentasApp).Content;
song=content.Load<Song>("Media/MySong");
MediaPlayer.Play(song);
}

//简单的按钮单击事件处理程序可使我们转至第二页
privatevoidButton_Click(objectsender,RoutedEventArgse){
sound=content.Load<SoundEffect>("Audio/MyAudio");
sound.Play();
NavigationService.Navigate(newUri("/GamePage.xaml",UriKind.Relative));
}

到此有朋友要问了:仅仅是调用了XNA中的字体和音乐,与纯XNA又有何区别?Silverlight控件呈现问题甚至还不需要字体呢,干嘛非得多次一举给XNA加个Silverlight壳?

别急,接下来便是Silverlight与XNA交互实现的关键:UIElementRenderer

就像本文开头所述那样,完美的交互必须是Silverlight的UElement和XNA的Texture2D之间的非跨线程交互操作,大家不妨先看看最终的实现代码:

UIElementRendererelementRenderer;
publicGamePage(){
InitializeComponent();
this.SupportedOrientations=SupportedPageOrientation.Landscape;
this.LayoutUpdated+=GamePage_LayoutUpdated;
....
}

///<summary>
///允许页面绘制自身。
///</summary>
privatevoidDraw(objectsender,GameTimerEventArgse){
graphicsDevice.Clear(Color.CornflowerBlue);
elementRenderer.Render();//通过elementRenderer呈现Silverlight中的UElement
spriteBatch.Begin();
spriteBatch.Draw(elementRenderer.Texture,Vector2.Zero,Color.White);//通过XNA的形式将elementRenderer整体绘制出来
spriteBatch.End();
}

///<summary>
///创建一个可以被XNA绘制的Silverlight-UI展示器UIElementRenderer
///</summary>
voidGamePage_LayoutUpdated(objectsender,EventArgse){
if(ActualWidth>0&&ActualHeight>0&&elementRenderer==null){
elementRenderer=newUIElementRenderer(this,(int)ActualWidth,(int)ActualHeight);
}
}

其实,UIElementRenderer的原理便是将Silverlight中的UElement对象以XNA的绘制形式在Draw()方法中画出来,真想大白:不论是Silverlight的东西还是XNA的东西,所有能看得到的对象最终都将以XNA的形式绘制出来,这也是成就SL.XNA模式得以完美兼具“效率”与“性能”的根本原因。

接下来我们也来俗一把,分别用Silverlight的TextBlock和XNA的Font编写Hello Game:

protectedoverridevoidOnNavigatedTo(NavigationEventArgse){
//设置图形设备的共享模式以启用XNA呈现
graphicsDevice.SetSharingMode(true);
//创建可以用来绘制纹理的新SpriteBatch。
spriteBatch=newSpriteBatch(graphicsDevice);
//TODO:使用this.content在此处加载游戏内容
textBlock=newTextBlock(){Text="HelloGame,I,mSilverlightTextBlock"};
LayoutRoot.Children.Add(textBlock);//将textBlock添加进Canvas画布中
Canvas.SetLeft(textBlock,10);Canvas.SetTop(textBlock,20);//设置textBlock在画布中的绝对位置
font=content.Load<SpriteFont>("Font/MyFont");

timer.Start();
base.OnNavigatedTo(e);
}

///<summary>
///允许页面绘制自身。
///</summary>
privatevoidDraw(objectsender,GameTimerEventArgse){
graphicsDevice.Clear(Color.CornflowerBlue);
elementRenderer.Render();//通过elementRenderer呈现Silverlight中的UElement
spriteBatch.Begin();
spriteBatch.Draw(elementRenderer.Texture,Vector2.Zero,Color.White);//通过XNA的形式将elementRenderer整体绘制出来
spriteBatch.DrawString(font,"HelloGame,I,mXNAFont",newVector2(20,55),Color.Yellow);//第三个参数代表绘制的绝对位置
spriteBatch.End();
}

默认情况下,后Draw的对象显示在最顶层;当然,如果你想动态更改他们之间的层级深度关系,可以使用Draw方法的其他形态,比如:

值得一提的是,如上面代码所示UIElementRenderer对象创建于GamePage_LayoutUpdated事件中,它的第一个UIElement类型参数为this,即指整个GamePage页面(UserControl):

试想一下,如果换成是一个Image或者ListBox等控件呢?高度自由的UIElementRenderer给了我们SL.XNA游戏开发无限遐想空间,不是吗?

至此,资源布局及代码结构这些毛坯级也是最核心的框架构建完毕,无论您是单纯的Silverlight开发者,或者XNA游戏开发者,亦或者两者通杀型,这个框架都能为你提供可无限拓展的高性能空间。下一节,我将继续为大家深入讲解SL.XNA中的控件交互,关注哦,^ ^。

本节源码下载地址:SLXnaGame1.zip

手记小结:本节非常详细的为大家讲解了如何从0开始一步步搭建基于Silverlight.XNA游戏框架;新手、老手,又或者你擅长的是Silverlight、WPF或XNA;对于初出茅庐的Windows Phone开发者来说这都是一篇开卷有益的启蒙之章,包括后续的更多章节,旨在通过自身的开发经历让朋友们高效率的掌握Windows Phone开发中关于C#、xaml、Silverlight、XNA等多方面知识。毕竟,一个人的能力与时间极其有限,卓越而经典的游戏需要更多的开发者参与进来,相信我们的共同努力可以铸成属于中国人辉煌的游戏江山!

推荐参考:NowpaperWilliams关于Windows Phone的游戏开发博客。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics