【IT168技术】在今天的开发领域,能从相同的游戏代码库开发支持多平台运行的游戏很困难,通常,同一游戏项目想要在不同平台上运行时,代码需要重写,这会引起许多头痛的问题,挫折和时间损失。XNA Game Studio正是在这种背景下诞生的,XNA Game Studio是一套来自微软公司的工具,它使游戏开发过程变得更加简单和有趣。XNA Game Studio真正有吸引力的地方在于,它为开发人员提供了一个无缝构建多平台游戏的方法。在这篇文章中,我们将介绍如何使用统一的代码库构建一个简单的XNA Framework游戏,让它可在Windows Phone 7、Xbox和PC上运行。
开发者需要什么
如果你还没有这样做过,请先访问http://create.msdn.com/下载Visual Studio 2010和XNA Game Studio 4.0的试用版,然后安装它们。
我们要构建什么
我们将要构建一个非常简单的XNA Framework游戏,主要目的是介绍如何将它部署到多个不同的平台,这个游戏说来很简单,就是将一个高尔夫球移动到某个目标位置。在Windows Phone 7上,使用加速计控制高尔夫球,在Xbox上,使用控制器上的游戏杆移动,在PC上,使用键盘上的箭头键移动,但用户将高尔夫球移动到目标位置,游戏就重新开始。怎么样,超级简单吧!
第1步:构建Windows Phone 7 XNA Framework游戏
我们先创建一个新的Windows XNA Framework项目,然后再修改为在Windows Phone 7和Xbox上运行的版本。点击“文件”*“新建项目”,在“XNA Framework Game Studio 4.0”下,选择“Windows Phone Game (4.0)”,项目命名为“CrossPlatformXNA FrameworkGame”,如图1所示。

▲图 1 创建新的Windows Phone游戏项目
先介绍一点理论的东西
接下来,我们需要给我们的项目编写代码,首先请允许我简单描述一下XNA Framework是如何工作的,下图展示了XNA Game Studio应用程序的运行流程。

▲图 2 XNA Game Studio应用程序的运行流程
代码以进入构造程序开始,然后是初始化变量,加载任何内容到ContentManager,再进入游戏循环,游戏循环将是这里最主要的代码。默认情况下,Windows Phone 7 XNA Framework应用程序以每秒30帧的速度运行,这意味着游戏循环每秒要被调用30次。游戏循环实际上是由两个函数 — Update()和Draw() — 组成的。
接下来向我们的项目导入一些纹理
在开始编码之前,让我们先导入一些游戏会使用到的纹理,我已经为高尔夫球和目标创建了纹理,你在你的项目中也可以使用,每个纹理有两个分辨率,因为游戏运行的平台不同,分辨率可能也不同,因此我提供了两个图像,一个是Xbox上的高清晰度分辨率版本,另一个是Windows Phone和PC上的低分辨率版本。
注:Windows Phone 7和Xbox包括硬件缩放功能,可以根据需要放大或缩小图像。

▲图 3 高尔夫球和目标对象纹理的高分辨率和低分辨率版本
将这些图像文件保存到本地计算机,然后转到Visual Studio 2010中的解决方案资源管理器,在CrossPlatformXNA FrameworkGameContent项目上点击右键,选择“添加”*“现有项目”,然后选择你保存在本地计算机上的图像文件。

