Cocos2d, mover un sprite usando acelerómetro en error de paisaje con eje x cuando se mantiene en position vertical

póster de primera vez del lector de time largo y así sucesivamente.

Mi problema es este: Actualmente estoy moviendo un sprite alnetworkingedor de la pantalla de un iPod touch (paisaje) usando los valores x e y del acelerómetro y cocos2d. El usuario puede calibrar el punto cero del acelerómetro tocando la pantalla, simplemente toma los valores actuales de x e y listos para restar del acelerómetro mientras la aplicación se está ejecutando. Esto permite al usuario seleccionar una position cómoda para sostener el iPod.

Esto funciona bien cuando el iPod se apoya en la espalda en el escritorio o se sostiene en la mano y se inclina ligeramente hacia mí en el eje x cuando está calibrado, el sprite se moverá a la misma velocidad hacia arriba y hacia abajo cuando se inclina en la x eje.

Sin embargo, más hacia mí la pantalla se inclina cuando se calibra algo extraño que sucede (bueno, algo que no esperaba de todos modos). Si la pantalla se inclina en el eje x lejos de mí, el sprite sube la pantalla a la velocidad normal o más rápido, si inclino la pantalla hacia mí en el eje x, el sprite se moverá por la pantalla mucho más lento o, a veces, no lo hará en absoluto.

Mirando los valores del acelerómetro para xi, me di count de que cuando el iPod se volcaba todo el path hacia mí, se acercaba a 0 con la inclinación o hacia mí, contando less o más de 0. Tengo la sensación de que podría ser debido a esto pero a la mañana inseguro de cómo progresar, ¿Alguien se ha topado con este problema antes y ha logrado encontrar una solución?

Aquí está el código que tengo hasta ahora.

TestLayer.H

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

TestLayer.m

 - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { // Set up variables CGSize winSize = [CCDirector shanetworkingDirector].winSize; UIAccelerationValue rollingX, rollingY, rollingZ; float accelX, accelY, accelZ; invertedControlls = NO; // High pass filter for networkingucing 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 shanetworkingDirector]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:networkingShip ForTime:deltaTime]; } - (id) init { if (([super init])) { CCLOG(@"TestSceneLayer --> Layer init"); CGSize screenSize = [[CCDirector shanetworkingDirector]winSize]; [self scheduleUpdate]; self.isTouchEnabled = YES; self.isAccelerometerEnabled = YES; // Grab the default Accelerometer values xCallib = [GameManager shanetworkingGameManager].xCallib; yCallib = [GameManager shanetworkingGameManager].yCallib; // Setup and add children networkingShip = [CCSprite spriteWithFile:@"networking_ship.png"]; [networkingShip setPosition:ccp(50.0f, screenSize.height / 2)]; [networkingShip setScaleX:screenSize.width/1024.0f]; [networkingShip setScaleY:screenSize.height/768.0f]; [self addChild:networkingShip]; } return self; } 

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

Estoy pensando quizás que estoy atacando los valores xy de la manera incorrecta y que las matemáticas están mal. Quiero que el usuario pueda usar la aplicación desde cualquier position. Cualquier ayuda o puntero en la dirección correcta será muy apreciada. Si necesita saber algo más, pregunte. Espero haber logrado aclararme.

Saludos, James.

La mejor respuesta tiene en count los tres ejes, suponiendo que lo desee con respecto a "sin embargo" el usuario se mantiene en un punto de calibración. Apegarse a una gravedad orientada xyy significa que sus valores serán sesgados hasta cierto punto para cualquier otra orientación.

De Alex Okafor en 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); } 

"Codificación rígida de nuestra orientación" neutral "[en ay]. Por defecto [la position" neutral "] es la orientación de tener el dispositivo inclinado ligeramente hacia su cara. Si deseaba estrictamente una orientación de 'arriba hacia abajo', entonces a (0, 0, -1). Para crear una nueva position 'neutral' puede probar el parámetro UIAcceleration para un solo cuadro y establecer que sea el nuevo [ay] para el rest de la aplicación (también conocido como calibración) " .

Si puede permitirse confiar en dispositivos más nuevos que contienen un giroscopio y tienen instalado iOS 4, recomendaría utilizar la interfaz de Core Motion en lugar de UIAccelerometer porque estará en desuso. Es mucho más preciso y hay una calibración incorporada en CMAttitude multiplyByInverseOfAttitude:

Observe la detección de movimiento de iPhone simple y los enlaces a la documentation para get información general para comenzar con la API de Core Motion.

Existe un consejo desafortunado y simplificado que dice que todo lo que se necesita para calibrar el acelerómetro es restar una position de compensación. Por supuesto, esto es incorrecto porque el range de valores de los ejes del acelerómetro varía de -1 a 1.

Si su punto de calibración a lo largo de un eje es 0.5, obtendrá un range de 1.5 que tiene su dispositivo orientado a lo largo del eje negativo y solo 0.5 a lo largo del eje positivo. Esto inevitablemente hace que su dispositivo sea más sensible al movimiento hacia el eje positivo, al time que limita su velocidad máxima.

Puede resolver esto estableciendo un range de calibración, que utilizará para dividir los valores de aceleración resultantes después de restar su punto de calibración:

 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; 

Espero que este código siga funcionando, lo copié de un antiguo proyecto.