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

原创 2012年03月29日 21:15:24

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

作为一个专情的人,深爱着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
namespace SlXnaDemoLib {

    /// <summary>
    
/// 全局(数据和方法)
    
/// </summary>
    public static class Global {

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

        /// <summary>
        
/// 项目Resource资源路径
        
/// </summary>
        public static string ProjectPath(string uri) {
            return string.Format("/{0};component/Resource/{1}", ProjectName, uri);
        }

        /// <summary>
        
/// 获取项目Resource中的位图
        
/// </summary>
        
/// <param name="uri">路径</param>
        
/// <returns></returns>
        public static BitmapImage GetImage(string uri) {
            return new BitmapImage(new Uri(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"事件,并于后台编写相应代码实现页面之间的点击跳转功能。

        // 简单的按钮单击事件处理程序可使我们转至第二页
        private void Button_Click(object sender, RoutedEventArgs e) {
            NavigationService.Navigate(new Uri("/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的资源存放形式不同,播放方式亦不同):

音乐播放
        SoundEffect sound;
        Song song;

        // 构造函数
        public MainPage() {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, EventArgs e) {
            content = (Application.Current as App).Content;
            song = content.Load<Song>("Media/MySong");
            MediaPlayer.Play(song);
        }

        // 简单的按钮单击事件处理程序可使我们转至第二页
        private void Button_Click(object sender, RoutedEventArgs e) {
            sound = content.Load<SoundEffect>("Audio/MyAudio");
            sound.Play();
            NavigationService.Navigate(new Uri("/GamePage.xaml", UriKind.Relative));
        }

 

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

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

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

       UIElementRenderer elementRenderer;
        public GamePage() {
            InitializeComponent();
            this.SupportedOrientations = SupportedPageOrientation.Landscape;
            this.LayoutUpdated += GamePage_LayoutUpdated;
            ....
        }

        /// <summary>
        
/// 允许页面绘制自身。
        
/// </summary>
        private void Draw(object sender, GameTimerEventArgs e) {
            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>
        void GamePage_LayoutUpdated(object sender, EventArgs e) {
            if (ActualWidth > 0 && ActualHeight > 0 && elementRenderer == null) {
                elementRenderer = new UIElementRenderer(this, (int)ActualWidth, (int)ActualHeight);
            }
        }

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

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

        protected override void OnNavigatedTo(NavigationEventArgs e) {
            // 设置图形设备的共享模式以启用 XNA 呈现
            graphicsDevice.SetSharingMode(true);
            // 创建可以用来绘制纹理的新 SpriteBatch。
            spriteBatch = new SpriteBatch(graphicsDevice);
            // TODO: 使用 this.content 在此处加载游戏内容
            textBlock = new TextBlock() { Text = "Hello Game, I,m Silverlight TextBlock" };
            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>
        private void Draw(object sender, GameTimerEventArgs e) {
            graphicsDevice.Clear(Color.CornflowerBlue);
            elementRenderer.Render(); //通过elementRenderer呈现Silverlight中的UElement
            spriteBatch.Begin();
            spriteBatch.Draw(elementRenderer.Texture, Vector2.Zero, Color.White); //通过XNA的形式将elementRenderer整体绘制出来
            spriteBatch.DrawString(font, "Hello Game, I,m XNA Font"new Vector2(2055), 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的游戏开发博客。

Silverlight.XNA(C#)跨平台3D游戏研发手记:(七)向Windows Phone移植之双向交互

继完成游戏主体框架搭建后,接下来我将通过SL.XNA模式中Silverlight控件与XNA对象之间双向交互操作的例子,向大家进一步讲解框架的拓展使用及简单的承载演示。在此之前大家需要理解Window...

Silverlight.XNA(C#)跨平台3D游戏研发手记:(三)蜂窝拓扑结构在SLG地图布局中的应用

上一节给大家讲解了如何在四边形单元格基础上构建SLG地图场景,并实现移动、战斗的基础框架;热爱SLG的朋友一定非常清楚,绝大多数的SLG游戏地形单元格都可归为四类:四边四向、四边八向和四边六向、六边六...

Silverlight.XNA(C#)跨平台3D游戏研发手记:(一)差集运算在SLG战斗范围设定中的应用

战棋游戏通常指以回合制为基础,角色在地图上按格移动作战的游戏,好比下棋一样,该类型游戏更侧重于策略,节奏较缓慢,注重精美、绚丽的画面,考验的是玩家运筹全局的智慧。耳熟能详的比如《梦幻模拟战》、《火焰纹...

Silverlight.XNA(C#)跨平台3D游戏研发手记:(二)四叉树遍历与人工智能A*算法在SLG移动路径范围测算中的应用

除战斗范围设定外,说到SLG中最有趣而经典的算法莫过于角色可移动范围的测算与寻路。当前成熟的SLG商业游戏中,以《火焰纹章》、《高级大战争》等系列新作为代表,它们在传统的基础上将可移动范围进行了拓展,...

Silverlight.XNA(C#)跨平台3D游戏研发手记:(四)SLG无限自由之过场动画

最初迷恋《火纹》,便是因为战斗时的场景切换(战斗特写);《梦幻模拟战》、《高达》、《高级大战争》亦如是。特别喜欢这些电子公仔上演一幕幕华丽的战局,如果要说一款战棋类SLG的精髓在哪?毫无疑问:没有战斗...

Silverlight.XNA(C#)跨平台3D游戏研发手记:(十)3D 场景与控制设计①

模型和骨骼动画仅仅是开启3D游戏的敲门砖,置入基于摄像机的场景设计方能呈现最完美的3D游戏。本节,我们依旧从简单着手,一步步创建基于模型的3D游戏场景。《XNA4.0学习指南(中文)》是一本绝对值得一...

Silverlight.XNA(C#)跨平台3D游戏研发手记:3D SLG(策略战棋游戏)设计案例

某天,当你一不小心发现已经够随心所欲的驾驭3D摄像机之时,任何类型的3D游戏都将成为囊中玩物,过往如烟。 回忆逝去的童年让我极度惦记的SLG策略战棋游戏,或许对于大多数玩家来说,它费时费力不被讨好;...

XNA那些事(六)--WINDOWS PHONE 游戏开发中的3D摄像机

马上国庆了,在这里祝各位同仁,国庆快乐,合家团圆! 前一段把3D建模当中一些基础性的工作开了个头,那么切入正题,接下来就必须要讨论一个3D游戏建模当中的一个重要概念--摄像机。 如果大家之...

XNA那些事(七)-详解WINDOWS PHONE 3D游戏中运动的摄像机与精灵(上)

博客进行到这里需要上一节代码课了,需要用一个例程来巩固一下我们之前所讲述的内容。为了本次的讲座我修改了一下一个比较典型的XNA的精典例程,大家可以在这个网址进行下载,http://download.c...

Windows phone 7 高级编程——使用visual stdio 、silverlight与XNA进行应用和游戏开发

Windows phone 7 高级编程——使用visual stdio 、silverlight与XNA进行应用和游戏开发 基本信息 原书名: Professional Windows ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Silverlight.XNA(C#)跨平台3D游戏研发手记:(六)向Windows Phone移植之框架构建
举报原因:
原因补充:

(最多只允许输入30个字)