体感游戏设计

 

摘要

Windows Phone 7 智能型手机支持完整的传感器 (Sensor) 功能,包括:重力传感器 (G-Sensor)、数字罗盘、趋近传感器、以及环境光线传感器,这些传感器可以视为另外一种型态的输入,可以用来控制游戏程序的进行。在这一篇文章,我们将会利用 Windows Phone 7 智能型手机的重力传感器,让使用者能够利用传感器代替输入设备,达到控制游戏执行的目的。

认识传感器

传感器可以视为一种特殊的输入设备,使用者可以不需要特别执行任何输入的动作,程序就可以依据传感器输入的数据做出反应。例如程序可以利用数字电子罗盘得到方向相关的数据,协助定位相关的功能进行定位;利用光线传感器感应外界光线的强弱,自动调节智能型手机屏幕的亮度;利用接近传感器判断智能型手机是否贴近使用者的脸部欲进行通话的动作,而锁定屏幕,避免误触智能型手机的功能而影响通话的进行;利用重力传感器感应智能型手机运动的方向,并据以调整智能型手机的屏幕显示方向,或是改变程序显示的内容。例如类似弹珠台的游戏程序就可以善用重力传感器模拟使用者摇晃弹珠台,以改变弹珠的滚动方向的动作,达到逼真的游戏操作体验。除此之外,重力传感器在支持 GPS (全球定位系统) 功能方面,能够在智能型手机接收不到卫星讯号时利用智能型手机运动的方法推断用户的位置。

传感器在游戏程序的应用

传感器在游戏程序的应用很广泛,例如使用者挥动手臂的动作可以模拟使用球棒挥击棒球的动作,模拟掷出保龄球的动作,拍击网球、羽毛球、或乒乓球的动作,模拟丢掷骰子,甚至可以模拟游戏者身体移动的方向,跳跃的高度与距离,让使用者融入游戏的场景,达到与游戏真实互动的感觉,而不是像传统的游戏,游戏的使用者感觉较像局外人。

Microsoft、任天堂、与 Sony 等游戏大厂在体感游戏开发方面都有丰富的成果,而且也都得到游戏玩家正面的反应。Windows Phone 7 智能型手机支持了完整的传感器功能,让程序设计师能够很方便开发出利用传感器控制的俄罗斯方块,在不靠键盘输入的状况下利用倾斜智能型手机的方式控制方块掉落的位置。

Windows Phone 7 体感游戏设计支持

了解传感器的用途以及传感器在游戏程序上的应用之后,接下来我们就要利用 Windows Phone 7 智能型手机提供的重力传感器开发能够利用传感器控制游戏显示的内容的游戏。

Windows Phone 7 智能型手机提供的重力传感器可以利用量测重力的原理判断智能型手机移动的方向,允许使用者利用摇动或甩动智能型手机的方式控制游戏的执行,其原理和汽车的安全气囊相同,在侦测到汽车快速减速的时候立刻充气以保护驾驶人与乘客不会受伤。

要使用重力传感器当做游戏程序的输入,以 XNA 为基础的游戏程序可以利用 Accelerometer 类别提供的功能启用/停用重力加速器,取得重力加速器的状态,以及处理重力加速器引发的事件。有关 Accelerometer 类别常用的属性可以参考表1 的说明:

表1:Accelerometer 类别常用的属性

属性名称 说明
State 管理重力加速器状态的属性,其型态为 SensorState 列举型态。有关 SensorState 列举型态合法的内容值可以参考表4 的说明。

 

Accelerometer 类别常用的方法可以参考表2 的说明:

表2:Accelerometer 类别常用的方法

方法名称 说明
Start 开始从重力加速器读取数据。
Stop 结束从重力加速器读取数据。

 

Accelerometer 类别常用的事件可以参考表 3 的说明:

表3:Accelerometer 类别常用的事件

事件名称 说明
ReadingChanged 当重力加速器读取到数据时会引发的事件。

 

处理 ReadingChanged 事件的事件处理程序的第二个参数的型态为 AccelerometerReadingEventArgs 类别,其 X、Y、与 X 属性的内容值代表智能型手机在 X 轴、Y 轴、和 Z 轴的加速方向,而不是三度空间的坐标,其单位为重力单位,也就是 G 力 (1G = 9.81 m/s2)。除了 X、Y、与 Z 三个属性以外,还有一个名称为 Timestamp 的属性,负责记录重力加速器读取数据的时间点。有关 X 轴、Y 轴、和 Z 轴的加速方向可以参考图1 的说明:

图1:X 轴、Y 轴、和 Z 轴的加速方向

请注意当智能型手机放在平坦的桌面上,而且正面朝上的时候,AccelerometerReadingEventArgs 类别的 Z 字段的内容值会是 -1.0,表示 Z 轴承受 -1G 的重力,而当智能型手机放在平坦的桌面上,而且正面朝下的时候,AccelerometerReadingEventArgs 类别的 Z 字段的内容值就会是 +1.0,表示 Z 轴承受 1G 的重力。