▲图 4 添加现有项目(图像文件)
开始编码
现在我们粗略地知道了XNA Framework应用程序如何运行,并且创建了一些可用于构建应用程序的纹理,终于可以开始编写程序代码了。
1、首先将CrossPlatformXNA FrameworkGame项目包含的Game1.cs重命名为“GolfGameMain.cs”;
2、然后打开GolfGameMain.cs文件,定义下面的变量,它们将用于我们的两个纹理,位置和速度信息:
Texture2D ball;
Texture2D target;
// Location information
Vector2 ballPt;
Vector2 targetPt;
// normalized velocity vector
Vector2 ballVelocity;
// Used to multiply on the velocity vector
float velocityMultiplier;
3、在Initialize()方法内添加下面的代码,设置高尔夫球的速度:
velocityMultiplier = 13.0f;
4、自己动手写一个名为ResetGame()的函数,初始化高尔夫球的速度和位置值,函数内容如下,我们指定高尔夫球的初始位置为左上角稍微靠右的位置,目标在右下角稍微靠左的位置,高尔夫球的速度从0开始:
{
ballVelocity = Vector2.Zero;
ballPt = new Vector2(target.Width, target.Height);
targetPt = new Vector2(graphics.GraphicsDevice.Viewport.Width - 2*target.Width, graphics.GraphicsDevice.Viewport.Height - 2*target.Height);
}
5、向LoadContent()函数添加下面的代码,加载我们导入到内容管理器的纹理,完成后,调用ResetGame()指定对象的位置:
ball = Content.Load
target = Content.Load
ResetGame();
6、现在我们已将内容载入到内容管理器,接下来让我们根据描述的位置绘制加载到屏幕的纹理,修改Draw()函数,修改后内容如下:
{
GraphicsDevice.Clear(Color.White);
spriteBatch.Begin();
// Draw our objects to the screen
spriteBatch.Draw(target, targetPt, Color.White);
spriteBatch.Draw(ball, ballPt, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
7、至此,我们应该可以运行这个程序,看到高尔夫球和目标绘制到屏幕上,看起来的效果应该和下图差不多:

▲图 5 程序运行效果
8、下一步要为做碰撞检查添加一个函数,当高尔夫球完全包含在目标内时,这个函数开始执行。
{
Rectangle ballRect = new Rectangle((int)ballPt.X, (int)ballPt.Y, ball.Width, ball.Height);
Rectangle targetRect = new Rectangle((int)targetPt.X, (int)targetPt.Y, target.Width, target.Height);
return targetRect.Contains(ballRect);
}
9、有了碰撞检测逻辑后,我们可以给高尔夫球移动到目标的过程也添加上逻辑,将下面的代码添加到Update()函数即可,检测到碰撞后,我们可以通过调用前面创建的函数ResetGame()重置游戏:
Keys[] pressedKeys = kState.GetPressedKeys();
ballVelocity = Vector2.Zero;
foreach (Keys k in pressedKeys)
switch (k)
{
case Keys.Right:
ballVelocity.X = velocityMultiplier;
break;
case Keys.Left:
ballVelocity.X = -velocityMultiplier;
break;
case Keys.Down:
ballVelocity.Y = velocityMultiplier;
break;
case Keys.Up:
ballVelocity.Y = -velocityMultiplier;
break;
}
ballPt += ballVelocity;
if (CheckForCollision())
ResetGame();
10、测试一下游戏运行时的状况,现在你应该可以使用键盘上的箭头控制高尔夫球的移动,当高尔夫球移动到目标时,游戏应该被重置。至此,你已经完成了主要的工作,从下一步开始,我们将修改代码,使其支持Windows Phone 7和Xbox平台。
第2步:修改代码,使其支持Windows Phone 7
11、为Windows Phone 7创建一个CrossPlatformXNA FrameworkGame项目副本,在项目名称上点击右键,选择“为Windows Phone创建项目副本”,第二个项目应该就这样创建好了,项目名称应该是“Windows Phone 7 copy of CrossPlatformXNA FrameworkGame”。
12、在“引用”上点击右键,选择“添加引用”,为你的项目添加一个引用,如下图所示,然后选择“Microsoft.Devices.Sensors”,点击“添加”。

▲图 6 添加引用
13、现在关闭所有打开的文件,选择并打开“Windows Phone 7 copy of Cross PlatformXNA FrameworkGame”项目下的GolfGameMain.cs文件。
14、在项目顶部添加下面的命名空间,通过宏“#if WINDOWS_PHONE”批量导入命名空间,这样可以告诉编译器忽略“#if…#endif”内的代码,除非它是为Windows Phone 7编译,后面我们会使用“if XBOX… #endif”为Xbox做同样的处理。
#if WINDOWS_PHONE
using Microsoft.Devices.Sensors;
#endif
15、添加一个类级变量,增加加速计支持
Accelerometer accelerometer;
#endif
16、在构造器中添加下面的代码,帮助修改设置,处理游戏如何显示,以及游戏运行在什么分辨率下。
// Hides the battery and signal strength bar
graphics.IsFullScreen = true;
#else
// Code to support changing the resolution of the game for the Xbox to handle high def screens
graphics.IsFullScreen = false;
graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
#endif
17、修改前面编写的Initialize()函数,初始化加速计,根据平台的不同为高尔夫球设置不同的速度。
if (accelerometer == null)
{
// Instantiate the accelerometer sensor
accelerometer = new Accelerometer();
accelerometer.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(accelerometer_ReadingChanged);
accelerometer.Start();
}
velocityMultiplier = 10.0f;
#elif WINDOWS
velocityMultiplier = 13.0f;
#else
velocityMultiplier = 20.0f;
#endif
18、添加一个函数处理我们在上一步注册的事件,这个函数将处理加速计读数变化,我们基于此设置高尔夫球的速度,这与在Windows版本中处理不同按键事件的代码是等效的。
void accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
ballVelocity.X = -velocityMultiplier*(float)e.Y;
ballVelocity.Y = -velocityMultiplier*(float)e.X;
}
#endif
19、修改LoadContent()函数,使其可以根据平台的不同加载不同的纹理,对Windows Phone和Windows平台,我们将加载低分辨率图像,对于Xbox,我们将加载高分辨率图像。
spriteBatch = new SpriteBatch(GraphicsDevice);
// Load our game content to the ContentManager
#if WINDOWS_PHONE || WINDOWS
ball = Content.Load<Texture2D>("ball_small");
target = Content.Load<Texture2D>("target_small");
#else
ball = Content.Load<Texture2D>("ball");
target = Content.Load<Texture2D>("target");
#endif
ResetGame();
20、修改Update()函数,如果我们是为Windows版本编译的话,我们只能看到键盘输入。
KeyboardState kState = Keyboard.GetState(PlayerIndex.One);
Keys[] pressedKeys = kState.GetPressedKeys();
ballVelocity = Vector2.Zero;
foreach (Keys k in pressedKeys)
switch (k)
{
case Keys.Right:
ballVelocity.X = velocityMultiplier;
break;
case Keys.Left:
ballVelocity.X = -velocityMultiplier;
break;
case Keys.Down:
ballVelocity.Y = velocityMultiplier;
break;
case Keys.Up:
ballVelocity.Y = -velocityMultiplier;
break;
}
#endif
21、很好,现在在解决方案资源管理器中的“Windows Phone 7 copy of CrossPlatformXNA FrameworkGame”项目上点击右键,运行“调试”*“运行新实例”,这时应该会启动Windows Phone 7模拟器,如下图所示,但遗憾的是,不能在模拟器中测试加速计功能,除非你将应用程序部署到一台真实的Windows Phone 7手机上。

▲图 7 在Windows Phone 7模拟器上运行的效果
下面我们创建一个Xbox版本
22、为Xbox创建CrossPlatformXNA FrameworkGame项目的一个副本,在项目名称上点击右键,选择“为Xbox 360创建项目的副本”,第三个项目应该就创建好了,项目名称应该为“Xbox 360 copy of CrossPlatformXNA FrameworkGame”。
23、最后一次修改构造器,为设置游戏的分辨率添加一个事件处理程序,最终的构造器看起来应该是:
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Frame rate is 30 fps by default for Windows Phone.
TargetElapsedTime = TimeSpan.FromTicks(333333);
#if WINDOWS_PHONE
// Hides the battery and signal strength bar
graphics.IsFullScreen = true;
#else
// Code to support changing the resolution of the game for the Xbox to handle high def screens
graphics.IsFullScreen = false;
graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
#endif
}
24、现在我们必须为我们注册的事件定义函数的定义。
{
#if XBOX
foreach (Microsoft.Xna.Framework.Graphics.DisplayMode displayMode in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
{
// High def resolution support for Xbox (if available)
if (displayMode.Width == 1920 || displayMode.Width == 1280)
{
e.GraphicsDeviceInformation.PresentationParameters.BackBufferFormat = displayMode.Format;
e.GraphicsDeviceInformation.PresentationParameters.BackBufferHeight = displayMode.Height;
e.GraphicsDeviceInformation.PresentationParameters.BackBufferWidth = displayMode.Width;
}
}
#endif
}
25、接下来我们必须修改Update()函数,添加Xbox控制器支持,这个函数的最终版本代码如下:
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
Vector2 velocity = Vector2.Zero;
#if WINDOWS
KeyboardState kState = Keyboard.GetState(PlayerIndex.One);
Keys[] pressedKeys = kState.GetPressedKeys();
ballVelocity = Vector2.Zero;
foreach (Keys k in pressedKeys)
switch (k)
{
case Keys.Right:
ballVelocity.X = velocityMultiplier;
break;
case Keys.Left:
ballVelocity.X = -velocityMultiplier;
break;
case Keys.Down:
ballVelocity.Y = velocityMultiplier;
break;
case Keys.Up:
ballVelocity.Y = -velocityMultiplier;
break;
}
#elif XBOX
ballVelocity.X = velocityMultiplier * GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.X;
ballVelocity.Y = -velocityMultiplier * GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.Y;
#endif
ballPt += ballVelocity;
if (CheckForCollision())
ResetGame();
base.Update(gameTime);
}
26、就这样,我们编写了一个可以运行在三个平台上的应用程序,是不是很简单呢?