Windows Phone 7 Accelerometer Bubble Level 详解
你可以在这里下载例子:
http://rorger.download.csdn.net/
最近在看Charles Petzold的一书,看到第5章的Accelerometer就卡住了,
因为之前没有接触过这类东西。
protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); Vector3 accVector; lock (accelerometerVectorLock) { accVector = accelerometerVector; } int sign = this.Window.CurrentOrientation == DisplayOrientation.LandscapeLeft ? 1 : -1; bubblePosition = new Vector2(screenCenter.X + sign * screenRadius * accVector.Y, screenCenter.Y + sign * screenRadius * accVector.X); float bubbleRadius = BUBBLE_RADIUS_MIN + (1 - accVector.Z) / 2 * (BUBBLE_RADIUS_MAX - BUBBLE_RADIUS_MIN); bubbleScale = bubbleRadius / (bubbleTexture.Width / 2); base.Update(gameTime); Viewport viewport = this.GraphicsDevice.Viewport; textPosition = new Vector2((viewport.Width - textSize.X) / 2, (viewport.Height - textSize.Y) / 2); }
其实最核心的内容就是这里了吧;
int sign = this.Window.CurrentOrientation ==
DisplayOrientation.LandscapeLeft ? 1 : -1;
bubblePosition = new Vector2(screenCenter.X + sign * screenRadius * accVector.Y,
screenCenter.Y + sign * screenRadius * accVector.X);
float bubbleRadius = BUBBLE_RADIUS_MIN + (1 - accVector.Z) / 2 *
(BUBBLE_RADIUS_MAX - BUBBLE_RADIUS_MIN);
accelemeroter 一下简称 acc
当手机向左旋转时,sign = 1 ;
acc(-1,0,0)
bubblePosition = (screenCenter.X + 0 ,screenCenter.Y + screenRadius * (-1)) ; //横坐标为中间,纵坐标为顶端
bubbleRadius = BUBBLE_RADIUS_MIN + (1 - 0) / 2 *(BUBBLE_RADIUS_MAX - BUBBLE_RADIUS_MIN);
//半径为MIN+1/2 (MAX-MIN);
当手机向右时,sing=-1;
acc(1,0,0)
bubblePosition = new Vector2(screenCenter.X + sign * screenRadius * accVector.Y,
screenCenter.Y + sign * screenRadius * accVector.X)
= (screenCenter.X+0,
screenCenter.Y+(-1)*screenRadius*1)
= (screenCenter.X,screenCenter.Y-screenRadius); //横坐标为中间screenCenter.X,纵坐标为顶端:screenCenter.Y-screenRadius
bubbleRadius=MIN+(1-0)/2 * (MAX-MIN) ;
(当手机向上,由于默认没有开启portraitup方向支持时,手机屏幕坐标是这样的:右上角为原点,x从上到下递增,y从右到左递增,更好的理解是:坐标和LandScapeLeft时一样,x从左到右递增,y从上到下递增;但是奇怪的事情发生了,如果手机转到LandScapeRight位置再转到portraitup时,坐标和LandScapeRight一样)
附上 图:
当手机向上时,sign = ?;
我为什么用?
因为sign的值有两种可能,如果是程序刚刚开始的话或这你由LandScapeLeft转向PortraitUp,而你的手机是portraitup,那么sign的值为1,相当于LandScapeLeft
acc(0,-1,0) //这个还是不变的
bubblePosition = new Vector2(screenCenter.X + sign * screenRadius * accVector.Y,
screenCenter.Y + sign * screenRadius * accVector.X)
= (screenCenter.X + 1 * screenRadius * (-1),
screenCenter.Y + 1 * screenRadius * 0)
= (screenCenter.X-screenRadius,
screenCenter.Y );
bubbleRadius = MIN + 1/2 * (MAX-MIN);
//想象下,如果是LandScapeLeft,那么是左边中间,在Portrait位置看来就是顶端中间
如果你由LandScapeRight转向portraitup
sign=-1; //因为手机内部还是LandScapeRight方向.
acc(0,-1,0) //这个还是不变的
bubblePosition = new Vector2(screenCenter.X + sign * screenRadius * accVector.Y,
screenCenter.Y + sign * screenRadius * accVector.X)
= (screenCenter.X + (-1)*screenRadius*(-1),
screenCenter.Y +0)
= (screenCenter.X+screenRadius,
screenCenter.Y)
bubbleRadius = MIN + 1/2 * (MAX-MIN);
//如果从LandScapeRight位置考虑,是右端中间,在PortraitUp位置看,那么就是顶端中间。
//如果是手机水平放置的话,面朝上
sign=?
///也就是说有可能是1和-1,道理同上
acc=(0,0,-1)
不管是1或者-1,由于x和y都是0;
所以有
bubblePosition = new Vector2(screenCenter.X + sign * screenRadius * accVector.Y,
screenCenter.Y + sign * screenRadius * accVector.X)
= (screenCenter.X + 0, screenCenter.Y + 0);
= (screenCenter.X,screenCenter.Y);
float bubbleRadius = BUBBLE_RADIUS_MIN + (1 - accVector.Z) / 2 *
(BUBBLE_RADIUS_MAX - BUBBLE_RADIUS_MIN);
= MIN + (1-(-1))/2*(MAX-MIN)
=MAX //这是球的半径最大
//如果手机水平方式,面朝下,也许你把它举过头顶是个不错的主意
acc=(0,0,1)
bubblePosition = new Vector2(screenCenter.X + sign * screenRadius * accVector.Y,
screenCenter.Y + sign * screenRadius * accVector.X)
= (screenCenter.X + 0, screenCenter.Y + 0);
= (screenCenter.X,screenCenter.Y);
float bubbleRadius = BUBBLE_RADIUS_MIN + (1 - accVector.Z) / 2 *
(BUBBLE_RADIUS_MAX - BUBBLE_RADIUS_MIN);
= MIN + (1-1))/2*(MAX-MIN)
=MIN //这是球的半径最小
至此,关键的位置都已经具体讨论了;可是我的问题是,Charles Petzold 这个guy为什么这么写,我心好痒痒,好痒痒;
我想说一下的思路,他把球的活动范围限制在了正方形中,也许是为了方便吧,因为如果活动范围是整个屏幕,要去判断大小,而且对于例子展示没有什么益处。
然后主要就是
bubblePosition的计算了:
bubblePosition = new Vector2(screenCenter.X + sign * screenRadius * accVector.Y,
screenCenter.Y + sign * screenRadius * accVector.X)
作者说:在计算X坐标的时候,使用到了accVector.Y,这看似弄混了,其实这是由于默认的屏幕方向在XNA中是portrait,这和acceleration vector是相反的。
注意:好好理解这句话吧:拿起手机(没有的也要想象),portraitup,然后伸出右手,做出右手坐标的手势。
手机和手一同逆时针旋转90度,看看,看看,看看吧;对于手机屏幕,x坐标是从左到右递增,而你的中指方向(代表向量y分量)和x坐标是相反的;手机屏幕y坐标是从上到下递增的,而你的食指方向(代表x分量)和y坐标是相反的。
自此,问题基本得到解决;
其实我还有这么一个最后的疑问,就是其它方向呢?作者到底是怎么想到用这种方式的呢?
要计算position,必须有参考点(screenCenter.X,screenCenter.Y)
然后必须计算移动的分量,该怎么计算移动分量呢?
比如x坐标方向,从左到右的变化范围是(screenCenter.X-screenRadius,screenCenter.X + screenRadius);
而accVector.Y的变化范围基本是 [-1,1] (不过说实话,还可以大于1,小于-1)
刚刚好可以这样:
screenCenter.X+accvector.Y*screenRadius
同理对于y坐标方向,变化范围是(screenCenter.Y-screenRadius,screenCenter.Y+screenRadius)
可以这样表示: screenCenter.Y+accVector.X*screenRadius
但是这够了吗?
对于LandScapeLeft情况,计算出来的坐标是(screenCenter.X,screenCenter.Y-screenRadius)满足;
对于LandScapeRight情况,计算出来的坐标是(screenCenter.X,screenCenter.Y+screenRadius),不满足;
其实可以知道,LandScapeLeft和LandScapeRight时,x坐标是符号相反,绝对值相等;
要想两种方式有相同的显示效果,那么其中一个变号就可以了;显然是LandScapeRight时变号。
自此,搞定sign
原来我的不厌其烦,我对bubbleRadius的大小还是有疑问,作者怎么就知道这么写呢?
float bubbleRadius = BUBBLE_RADIUS_MIN + (1 - accVector.Z) / 2 *
(BUBBLE_RADIUS_MAX - BUBBLE_RADIUS_MIN);
这一行看似简单的代码对我这种粗人还是有些好理解,但是不知道为什么????
思路:远的时候,最小;近的时候,最大。当水平朝上,最大,水平朝下,最小。近者大,而远者小。
MIN MAX
中间的时候呢? MIN+1/2(MAX-MIN) ;
其它情况呢?
变化范围:[MIN,MAX]
由于 accVector.Z的变化范围是[-1,1];
但是我们想用的是-accVector.Z ,因为水平朝上时,z为-1,而球最大,故而用其反。
那么就是 MIN+1/2(MAX-MIN) - accVector.Z*1/2*(MAX-MIN)
= MIN + (1-accVector.Z)*1/2*(MAX-MIN)
= MIN + (1-accVector.Z)/2*(MAX-MIN) //窃笑,不知道有没有更简单的理解
这个代码挺疯狂的吧,或者是我太笨了,不过终究懂了。
先找范围,再找关系,最后化简;
酝酿了一周了:
XNA屏幕坐标 ,y从上到下,x从左到右递增;
当PortraitUP时,accelerometer坐标是右手坐标,手机旋转,坐标系也旋转。
后记:我想要全屏幕,以及我想把球的运动范围扩展到全屏幕,而不是正方形块中:
在设置IsFullScreen时要注意:不要在构造函数中设置,在Initialize函数中设置,否则会出现 accelrometer读取错误问题,在真机上尝试验证。
protected override void Initialize() { // TODO: Add your initialization logic here Accelerometer acc = new Accelerometer(); acc.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(OnAccelerometerReadingChanged); try { acc.Start(); ; } catch (System.Exception ex) { } this.graphics.IsFullScreen = true; base.Initialize(); }
为windows增加 Vector2 screenRadius;
在LoadContent()函数中:增加
screenRadius.X = screenCenter.X - BUBBLE_RADIUS_MAX;
screenRadius.Y = screenCenter.Y - BUBBLE_RADIUS_MAX;
在Update函数中:
bubblePosition.X = (screenCenter.X + screenRadius.X * acclerometer.Y * sign);
bubblePosition.Y = (screenCenter.Y + screenRadius.Y * acclerometer.X * sign);
顺带更改图片:
你可以试试这个图片,蛮可爱的:
这要求你把背景的刷新颜色设置为黑色:
在draw()的 clear函数中:
GraphicsDevice.Clear(Color.Black);
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Black); // TODO: Add your drawing code here spriteBatch.Begin(); spriteBatch.Draw(bubbleTexture, bubblePosition, null, Color.White, 0, bubbleCenter, scale, SpriteEffects.None, 0); spriteBatch.End(); base.Draw(gameTime); }
你可以在这里下载例子:
http://rorger.download.csdn.net/