Cocos2d,在竖直放置时使用加速度计在具有 x 轴的横向错误中移动精灵

Posted

技术标签:

【中文标题】Cocos2d,在竖直放置时使用加速度计在具有 x 轴的横向错误中移动精灵【英文标题】:Cocos2d, moving a sprite using accelerometer in landscape bug with x axis when held upright 【发布时间】:2011-10-19 18:40:54 【问题描述】:

长期读者第一次海报等。

我的问题是,目前我正在使用加速度计 x 和 y 值以及 cocos2d 在 iPod touch(横向)屏幕上移动精灵。用户可以通过触摸屏幕来校准加速度计零点的位置,这只是在应用程序运行时获取准备从加速度计中减去的当前 x 和 y 值。这允许用户选择一个舒适的位置来握住 iPod。

当 iPod 平放在桌子上或握在手中并在校准时在 x 轴上略微向我倾斜时,这可以正常工作,当倾斜时精灵将以相同的速度在屏幕上上下移动在 x 轴上。

但是,当校准时,屏幕越靠近我,就会发生奇怪的事情(好吧,无论如何我都没有预料到)。如果我在 x 轴上远离我的屏幕倾斜,精灵以通常的速度或更快地向上移动屏幕,如果我在 x 轴上向我倾斜屏幕,精灵在屏幕上向下移动的速度要慢得多,有时甚至根本不移动。

查看 x 的加速度计值,我意识到当 iPod 一直朝我倾斜时,它接近于 0,倾斜或朝我倾斜时计数小于或大于 0。我有一种感觉可能是因为这个但不确定如何进行,有没有人遇到过这个问题并设法找到解决方案?

这是我目前的代码。

TestLayer.H

@interface TestSceneLayer : CCLayer 
CCSprite *redShip;
float movementX, movementY;
float xCallib, yCallib;
BOOL willMoveX, willMoveY;  

TestLayer.m

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration 

// Set up variables
CGSize winSize = [CCDirector sharedDirector].winSize;
UIAccelerationValue rollingX, rollingY, rollingZ;
float accelX, accelY, accelZ;
invertedControlls = NO;

// High pass filter for reducing jitter
rollingX = (acceleration.x * kFilteringFactor) + (rollingX * (1.0 - kFilteringFactor));    
rollingY = (-acceleration.y * kFilteringFactor) + (rollingY * (1.0 - kFilteringFactor));    

accelX = acceleration.x - rollingX;
accelY = acceleration.y - -rollingY;

// Calculate movement for x and y axis
float accelDiffX = accelX - kRestAccelX;
float accelFractionX = accelDiffX / kMaxDiff;
movementY = kShipMaxPointsPerSec * accelFractionX;

float accelDiffY = accelY - kRestAccelY;
float accelFractionY = accelDiffY / kMaxDiff;
movementX = kShipMaxPointsPerSec * accelFractionY;

// Thresh holds for x and y axis movement
willMoveX = YES;
willMoveY = YES;

if (((movementX < 70.0f) && (movementX > -70.0f))) willMoveX = NO;
else if (((movementY < 70.0f) && (movementY > -70.0f))) willMoveY = NO; 



- (void) applyAccelerometerToNode:(CCNode*)tempNode ForTime:(ccTime)dTime 
// temp node is the sprite being moved
CGSize screenSize = [[CCDirector sharedDirector]winSize];

float oldX = [tempNode position].x;
float oldY = [tempNode position].y;
float newX, newY;


if (willMoveY)  
    newY = [tempNode position].y + (movementY * dTime);
 else newY = oldY;
if (willMoveX)  
    newX = [tempNode position].x - (movementX * dTime);
 else newX = oldX;


// Constrain movement on screen to stop it shooting off like a little bastard
if ((newY > (screenSize.height - 40.0f)) || newY < 40.0f ) 
    newY = oldY;


if ((newX > (screenSize.width -40)) || newX < 40.0f ) 
    newX = oldX;


[tempNode setPosition:ccp(newX,newY)];


- (void) update:(ccTime)deltaTime      
[self applyAccelerometerToNode:redShip ForTime:deltaTime];


- (id) init 
if (([super init])) 
    CCLOG(@"TestSceneLayer --> Layer init");

    CGSize screenSize = [[CCDirector sharedDirector]winSize];

    [self scheduleUpdate];
    self.isTouchEnabled = YES;
    self.isAccelerometerEnabled = YES;

