Cocos2d,在直立时使用加速度计在横向 bug 中使用 x 轴移动精灵

发布于 2024-12-10 13:55:57 字数 3788 浏览 0 评论 0原文

长期读者第一次海报等等。

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

当 iPod 平放在桌子上或握在手中并在 x 轴上稍微向我倾斜时,这种效果很好,当校准时,精灵将以相同的速度在屏幕上上下移动。轴。

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

查看 xi 的加速计值,发现当 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;
}

Constants.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

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

问候, 詹姆斯.

long time reader first time poster and so on.

My problem is this, Currently i am moving a sprite around the screen of an iPod touch (landscape) using the accelerometer x and y values and cocos2d. The user can calibrate where the accelerometer zero point is by touching the screen, this just grabs the current x and y values ready to subtract from the accelerometer while the app is running. This allows the user to select a comfortable position to hold the iPod.

This works fine when the iPod is sat flat on its back on the desk or held in the hand and tilted slightly towards me on the x axis when calibrated, the sprite will move at the same speed up and down the screen when tilted on the x axis.

However the further towards me the screen is tilted when calibrated something odd happens (well something i didn't expect anyway). If i the screen tilt on the x axis away from me the sprite moves up the screen at the usual speed or faster, if i tilt the screen towards me on the x axis the sprite moves down the screen much slower or sometimes not at all.

Looking at the accelerometer values for x i realised that when the iPod it tipped all the way towards me it is close to 0 with tilting away or towards me counting less than or more than 0. I have a feeling it could be because of this but am unsure of how to progress, Has anyone run into this problem before and managed to find a solution?

Here is the code i have so far.

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;
}

Constants.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

I am thinking perhaps that i am attacking the x y values the wrong way and the maths is just all wrong. I want the user to be able to use the application from any position. Any help or pointer in the right direction will be greatly appreciated. If you need to know anything else please ask. I hope i managed to make myself clear.

Regards,
James.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

独木成林 2024-12-17 13:55:57

最佳答案考虑所有三个轴,假设您希望它相对于用户“但是”保持在校准点处。坚持以重力为导向的 x 和 y 意味着您的值对于任何其他方向都会有一定程度的偏差。

来自亚历克斯·奥卡福 (Alex Okafor) 在 雨游行 上的发言:

- (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](又名)。校准)”。

The best answer takes into account all three axes, presuming you want it with respect to "however" the user is holding at a calibration point. Sticking to a gravity-oriented x and y means your values will be skewed to some extent for any other orientation.

From Alex Okafor on 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);

}

"Hard coding our 'neutral' orientation [in ay]. By default [the "neutral" position] is the orientation of having the device tilted slightly towards your face. If you wanted strictly a 'top down' orientation then a (0,0,-1) vector would be put here. To create a new 'neutral' position you can sample the UIAcceleration parameter for a single frame and set that to be the new [ay] for the remainder of the app (aka calibrating)".

老街孤人 2024-12-17 13:55:57

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

看看简单的 iPhone 运动检测以及指向文档的链接以获取更多一般信息从 Core Motion API 开始。

If you can afford to rely on newer devices that contain a gyroscope and have iOS 4 installed, I would recommend using Core Motion interface instead of UIAccelerometer because it will be deprecated. It is far more accurate and there is a build-in calibration in CMAttitude multiplyByInverseOfAttitude:

Look at Simple iPhone motion detect and the links to the documentation for more general information to get started with Core Motion API.

浊酒尽余欢 2024-12-17 13:55:57

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

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

There's an unfortunate oversimplified advice going around that all it takes to calibrate the accelerometer is to subtract an offset position. This is of course wrong because the value range of the accelerometer axes ranges from -1 to 1.

If your calibration point along an axis is 0.5, you get a range of 1.5 that has your device oriented along the negative axis, and only 0.5 along the positive axis. This inevitably makes your device more sensitive to motion towards the positive axis, while limiting its maximum speed.

You can solve this by setting a calibration range, which you'll use to divide the resulting acceleration values after subtracting your calibration point:

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;

I hope this code still works, I copied it from an old project.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文