[说明]

透过 Accelerometer 类别的 State 属性取得的重力加速器状态是 SensorState 列举型态的数据,其合法的内容值请参考表4 的说明:

表4:SensorState 列举型态合法的内容值

内容值名称 说明
NotSupported 未支持重力加速器。
Ready 重力加速器处于可以处理数据的状态。
Initializing 重力加速器正在初始化。
NoData 未支持重力加速器。
NoPermissions 呼叫者没有权限取用重力加速器接收到的数据。
Disabled 重力加速器处于禁用的状态。

 

要使用重力加速器判断智能型手机加速的方向,首先您必须使用鼠标的右键点中 [Solution Explorer] 窗口中的项目名称,从出现的菜单选择 [Add Reference] 功能,然后于出现的窗口中选择名称为 Microsoft.Devices.Sensors 的组件,如图2 所示:

图2:参考名称为 Microsoft.Devices.Sensors 的组件的画面

做好之后请按下 [OK] 键完成参考组件的动作。接下来请于 Game1 类别中加入以下的变量宣告,负责管理重力加速器装置:

Accelerometer gSensor;                               //管理重力加速器的變數

然后于 Initialize 方法执行建立 Accelerometer 类别的对象的动作,为 Accelerometer 类别的对象的 ReadingChanged 事件制作事件处理程序,并呼叫 Accelerometer 类别的 Start 方法,开始接收从重力加速器输入的数据,如下:

protected override void Initialize()
{
    gSensor = new Accelerometer();                  //建立 Accelerometer 類別的物件
    gSensor.ReadingChanged += new 
        EventHandler<AccelerometerReadingEventArgs>(
   gSensor_ReadingChanged); //處理 Accelerometer 類別的物件的 ReadingChanged 事件
    gSensor.Start();                                //開始接收重力加速器輸入的資料
    base.Initialize();
}

应用程序只要于 Accelerometer 类别的对象的 ReadingChanged 事件的事件处理程序中利用型态为 AccelerometerReadingEventArgs 类别,名称为 e 的参数,就可以得知 Windows Phone 7 装置加速的方向,如下:

void gSensor_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
    //取用 e.X, e.Y, e.Z
}

利用传感器移动 XNA 游戏显示的物体

了解 Windows Phone 7 对体感游戏设计的支持之后,接下来我们就要设计一个能够支持体感控制的 XNA 游戏,让使用者能够以倾斜 Windows Phone 7 智能型手机的方式移动物体。

首先请启动 Visual Studio 2010 Express for Windows Phone,建立一个 [Windows Phone Game (4.0)] 型态的项目,然后加入游戏程序欲显示的图片到 Content Pipeline 项目中。

做好之后请为 Game1 类别加入以下的变量宣告,负责管理欲显示的图片和图片的显示位置,以及管理重力加速器和记载加速度方向的变量:

Texture2D Logo;                                        //管理欲顯示的圖片的變數
Vector2 LogoPosition;                               //管理圖片顯示位置的變數
Accelerometer gSensor;                              //管理重力加速器的變數
Vector2 LogoVelocity;                               //管理加速度方向的變數

宣告妥游戏程序欲使用的变量之后请编辑 Game1 类别的建构函式,执行设定游戏窗口的高度与宽度的工作,设定好游戏窗口高度与宽度的 Game1 类别建构函式如下:

public Game1()
{
    graphics = new GraphicsDeviceManager(this);
    Content.RootDirectory = "Content";
    graphics.PreferredBackBufferWidth = 480;                    //設定遊戲視窗的寬度
    graphics.PreferredBackBufferHeight = 800;                   //設定遊戲視窗的高度
    TargetElapsedTime = TimeSpan.FromTicks(333333);
}

因为我们想让游戏的使用者能够利用传感器控制游戏显示的对象,所以我们必须在 Game1 类别的 Initialize 方法中启用重力加速器,做好的 Initialize 方法如下:

protected override void Initialize()
{
    gSensor = new Accelerometer();                  //建立 Accelerometer 類別的物件
    gSensor.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(
gSensor_ReadingChanged);                //為建立 Accelerometer 類別的物件指定
//ReadingChanged 事件的事件處理程序
    gSensor.Start();                                            //啟動重力加速器
    base.Initialize();
}

上述的程序代码需要用到名称为 gSensor_ReadingChanged 的事件处理程序,所以我们必须在 Game1 类别中加入以下的方法,负责处理 Accelerometer 类别的对象 (即重力加速器) 引发的 ReadingChanged 事件:

