从构思到发布 开发Windows Phone 7小游戏

原创
移动开发
本文介绍了Windows Phone 7平台游戏开发过程的整个周期,从游戏构思开始,到游戏发布到Windows Phone Marketplace结束,这个过程的任何细节都不会遗漏。

【51CTO译文】引言

到底能多快地为运行Windows Phone 7的现代化手机开发一款功能完备的游戏?为此你需要具备什么条件?又会面临什么样的难题?

我们将在开发小游戏期间力图解答上述问题。为了营造一种实际的环境,这个功能完备的应用程序开发后将提交到Windows Phone Marketplace。

我们将审查游戏开发过程的整个周期,从游戏构思开始,到游戏发布到Marketplace结束,这个过程的任何细节都不会遗漏。

游戏构思

我们不会仿效其他游戏。我们考虑的是开发一款新颖、有趣、绚丽的游戏——是专门为使用方向感应器和触摸屏的手机而开发的。你只要将手机往不同的侧边倾斜,就可以在方向感应器的帮助下操纵小球,好像小球就在方形盒子里面滚动。

画面上有多个彩球。它们试图撞到白球,给白球着上自己的颜色。

用户只要用指尖点一下彩球,就可以击碎彩球。你点一下球后,就会变成色彩鲜艳的飞溅物,然后会消失,之后新的球会出现。每击碎一个球,就能得到更高的分数。用户能玩的关卡数量没有限制。

玩家的目标应该是得到尽可能高的分数。

为了让玩家可以使用方向感应器(而不仅仅击碎球),为游戏添加了更多的球(黑球)。除非黑球被着上别的颜色,否则不会被击碎。

2D图形对这款游戏来说绰绰有余。此外,需要播放音乐,与方向感应器和触摸屏进行交互。

开发环境

为了针对Windows Phone 7开发应用程序,我们需要Visual C# 2010简易版和Windows Phone开发者工具(Windows Phone Developer Tools)。两者都是免费产品。

然后进入到微软网站,下载和安装下列工具:

Windows Phone开发者工具无法安装到Windows XP上,不过你可以采取这个办法。然而,手机模拟器可能无法在Windows XP上运行,因而***还是安装Windows 7。

如果你已经拥有Visual Studio 2010,***在上面安装Windows Phone开发者工具,因为Visual C# 2010 简易版用起来明显更不方便。

技术选择:XNA对决Silverlight

安装了Windows Phone开发者工具后,可以选择用Studio来创建新的项目类型:Windows Phone应用程序(Windows Phone Application)和Windows Phone游戏(Windows Phone Game)。

哪一个更适合我们呢?

***个项目是面向Windows Phone 7的Silverlight应用程序。相应地,它可以访问几乎所有的类,这些类在通常的Silverlight中得到表示。它有XAML和控件以及Studio中的设计器等。

第二个项目是XNA 4.0.应用程序。它没有任何控件;不过,它有游戏周期,可以快速访问硬件图形的功能特性。

从名称和技术描述来判断,我们需要的是XNA。Silverlight有没有可能也适合开发游戏?用Silverlight for Windows Phone简单测试一下,就会发现这项技术的局限性:数百个同时运动的小图像让手机进入幻灯片模式。如果我们用XNA进行同样的测试,一切都快速运行(拥有数量更多的图像)。

这就是为什么我们用XNA来开发游戏。遗憾的是,没有控件;但目前看来控件也没什么用处。

上手XNA

我们不妨用Studio来创建Windows Phone游戏项目。该项目与游戏类一同出现:

public class Game1 : Microsoft.Xna.Framework.Game

