Windows Phone 7范例游戏Platformer实战6——加速度传感器解读
Windows Phone 7范例游戏Platformer实战1——5大平台支持
Windows Phone 7范例游戏Platformer实战2——游戏设计初步
Windows Phone 7范例游戏Platformer实战3——游戏资源和内容管道
Windows Phone 7范例游戏Platformer实战4——冲突检测的实现
Windows Phone 7范例游戏Platformer实战5——多点触控编程
加速度传感器在Windows Phone 7的硬件规格中已经成为一种标配,有了加速度传感器,我们就可以根据地球引力来仿真现实生活中的许多操作。比如说我们倾斜手机,就可以实现英雄的向倾斜的方向移动。还有我们可以挥动手机,实现游戏中保龄球的挥出操作等等功能。摇晃手机实现图片或者背景图片的变换,一切只限于你的想象力。因此掌握加速度传感器的编程也是XNA游戏开发的必经之路。
WP7上的加速度传感器输出可以用一个三维空间的矢量来描述,这里要注意下,在XNA中存在三维的矢量类型,而Silverlight是不存的。本文除了将给出Platformer的加速度感应器代码外,还将深入WP7的加速度感应器的概念,让你在以后的项目开发中可以快速切入。
当一个三维的点坐标(x,y,z)表示空间一个特定的位置时,矢量(x,y,z)代表的意义则更加丰富,它包含了方向和长度的概念。很明显点坐标和矢量是有关联的。矢量(x,y,z)的方向就是点(0,0,0)到点(x,y,z)的方向。但是矢量(x,y,z)并不是由点(0,0,0)到点(x,y,z)构成的那条直线,而只是代表这条直线的方向。
矢量(x,y,z)的长度我们可以使用勾股定理快速获取,下面是计算的数学表达式。
在使用加速度传感器时,我们可以把WP7手机想像成一个三维的坐标系统。无论电话放置的方向的什么,Y坐标轴是电话的底端(包含按钮的那端)到顶端的方向,而且这个走向是是X轴正方向。X坐标轴则是从左至右的走向,这个走向亦是正方向,Z坐标轴正走向则是面对你的方向。
下面是WP7三维矢量坐标的标示图:
这是一个我们在实际生活和数学中都经常使用的经典三维坐标系统,XNA中的3D编程也采用了这种坐标方法。这种坐标系统有一个专业术语,被称作笛卡尔右手坐标系统。
笛卡尔右手坐标系统的意思就是将右手背对着手机屏幕放置,拇指即指向X轴的正方向。伸出食指和中指,食指指向Y轴的正方向,中指所指示的方向即是Z轴的正方向。有点类似面对自己的兰花指造型,大家可以看看示意图。
这种坐标朝向永远是固定的,无论你是将手机是横拿还是竖放,又或者游戏是在Landscape和Portrait模式下运行,均如此。你只需要记住前面强调的两点,无论电话放置的方向的什么,Y坐标轴是电话的底端(具备开始按钮的那端)到顶端的走向,而且这个方向是是X轴正方向。X坐标轴则是从左至右的走向,这个走向亦是正方向。再使用右手定理,就可以很快地确定Z的坐标轴走向了。
正如你想象的那样,Windows Phone 7应用程序的在屏幕选择时会自动切换正是由加速度传感器实现的。如果你的手机是静止的,加速度传感器的矢量方向永远是指向地心的,如果矢量的长度为1的话,我们称之为1G。当你着正拿着WP7手机时,加速度传感器的矢量为(0,-1,0),方向指向地心。逆时针旋转90度,加速度传感器矢量变为(-1,0,0)。再逆时针旋转90度又变为(0,1,0)。
当你将手机朝上平放在桌面上时,加速度传感器矢量为(0,0,-1)。这些值也就是Windows Phone 7仿真器经常报告的矢量。
正常拿着时,加速度传感器矢量为(0,-1,0)
右Landscape模式时,加速度传感器矢量为(1,0,0)
左Landscape时,加速度传感器矢量为(-1,0,0)
向右倾斜45度时,加速度传感器矢量为(0.7,-0.7,0)。这样使用勾股定理正好得到矢量长度为1
当然,加速度传感器矢量很少能达到这种类似(0,-1.0)这种精确度,其长度不可避免会有些出入。在WP7手机静止时,加速度传感器矢量长度会有一定的误差。当你在访问月球时使用WP7的话,你可能会认为矢量速度为0.17这个大概的区域(月球重力为地球的1/6),但是这个数值主要还是取决于手机的加速度传感器品质。
前面谈论的加速度感应器矢量都是在手机静止状态下发生的,加速度感应器矢量还可以指向其它的一些方向,当手机在快速运动时其矢量长度也会发生或大或小的变化。
比如说,当你猛地将手机向左移动时,如果手机的速度一直处于增大时,加速度传感器矢量将指向右。但当手机的移动速度稳定时,加速度传感器矢量的长度又变为重力大小。当你最后减速时,加速度传感器矢量的方向一直保持左边,直到手机完全停止为止。
还有一种情况就是当手机自由落体时,原理上可认为此时加速度传感器矢量的长度为0。
在程序开发过程中需要和加速度传感器打交道时,你首先要引用Microsoft.Devices.Sensors这个dll文件。此外,WMAppManifest.xml这个文件还需要包含<Capability Name="ID_CAP_SENSORS" />这个描述。一般来说这个描述是在项目创建时就已存在,这里和大家提醒下。
我们知道Platformer游戏中,是使用加速度传感器的来确定英雄的移动方向,以及移动的速度,这和你倾斜手机的角度有着密切的关系。我们需要对加速度传感器返回的矢量值进行处理,以实现仿真现实场景的移动效果。此外,当程序运行在模拟器上时,我们可以使用电脑键盘的方向键来仿真加速度传感器。
2 //-----------------------------------------------------------------------------
3 // Accelerometer.cs
4 //
5 // Microsoft XNA Community Game Platform
6 // Copyright (C) Microsoft Corporation. All rights reserved.
7 //-----------------------------------------------------------------------------
8 #endregion
9
10 #region Using Statements
11 using System;
12 using Microsoft.Xna.Framework;
13 using Microsoft.Xna.Framework.Input;
14 #endregion
15
16 namespace Platformer
17 {
18 /// <summary>
19 /// 加速度传感器封装类
20 /// </summary>
21 public static class Accelerometer
22 {
23 #if WINDOWS_PHONE
24 // 实例化加速度传感器对象
25 private static Microsoft.Devices.Sensors.Accelerometer accelerometer = new Microsoft.Devices.Sensors.Accelerometer();
26
27 //因为加速度传感器的ReadingChanged事件可以由不同的线程触发
28 //为此我们在游戏中要对传感器进行锁定(Lock),以便需要时单独享用。
29 private static object threadLock = new object();
30
31 //用于保存加速度传感器最后反馈的数据
32 private static Vector3 nextValue = new Vector3();
33 #endif
34
35 // 为防止加速度传感器初始化两次引入的变量
36 private static bool isInitialized = false;
37
38 // 加速度传感器是否开启
39 private static bool isActive = false;
40
41 /// <summary>
42 /// 为当前游戏初始化加速度传感器. 这个方法只能在游戏中调用一次
43 /// </summary>
44 public static void Initialize()
45 {
46 // 确保加速度传感器是第一次初始化,如果已经初始化过了,则抛出异常。
47 if (isInitialized)
48 {
49 throw new InvalidOperationException("Initialize can only be called once");
50 }
51
52 #if WINDOWS_PHONE
53 // 如果是实际的WP7设备,则开始加速度传感器的初始化工作。
54 if (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Device)
55 {
56 try
57 {
58 accelerometer.ReadingChanged += new EventHandler<Microsoft.Devices.Sensors.AccelerometerReadingEventArgs>(sensor_ReadingChanged);
59 accelerometer.Start();
60 isActive = true;
61 }
62 catch (Microsoft.Devices.Sensors.AccelerometerFailedException)
63 {
64 isActive = false;
65 }
66 }
67 else
68 {
69 // 因为在模拟器上我们可以使用方向键控制英雄的移动和跳跃
70 // 所以假定加速度传感器的已经处于激活的状态
71 isActive = true;
72 }
73 #endif
74
75 // 保存已被初始化的信息
76 isInitialized = true;
77 }
78
79 #if WINDOWS_PHONE
80 /// <summary>
81 /// 加速度传感器数据发生变化后的矢量值
82 /// </summary>
83 /// <param name="sender"></param>
84 /// <param name="e"></param>
85 private static void sensor_ReadingChanged(object sender, Microsoft.Devices.Sensors.AccelerometerReadingEventArgs e)
86 {
87 //保存加速度传感器的返回值,这样可以在下一次游戏状态更新时使用。
88 lock (threadLock)
89 {
90 nextValue = new Vector3((float)e.X, (float)e.Y, (float)e.Z);
91 }
92 }
93 #endif
94
95 /// <summary>
96 /// 获得当前加速度传感器的状态
97 /// </summary>
98 /// <returns>获得加速度传感器的当前状态.</returns>
99 public static AccelerometerState GetState()
100 {
101 // 在开始获得加速度传感器状态时,我们必须确保加速度传感器已经初始化
102 if (!isInitialized)
103 {
104 throw new InvalidOperationException("You must Initialize before you can call GetState");
105 }
106
107 //为加速度传感器的状态创建一个新矢量对象
108 Vector3 stateValue = new Vector3();
109
110 #if WINDOWS_PHONE
111 // 如果加速度传感器被激活
112 if (isActive)
113 {
114 if (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Device)
115 {
116 // 如果游戏是在WP7手机上运行,读取加速度传感器的矢量值
117 lock (threadLock)
118 {
119 stateValue = nextValue;
120 }
121 }
122 else
123 {
124 //如果是在模拟器上,我们可以使用方向键来仿真加速度传感器的矢量值。
125 //你可以使用pause/break键来切换到键盘操作。
126 KeyboardState keyboardState = Keyboard.GetState();
127
128 stateValue.Z = -1;
129
130 if (keyboardState.IsKeyDown(Keys.Left))
131 stateValue.X--;
132 if (keyboardState.IsKeyDown(Keys.Right))
133 stateValue.X++;
134 if (keyboardState.IsKeyDown(Keys.Up))
135 stateValue.Y++;
136 if (keyboardState.IsKeyDown(Keys.Down))
137 stateValue.Y--;
138
139 stateValue.Normalize();
140 }
141 }
142 #endif
143
144 return new AccelerometerState(stateValue, isActive);
145 }
146 }
147
148 /// <summary>
149 /// 对加速度传感器当前状态的封装
150 /// </summary>
151 public struct AccelerometerState
152 {
153 /// <summary>
154 /// 获得加速度传感器当前的重力
155 /// </summary>
156 public Vector3 Acceleration { get; private set; }
157
158 /// <summary>
159 /// 查看加速度传感器是否激活和运行
160 /// </summary>
161 public bool IsActive { get; private set; }
162
163 /// <summary>
164 // 构造函数
165 /// </summary>
166 /// <param name="acceleration">当前加速度传感器的加速度值</param>
167 /// <param name="isActive">加速度传感器是否激活</param>
168 public AccelerometerState(Vector3 acceleration, bool isActive)
169 : this()
170 {
171 Acceleration = acceleration;
172 IsActive = isActive;
173 }
174
175 /// <summary>
176 /// 返回一个包含加速度值和是否激活的字符串
177 /// </summary>
178 /// <returns>描述加速度传感器状态的字符串.</returns>
179 public override string ToString()
180 {
181 return string.Format("Acceleration: {0}, IsActive: {1}", Acceleration, IsActive);
182 }
183 }
184 }
185
好了,加速度传感器的使用暂时就结束到这里了,轩辕会在后续的章节中介绍如何使用返回的矢量值来控制英雄的移动方向和移动速度。下一节开始讲解游戏的动画类和动画的绘制了,敬请期待。
喜欢本文的读者们请劳驾点击下文章下面的“推荐”,非常感谢哈。周末放弃休息,继续加更,拉下票,希望dudu手下留情。哈哈