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 轴的横向错误中移动精灵的主要内容,如果未能解决你的问题,请参考以下文章