不妨定义用户在玩游戏期间如何握持手机。假设一只手握持手机(旋转手机即可操纵白球)、另一只手点彩球更为舒适。这意味着,我们需要纵向游戏模式。我们为游戏构建器添加了这种模式。

  1. public Game1()  
  2.  
  3. {  
  4.  
  5. graphics = new GraphicsDeviceManager(this);  
  6.  
  7. Content.RootDirectory = "Content";  
  8.  
  9. graphics.SupportedOrientations = DisplayOrientation.Portrait;  
  10.  
  11. graphics.PreferredBackBufferWidth = 480;  
  12.  
  13. graphics.PreferredBackBufferHeight = 800;  
  14.  
  15. graphics.IsFullScreen = true;  
  16.  

 然后在这个类中,我们最感兴趣的是两项功能:Update(更新)和Draw(绘制)。 

  1. protected override void Update(GameTime gameTime)  
  2.  
  3. {  
  4.  
  5. // Allows the game to exit(允许游戏退出)  
  6.  
  7. if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)  
  8.  
  9. this.Exit();  
  10.  
  11. // TODO: Add your update logic here(TODO:在这里添加更新逻辑)  
  12.  
  13. base.Update(gameTime);  
  14.  
  15. }  
  16.  
  17. protected override void Draw(GameTime gameTime)  
  18.  
  19. {  
  20.  
  21. GraphicsDevice.Clear(Color.CornflowerBlue);  
  22.  
  23. // TODO: Add your drawing code here(TODO:在这里添加绘制代码)  
  24.  
  25. base.Draw(gameTime);  
  26.  

***项功能专门用于游戏逻辑处理。我们会得到用户动作(从方向感应器到触摸屏)方面的数据,并算出所有游戏物体的新位置。

第二项功能专门用于绘制屏幕上的游戏物体。

有必要说明:这两项功能执行的总时间不该超过游戏周期的一次迭代。这在手机上相当于33毫秒,因为游戏周期的频率是每秒30帧。如果功能执行的时间超过这个值,游戏速度就会很慢,一些帧就会被遗漏,或者更糟糕的是,游戏会忽略用户动作。

这个游戏实际上是实时应用程序。它对垃圾收集器(Garbage Collector)的动作会有怎样的反应?***是避免垃圾收集器突然干预的情况,那样我们可以顺利地处理物体,不用在每次迭代时创建成千上万物体。一般来说,我们不需要经常创建物体,因为游戏世界的所有物体相对来说历时长久。我们会使用已有物体的结构或链接作为特征参数。这样一来,垃圾收集器不会阻止我们做所需的动作。

我们不妨试着编译和启动应用程程序。在几台电脑上,你可能会看到表明XNA无法在模拟器上启动的消息。这样一来,你可能要创建Windows游戏项目,并附加来自Windows Phone游戏项目的文件(Add As Link,即添加为链接)。同样这些XNA代码可以在手机和Windows(以及XBOX 360)上运行。

***,有一个窗口填满了紫色。这是由于GraphicsDevice.Clear(Color.CornflowerBlue)功能执行的结果。

游戏类中有一个SpriteBatch对象类。我们在绘制时需要这个对象。它含有处理2D图形和文本的所有必要方法。

不过我们仍缺少最重要的方面:游戏内容。#p#

游戏内容

游戏内容是游戏必不可少的不同资源,比如纹理(用于绘制的图像)、字体(输出文本时需要)和声音。

我们在创建Windows Phone游戏项目的同时,还创建了名称是“Content”的另一个项目。我们应该为这个项目添加图像和声音文件。

此外,我们应该设定用于这个项目中文本输出的所有字体。

在Content.Load(assetName)函数的帮助下,将内容装入到程序很简单。游戏类中拥有Content属性。我们连同内容添加到项目中的资源名称应该设成assetName。通常来说,这仅仅是没有扩展名的文件名。要使用的内容类型(Texture2D、SpriteFont或SoundEffect)应该设成T。

内容装入不是一种快速操作,这就是为什么我们在游戏开始时会装入所有内容,以便需要时可以使用。

现在一切已准备好,我们可以开始开发游戏了。

游戏逻辑

所有游戏逻辑都可以放入到GameController这个单独的类中。现在,游戏类中的Update功能看起来如同这样:

  1. protected override void Update(GameTime gameTime)  
  2.  
  3. {  
  4.  
  5. // Allows the game to exit(允许游戏退出)  
  6.  
  7. if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)  
  8.  
  9. this.Exit();  
  10.  
  11. GameController.Update(gameTime);  
  12.  
  13. base.Update(gameTime);  
  14.  

注意:我们把GameTime类的对象发送到GameController.Update。它拥有ElapsedGameTime属性,该发展表明自上一次调用Update功能上以来过去了多长时间。这个时间通常是固定的,相当于手机上的33毫秒。不过,***不要依靠它,而是在所有计算中都使用发送的值。

我们在需要计算游戏世界物体的运动时,将使用ElapsedGameTime。比如说,如果我们需要计算白球的新位置(受手机倾斜的影响),它看起来会这样:

position += (float)(acceleration * gameTime.ElapsedGameTime.TotalSeconds);

小球和飞溅物(它们一出现)的半径按这种方式来计算:

radius += (float)(GrowSpeed * gameTime.ElapsedGameTime.TotalSeconds);

所有计算会用不同的类来进行。每个类都有各自相似的Update(gameTime)功能。GameController.Update会调用它们。#p#

绘制图形

绘制图形发送到GameController类。游戏类中的Draw功能现在看起来这样:

  1. protected override void Draw(GameTime gameTime)  
  2.  
  3. {  
  4.  
  5. GraphicsDevice.Clear(Color.Black);  
  6.  
  7. spriteBatch.Begin();  
  8.  
  9. GameController.Draw(spriteBatch);  
  10.  
  11. spriteBatch.End();  
  12.  
  13. base.Draw(gameTime);  
  14.  

注意:我们只将SpriteBatch对象类发送到GameController.Draw。它只用于绘制。我们这里不需要GameTime,因为所有必要的计算已经在Update功能中进行。

随后,GameController.Draw会从得到游戏世界逻辑的类调用相似的Draw(spriteBatch)功能。

绘制本身看起来相当简单:

spriteBatch.Draw(texture, position, color);

这时我们只能显示准备好的纹理。绘制2D基本图形(直线、长方形和椭圆形等)的功能并不由XNA来提供。

可以通过从内容装入纹理来得到纹理:

Texture2D texture = Content.Load(assetName);

纹理已经被装入了;所有位置已被计算出来;不过,我们可以“调整”颜色!如果我们需要原始纹理颜色,就要有Color.White。

如果我们需要另一种颜色,应该用这种颜色布局来绘制纹理。如果我们要用阿尔法通道(alpha channel)来设定颜色,那么就能透明地绘制纹理。那样,我们就能轻松自如地显现和隐匿物体以及颜色变化(这些正是我们在游戏中需要的)。

为了混合两种颜色,我们使用Color.Lerp函数。为了增加阿尔法通道,应使用Color.FromNonPremultiplied 函数。

另外还有spriteBatch.Draw函数的其他变种,允许变化和扩展文本,这也是我们的游戏所需要的。

文本输出

文本输出就跟图形输出一样简单:

spriteBatch.DrawString(spriteFont, text, position, color);

你可以通过从内容装入SpriteFont类的对象来得到对象:

SpriteFont spriteFont = Content.Load(assetName);

你还可以在这里以同样的方式设置字体颜色。如果你用阿尔法通道设置颜色,文本将是透明的。

如果你用浮动位置设置文本输出的坐标,那么文本在显示时可能会有点失真。这就是为什么在流畅的文本移动没必要时,我们要将坐标舍入到整数。

spriteBatch.DrawString函数有其他变种,允许改变和扩展文本。有必要记住:这种运动会引起文本表述错误。之所以会出现这种情况,是因为XNA并不处理原始向量字体,但能处理栅格表示;项目一编译好,就会创建栅格,随后添加到内容中。#p#

触摸屏

为了定义用户在何处点击屏幕,应该从Microsoft.Xna.Framework.Input.Touch.TouchPanel类得到数据:

  1. foreach (var item in TouchPanel.GetState())  
  2.  
  3. {  
  4.  
  5. if (item.State == TouchLocationState.Pressed  
  6.  
  7. || item.State == TouchLocationState.Moved)  
  8.  
  9. {  
  10.  
  11. // Get item.Position(得到item.Postion)  
  12.  
  13. }  
  14.  

 因此,我们就能得到用户触摸的所有屏幕点。不过,我们需要关于屏幕触摸一次性动作(点击、在屏幕上移动手指时)的数据。这些数据是跟踪屏幕按钮(比如暂停按钮)触摸所必可不少的。为了得到这些数据,应使用手势支持。

在游戏开始时,应该表明我们需要支持手势(点击):

TouchPanel.EnabledGestures = GestureType.Tap;

然后,我们可以在游戏周期的每次迭代得到手势:

  1. while (TouchPanel.IsGestureAvailable)  
  2.  
  3. {  
  4.  
  5. GestureSample gesture = TouchPanel.ReadGesture();  
  6.  
  7. if (gesture.GestureType == GestureType.Tap)  
  8.  
  9. {  
  10.  
  11. // Get guesture.Position(得到guesture.Position)  
  12.  
  13. }  
  14.  

方向感应器

为了得到关于手机倾斜的数据,就要使用Microsoft.Devices.Sensors.Accelerometer类。遗憾的是,我们无法直接从方向感应器得到数据,因为它只支持事件模式。这就是为什么应该使用创建对象和订阅事件的附属类:

  1. accelerometer = new Microsoft.Devices.Sensors.Accelerometer();  
  2.  
  3. accelerometer.ReadingChanged += AccelerometerChanged;  
  4.  
  5. accelerometer.Start(); 

在事件处理器中,我们会记住加速值,并保存起来,以便以后使用:

  1. private void AccelerometerChanged(object sender, AccelerometerReadingEventArgs e)  
  2.  
  3. {  
  4.  
  5. vector = new Vector3((float)e.X, (float)e.Y, (float)e.Z);  
  6.  

加速向量含有三个轴(X轴、Y轴和Z轴)的信息,但我们先只需要头两个轴(用于手机的纵向模式)。这就是为什么在我们的坐标系中返回加速的属性看起来这样:

  1. public Vector2 Acceleration  
  2.  
  3. {  
  4.  
  5. get  
  6.  
  7. {  
  8.  
  9. return new Vector2(vector.X, -vector.Y);  
  10.  
  11. }  
  12.  

是的,这个加速会被施加于白球。#p#

播放声音

可以在SoundEffect类的对象中Play函数的帮助下,播放XNA中的声音。这个类的对象可以从内容装入:

SoundEffect sound = Content.Load(assetName);

只要这么处理,就可以播放声音了。

窗口

许多东西已准备好:白球通过方向感应器来运动,其余球会追白球;一旦撞上,就会变成飞溅物,会计算分数,随后播放声音。看起来像是一切准备就绪?不,没有这么简单。

现在我们需要创建窗口;开始窗口(主菜单)、暂停对话窗口和游戏结束窗口(显示得分数和记录)。

面向Windows Phone的XNA没有窗口,这就是为什么我们得自己创建窗口。不过,实际创建起来比较简单。

用Parent、Children、Bounds、Visible和Enabled这些主要功能以及Update和Draw两个函数创建一个基本控件就够了。然后,应该创建几个子类:Window、Button和Label等。

之后,就很容易在窗口中显示元素。

保存游戏状态

手机上的游戏可以随时暂停,只要点击Home按钮或借助其他任何外部事件。这就是为什么我们要注意随时保存游戏状态,等游戏启动后,可以进一步恢复这个状态。

不妨使用System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings类来保存状态(及设置)。这个类实现了IDictionary接口。我们不妨把所有游戏世界物体(但愿物体数量不多)的的当前状态概括到这个词典中,并调用IsolatedStorageSettings.ApplicationSettings.Save()函数。

退出游戏时,游戏会保存起来。为此,不妨覆盖游戏类中的OnExiting函数:

  1. protected override void OnExiting(object sender, EventArgs args)  
  2.  
  3. {  
  4.  
  5. GameController.SaveState();  
  6.  
  7. base.OnExiting(sender, args);  
  8.  

游戏恢复以类似方式进行。应用程序启动时,我们收到来自IsolatedStorageSettings.ApplicationSettings的数据,恢复所有游戏世界物体。

应用程序的激活和停用

我们的这个应用程序并不总是处于活动状态。有时它可能被停用了(比如来电时),需要再次激活。

为了跟踪这些事件,不妨覆盖游戏类中的OnActivated函数和OnDeactivated函数。

在应用程序停用期间,我们让游戏处于暂停模式,那样用户回来后,关于恢复游戏的对话消息会出现。

此外,为了不浪费处于停用状态的手机的计算资源,我们在游戏类中Update函数的开头添加下列代码: 

  1. if (!IsActive)  
  2.  
  3. {  
  4.  
  5. SuppressDraw();  
  6.  
  7. return;  
  8.  

#p#启动画面

我们的游戏(确切地说,是游戏内容)在几秒钟内装入,在这个装入期间只显示黑色屏幕。我们应该向用户表明应用程序在运行中。为此,应该绘制一个漂亮的启动画面(splash screen),在游戏启动时显示:

splash screen

如果你只是往Silverlight应用程序里面添加针对Windows Phone的SplashScreenImage.jpg文件,启动画面会自动显示。

不过这对XNA项目来说行不通。

我们得改动内容的装入。首先,我们为启动画面做好纹理,然后在***次调用Draw函数的过程中绘制纹理。然后,我们装入其余内容,启动游戏。直到其余内容都装入,启动画面才显示。

现在,这个游戏看起来漂亮多了。

手机中的游戏位置

为了让我们的游戏出现在手机中的Games部分(只要点击手机开始屏幕上的XBOX LIVE按钮,就能访问它),需要编辑项目中的WMAppManifest.xml文件。应该在这个文件中,而不是在Genre="Apps.Normal"这一行中编写Genre="Apps.Games"。

另外我们把游戏名称和描述放入到同一个文件(分别是Title和Description属性)。删除不必要的需求(section)。在这个部分中,我们只留下方向感应器支持。这个版本的游戏不需要其他部分。

该项目应该有两幅图片:GameThumbnail.png和Background.png。

Games部分的游戏显示区需要***幅图片。手机的开始屏幕需要第二幅图片。这两幅图片的大小都应该是173×173像素。

试用模式

由于我们开发的这个游戏将是收费游戏,需要添加试用模式支持。试用功能已经内置到平台中,在Microsoft.Phone.Marketplace.LicenseInformation类的帮助下完成:

  1. var license = new Microsoft.Phone.Marketplace.LicenseInformation();  
  2.  
  3. return license.IsTrial(); 

由于该函数相当慢,我们不会在每次游戏周期迭代时调用它。相反,我们只会在应用程序激活时才调用它(在游戏类的OnActivated函数中,结果将作为变量来保存)。

如果应用程序在试用模式下启动,那么在试用几分钟后,就会出现提供游戏购买(或开始新游戏)的窗口。

点击购买按钮时,我们就会调用、在Windows Phone Marketplace中显示应用程序:

  1. var market = new Microsoft.Phone.Tasks.MarketplaceDetailTask();  
  2.  
  3. market.Show(); 

购买后,用户可以回到应用程序,继续玩游戏。

后退按钮

一切几乎已准备好,我们要把应用程序发送到Windows Phone Marketplace。在发布之前,该应用程序经过微软的认真测试;几天后,微软给出了结论:拒绝接受。原因出在哪里?

问题出在我们在Update函数中留下的代码。

  1. protected override void Update(GameTime gameTime)  
  2.  
  3. {  
  4.  
  5. // Allows the game to exit(允许游戏退出)  
  6.  
  7. if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)  
  8.  
  9. this.Exit();  
  10.  
  11. ...  
  12.  

后退(Back)这个硬件按钮必须以某种方式来工作,也就是让用户回到前一个屏幕。如果用户在应用程序的开始屏幕(主游戏菜单)上,可以使用该按钮来退出应用程序。如果处在暂停模式下,该按钮可以让用户回到游戏中。

在我们这个情况下,后退按钮总是调用应用程序退出。不妨清除这个代码,让后退按钮能够正确工作,再次发送给微软进行审查。重复审查所用时间比较少。#p#

结果

是的,这个应用程序得到了接受!现在,该游戏出现在Windows Phone Marketplace中了。

我们在游戏开发上总共花了两周时间(发布到Marketplace又花了两周时间)。***,我们拥有了一个漂亮而诱人的游戏:

游戏

***WP7游戏网站的编辑人员评论了这款游戏

提交一周后,这个游戏就跻身于Windows Phone Marketplace***的100款收费游戏中。

我们从这次游戏开发中能够得到什么样的结论?答案是:为Windows Phone 7开发游戏其实简单得很!

51CTO观点

诺基亚日前与微软正式签订Windows Phone 7合作协议,并放弃Symbian系统的研发业务,这足以表明Windows Phone操作系统将成为诺基亚主要的智能手机平台。诺基亚与微软的强强联手将会给开发者带来良好的开发环境,学习Windows Phone平台对开发者来说会是个不错的选择。

原文出处

【51CTO.com独家特稿,非经授权谢绝转载,合作媒体转载请注明原文作者及出处!】

【编辑推荐】

  1. 微软发布WP软件包 吸引iOS开发人员
  2. 如何在Windows Phone 7 3D开发中使用纹理贴图
  3. 使用IronRuby开发Windows Phone 7应用程序
  4. 独立开发者分享手机游戏开发经验
  5. SocialTimes:手机社交游戏开发秘籍

 

责任编辑:佚名 来源: 51CTO.com
相关推荐

2010-08-10 09:11:12

Windows PhoNXA

2011-01-13 14:29:54

2010-12-16 10:06:31

Windows Pho

2010-08-10 13:21:41

Windows PhoWindows Pho

2010-08-06 15:44:28

Windows PhoWindows PhoSilverlight

2013-07-30 11:18:37

Windows PhoWindows Pho

2010-07-21 09:11:57

Windows PhoWindows PhoWindows Pho

2010-08-24 09:32:41

Windows PhoWindows Pho

2012-03-18 19:44:11

Windows Pho

2012-06-25 14:45:50

2011-06-07 11:35:38

Windows Pho

2012-08-16 10:35:50

Windows Pho

2011-01-07 09:04:14

更新内容Windows Pho

2010-12-14 18:48:49

微软

2010-09-10 09:05:03

XNAWindows Pho

2010-06-06 15:54:54

Windows Pho

2010-09-02 13:05:51

Windows PhoWindows Pho

2010-04-08 17:40:23

Windows Pho

2010-09-17 09:34:29

开发工具Windows Pho

2010-08-13 08:21:11

Windows Pho
点赞
收藏

51CTO技术栈公众号