Windows Phone 7 Accelerometer Bubble Level 详解

    技术2022-05-20  36

                                                        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/

     

     

     


    最新回复(0)