Cocos2d, перемещение спрайта с помощью акселерометра в альбомной ошибке с осью х, когда удерживается в вертикальном положении - PullRequest
3 голосов
/ 19 октября 2011

долгое время читатель первый раз плакат и т. Д.

Моя проблема заключается в следующем: в настоящее время я перемещаю спрайт по экрану iPod touch (альбомная ориентация), используя значения акселерометра x и y и cocos2d. Пользователь может откалибровать, где находится нулевая точка акселерометра, прикоснувшись к экрану, он просто получает текущие значения 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

Возможно, я думаю, что нападаю на значения x y неправильно, а математика просто неверна. Я хочу, чтобы пользователь мог использовать приложение из любой позиции. Любая помощь или указатель в правильном направлении будет принята с благодарностью. Если вам нужно узнать что-то еще, пожалуйста, спросите. Надеюсь, мне удалось прояснить ситуацию.

С уважением, Джеймс.

Ответы [ 3 ]

3 голосов
/ 12 февраля 2013

Лучший ответ учитывает все три оси, при условии, что вы хотите получить его относительно «однако», который пользователь удерживает в точке калибровки.Придерживаясь гравитационно-ориентированных x и y означает, что ваши значения будут в некоторой степени искажены для любой другой ориентации.

От Алекса Окафор на Парад дождя :

- (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);

}

«Жесткое кодирование нашей« нейтральной »ориентации [в yy]. По умолчанию [« нейтральная »позиция] - это ориентация устройства, слегка наклоненного к вашему лицу. Если вы хотели строго ориентировать« сверху вниз », тогда (0,0, -1) вектор будет помещен сюда. Чтобы создать новую «нейтральную» позицию, вы можете выбрать параметр UIAcceleration для одного кадра и установить его как новый [ay] для оставшейся части приложения (так называемая калибровка).)».

2 голосов
/ 20 октября 2011

Если вы можете позволить себе полагаться на новые устройства с гироскопом и с установленной iOS 4, я бы порекомендовал использовать интерфейс Core Motion вместо UIAccelerometer, поскольку он устарел. Это гораздо точнее, и есть встроенная калибровка в CMAttitude multiplyByInverseOfAttitude:

Посмотрите на Простое обнаружение движения iPhone и ссылки на документацию для получения более общей информации, чтобы начать работу с Core Motion API.

0 голосов
/ 20 октября 2011

Существует досадный упрощенный совет: все, что нужно для калибровки акселерометра, это вычесть положение смещения. Это, конечно, неправильно, потому что диапазон значений осей акселерометра составляет от -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;

Надеюсь, этот код все еще работает, я скопировал его из старого проекта.

...