void gSensor_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
    LogoVelocity.X += (float)e.X;                           //取得 X 軸的加速度
    LogoVelocity.Y += -(float)e.Y;                          //取得 Y 軸的加速度
    LogoPosition += LogoVelocity;               //將加速度的內容值加入到圖片的位置
}

建立妥管理 Accelerometer 类别的对象,启动重力加速器之后,并制作好处理重力加速器引发的 ReadingChanged 事件的事件处理程序之后,请编辑 Game1 类别的 LoadContent 方法,加载游戏程序欲显示的图片,顺便设定图片默认的显示位置,做好的 LoadContent 方法如下:

protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);
    Logo = Content.Load<Texture2D>("xna");                //載入遊戲程式欲顯示的圖片
    Viewport viewport = graphics.GraphicsDevice.Viewport;       //取得遊戲視窗的視界
    LogoPosition = new Vector2( (viewport.Width - Logo.Width) / 2, (viewport.Height – 
                      Logo.Height) / 2);     //設定圖片預設顯示的位置在遊戲視窗的正中央
}

因为我们在初始化的阶段启用了重力加速器传感器,所以我们需要在游戏程序结束之前关闭重力加速器。您可以编辑 Game1 类别的 UnloadContent 方法,于 UnloadContent 方法中呼叫 Accelerometer 类别的对象的 Stop 方法,将重力加速器关闭,编辑好的 UnloadContent 方法如下:

protected override void UnloadContent()
{
    gSensor.Stop();
}

因为我们开发的游戏程序允许用户以倾斜的方式让用户移动游戏程序显示的对象,所以我们必须在 Game1 类别的 Update 方法中改变物体显示的位置,当游戏程序显示的物体碰撞到游戏窗口的四个边界的时候,停止移动物体的动作。所以我们要将 Game1 类别的 Update 方法编辑成以下的样子,在物体碰撞到游戏窗口的四个边界时,设定对象显示的位置,并将重力加速度的内容值设定为 0:

protected override void Update(GameTime gameTime)
{
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();
    Viewport viewport = graphics.GraphicsDevice.Viewport;       //取得遊戲視窗的視界
    if (LogoPosition.X < 0)                //如果所顯示的圖片已經超出遊戲視窗的左邊界
    {
        LogoPosition.X = 0;           //將所顯示的圖片的位置靠齊在遊戲視窗的左邊界
        LogoVelocity.X = 0;                         //設定 X 軸方向的加速度為0
    }
    else if (LogoPosition.X > viewport.Width – 
Logo.Width)                 //如果所顯示的圖片已經超出遊戲視窗的右邊界
    {
        LogoPosition.X = viewport.Width – 
Logo.Width;            //將所顯示的圖片的位置靠齊在遊戲視窗的右邊界
        LogoVelocity.X = 0;                         //設定 X 軸方向的加速度為0
    }
    if (LogoPosition.Y < 0)              //如果所顯示的圖片已經超出遊戲視窗的上邊界
    {
        LogoPosition.Y = 0;            //將所顯示的圖片的位置靠齊在遊戲視窗的上邊界
        LogoVelocity.Y = 0;                         //設定 Y 軸方向的加速度為0
    }
    else if (LogoPosition.Y > viewport.Height – 
Logo.Height)                    //如果所顯示的圖片已經超出遊戲視窗的下邊界
    {
        LogoPosition.Y = viewport.Height – 
Logo.Height;                  //將所顯示的圖片的位置靠齊在遊戲視窗的下邊界
        LogoVelocity.Y = 0;                         //設定 Y 軸方向的加速度為0
    }
    base.Update(gameTime);
}

[提示]

上述的程序代码会在游戏程序显示的物体碰撞到游戏窗口的四个边界的时候将物体停放在窗口的边界,如果要让对象碰撞到游戏窗口的边界时产生反弹的效果,可以将对象加速度的方向设定为负值,再依据模拟的摩擦系数进行递减,详细的做法可以参考[设计支持手势操作的 XNA 游戏]一文的说明,让游戏程序显示的物体能够像实际世界的物体一样呈现自然的反弹效果。

最后我们要编辑 Game1 类别的 Draw 方法,将游戏程序显示的物体显示在游戏程序的窗口中,编辑好的 Draw 方法如下:

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();                                    //宣告繪製的動作開始
    spriteBatch.Draw(Logo, LogoPosition,Color.White);       //繪製事先載入的圖片資源
    spriteBatch.End();                                  //宣告繪製的動作結束
    base.Draw(gameTime);
}

做好之后请执行建置项目的动作,完成使用传感器的游戏程序的制作。