// Grab the default Accelerometer values
    xCallib = [GameManager sharedGameManager].xCallib;
    yCallib = [GameManager sharedGameManager].yCallib;

// Setup and add children
    redShip = [CCSprite spriteWithFile:@"red_ship.png"];
    [redShip setPosition:ccp(50.0f, screenSize.height / 2)];
    [redShip setScaleX:screenSize.width/1024.0f];
    [redShip setScaleY:screenSize.height/768.0f];
    [self addChild:redShip];

return self;

常量.h

#define kFilteringFactor 0.1
#define kShipMaxPointsPerSec (winSize.height*0.5)        
#define kRestAccelX (xCallib)
#define kMaxDiff 0.2
#define kRestAccelY (yCallib)
#define kMaxDiffY 0.1

我在想也许我以错误的方式攻击 x y 值,而数学完全错了。我希望用户能够从任何位置使用该应用程序。任何正确方向的帮助或指示将不胜感激。如果您需要了解其他任何信息,请询问。我希望我设法让自己清楚。

问候, 詹姆斯。

【问题讨论】:

【参考方案1】:

最佳答案考虑了所有三个轴,假设您希望它与用户在校准点的“然而”保持一致。坚持以重力为导向的 x 和 y 意味着您的值将在一定程度上偏向任何其他方向。

来自 Alex Okafor,Parade of Rain:

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration 
  // A much more concise version courtesy of Mikko Mononen http://digestingduck.blogspot.com/
  Vec2 accel2D(0,0);
  Vec3 ax(1, 0, 0);
  Vec3 ay(-.63f, 0,-.92f); // this is your "neutral" / calibrated position
  Vec3 az(Vec3::Cross(ay,ax).normalize());
  ax = Vec3::Cross(az,ay).normalize();
  accel2D.x = -Vec3::Dot(Vec3(acceleration.x, acceleration.y, acceleration.z), ax);
  accel2D.y = -Vec3::Dot(Vec3(acceleration.x, acceleration.y, acceleration.z), az);


“硬编码我们的“中性”方向 [in ay]。默认情况下,[“中性”位置]是让设备稍微向您的脸倾斜的方向。如果您想要严格的“自上而下”方向,那么(0,0,-1) 向量将放在这里。要创建一个新的“中性”位置,您可以对单个帧的 UIAcceleration 参数进行采样,并将其设置为应用程序其余部分的新 [ay](又名校准)”。

【讨论】:

【参考方案2】:

如果您有能力依赖包含陀螺仪并安装了 ios 4 的较新设备,我建议您使用 Core Motion 界面而不是 UIAccelerometer,因为它将被弃用。它更加准确,并且在CMAttitude multiplyByInverseOfAttitude 中有内置校准:

查看Simple iPhone motion detect 和文档链接,了解更多有关开始使用 Core Motion API 的一般信息。

【讨论】:

【参考方案3】:

不幸的是,有一个过于简单化的建议,即校准加速度计所需的只是减去一个偏移位置。这当然是错误的,因为加速度计轴的取值范围是 -1 到 1。

如果沿轴的校准点为 0.5,则设备沿负轴定向的范围为 1.5,而沿正轴的校准点仅为 0.5。这不可避免地会使您的设备对正轴运动更加敏感,同时限制其最大速度。

您可以通过设置校准范围来解决此问题,您将使用该校准范围在减去校准点后除以得到的加速度值:

calibrationValueRange = CGPointMake(1.0f - fabsf(calibrationPoint_.x), 
                                    1.0f - fabsf(calibrationPoint_.y));
accelerationPoint.x = (accelerationPoint.x - calibrationPoint_.x) /               
                                                   calibrationValueRange.x;
accelerationPoint.y = (accelerationPoint.y - calibrationPoint_.y) / 
                                                   calibrationValueRange.y;

我希望这段代码仍然有效,我从一个旧项目中复制了它。

【讨论】:

您好,谢谢您的回复,我尝试了您的建议,但 x 值上的值仍然看起来错误。在 x 轴上,正值直接上升,而在负值上几乎没有变化。

以上是关于Cocos2d,在竖直放置时使用加速度计在具有 x 轴的横向错误中移动精灵的主要内容,如果未能解决你的问题,请参考以下文章

cocos2d 3.x 如何处理精灵外部的触摸

对MPU6050坐标矩阵修改的学习

cocos2d video视频层放置ui

1739. 放置盒子

使用加速度计时的旋转装置

关于安卓手机三轴加速度传感器的一些问题。