因为我们制作的游戏程序需要使用到 Windows Phone 7 智能型手机上的重力加速器,让游戏程序利用传感器输入的数据来控制游戏程序的进行,所以制作好的程序必须部署到实际的 Windows Phone 7 智能型手机上执行,无法部署到 Windows Phone 7 的仿真器上执行,因为 Windows Phone 7 仿真器并未提供传感器的功能,就算 Windows Phone 7 仿真器提供重力加速器传感器,用户也很难摇动或是倾斜计算机的屏幕以产生重力加速的效果,以试验游戏程序设计读取传感器输入的数据的功能是否正确。有关如何将开发好的游戏程序部署到实际的 Windows Phone 7 智能型手机的做法可以参考 [设计支持手势操作的 XNA 游戏] 一文的说明。

请将开发好的游戏程序部署到实际的 Windows Phone 7 智能型手机执行,您将会看到如图3 的执行画面:

图3:使用重力加速器功能的游戏程序执行的情形

您可以将智能型手机往任意的方向倾斜,游戏程序显示的物体就会往屏幕倾斜的方向移动,这是因为智能型手机往任意的方向倾斜之后产生的重力加速度会被用来当做移动游戏程序移动物体的速度的关系。

[提示]

有些游戏程序会在用户将智能型手机的屏幕转向之后,切换屏幕显示的内容,例如将原本垂直显示的窗口内容改变成水平显示的内容,或是将原本水平显示的窗口内容改变成垂直显示的内容,这种功能并不需要利用 Accelerometer 类别提供的功能才能够达成。要在使用者旋转智能型手机方向的时候自动旋转游戏程序窗口的显示方向,以 XNA 为基础的游戏程序只要设定 GraphicsDeviceManager 类别的对象的 SupportedOrientations 属性的内容值,就可以在使用者旋转智能型手机的方向的时候自动旋转游戏程序窗口的显示方向。

GraphicsDeviceManager 类别的对象的 SupportedOrientations 属性的型态为 DisplayOrientation 结构,其合法的内容值请参考表5 的说明:

表5:DisplayOrientation 结构合法的内容值

合法的内容值 说明
Default 默认的窗口显示方向。
LandscapeLeft 将窗口的显示方向逆时钟旋转 90 度,变成水平显示的方向,此时屏幕的宽度将会大于窗口的高度。
LandscapeRight 将窗口的显示方向顺时钟旋转 90 度,变成水平显示的方向,此时窗口的宽度将会大于窗口的高度。
Portrait 以垂直的方向显示窗口的内容,此时窗口的高度将会大于窗口的宽度。

您只要在 Game1 类别的建构函式中将 GraphicsDeviceManager 类别的对象的 SupportedOrientations 属性的内容值设定成适当的内容值,就可以在使用者旋转智能型手机的方向的时候自动旋转游戏程序窗口的显示方向,做法如下:

public Game1()
{
    graphics = new GraphicsDeviceManager(this);
    Content.RootDirectory = "Content";
    graphics.SupportedOrientations = 
DisplayOrientation.Portrait |
                DisplayOrientation.LandscapeLeft |
DisplayOrientation.LandscapeRight;      //表示視窗能夠自動切換成垂直顯//示,逆時鐘旋轉 90 度顯示,以及順//時鐘旋轉 90 度顯示
    graphics.PreferredBackBufferWidth = 480;
    graphics.PreferredBackBufferHeight = 800;
    TargetElapsedTime = TimeSpan.FromTicks(333333);
}

做好之后请将应用程序部署到实际的 Windows Phone 7 智能型手机执行,然后旋转执行程序的 Windows Phone 7 智能型手机屏幕的方向,您就会发现应用程序也会自动调整窗口显示的方向,使其和屏幕的方向一致,如图4 所示:

图4:可以依据屏幕的方向自动调整窗口显示方向的游戏程序

范例下载:AccelerometerEnabledGame.zip

audioEngine = new AudioEngine("Content\\MyAudio.xgs");     //建立 AudioEngine 類別的物件

waveBank = new WaveBank(audioEngine, 

"Content\\Wave Bank.xwb");                    //建立 Wave Bank 類別的物件

soundBank = new SoundBank(audioEngine, 

"Content\\Sound Bank.xsb");                     //建立 Sound Bank 類別的物件

请注意建立 AudioEngine 类别对象时指定的是 XACT3 的项目名称,其扩展名为 .xgs,建立 WaveBank 类别的对象时指定的是 Wave Bank 的名称,其扩展名为 .xwb,而建立 SoundBank 类别的对象时指定的是 Sound Bank 的名称,其扩展名为 .xsb。

建立好必要的对象之后,请于 Game1 类别的 Update 方法加入以下的程序代码,准备欲播放的音效内容,并呼叫 AudioEngine 类别对象的 PlayCue 方法执行播放音效的动作:

audioEngine.Update();

soundBank.PlayCue("RingOut");

请注意呼叫 AudioEngine 类别对象的 PlayCue 方法所传入的是 Cue Name,意即要播放 Cue Name 所代表的声音文件。做好之后只要执行程序就可以听到程序播放 XACT3 工具管理的音效。