将当前设备位置识别为平坦

时间:2014-02-24 05:24:38

标签: ios ios7 sprite-kit core-motion uiaccelerometer

所以我有这个应用程序我正在努力你可以通过倾斜设备(加速度计)在屏幕上滚动球。我怎样才能改变下面的代码,这样我就不必将手机保持平衡,并将其作为我的中立平衡点。我想要的是,无论你在应用程序加载时对设备的任何倾斜,这都是神经平衡点。从当前角度来看,您握住设备即中性点。中立平衡点意味着球几乎仍然存在的点。希望这清楚我想要什么。该应用程序也只是landscapeRight。

note 下面的代码可以100%正常工作,就像它需要它来为我的应用程序工作一样。我需要保持手机平放以滚动球...

CGRect screenRect;
CGFloat screenHeight;
CGFloat screenWidth;
double currentMaxAccelX;
double currentMaxAccelY;
@property (strong, nonatomic) CMMotionManager *motionManager;

-(id)initWithSize:(CGSize)size {

     //init several sizes used in all scene
        screenRect = [[UIScreen mainScreen] bounds];
        screenHeight = screenRect.size.height;
        screenWidth = screenRect.size.width;

    if (self = [super initWithSize:size]) {

        self.motionManager = [[CMMotionManager alloc] init];
        self.motionManager.accelerometerUpdateInterval = .2;

       [self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue]
                                                 withHandler:^(CMAccelerometerData  *accelerometerData, NSError *error) {
                                                    [self outputAccelertionData:accelerometerData.acceleration];
                                                     if(error)
                                                     {
                                                         NSLog(@"%@", error);
                                                     }
                                                 }];
    }

    return self;

}

-(void)outputAccelertionData:(CMAcceleration)acceleration{

    currentMaxAccelX = 0;
    currentMaxAccelY = 0;

    if(fabs(acceleration.x) > fabs(currentMaxAccelX))
    {
        currentMaxAccelY = acceleration.x;
    }
    if(fabs(acceleration.y) > fabs(currentMaxAccelY))
    {
        currentMaxAccelX = acceleration.y;
    }
}

-(void)update:(CFTimeInterval)currentTime {

    /* Called before each frame is rendered */

    //set min and max bounderies
    float maxY = screenHeight - (self.ball.size.width/2);
    float minY = 0 + (self.ball.size.width/2);

    float maxX = screenWidth - (self.ball.size.height/2);
    float minX = 0 + (self.ball.size.height/2);

    float newY = 0;
    float newX = 0;
    //left and right tilt
    if(currentMaxAccelX > 0.05){
        newX = currentMaxAccelX * -10;
    }
    else if(currentMaxAccelX < -0.05){
        newX = currentMaxAccelX*-10;
    }
    else{
        newX = currentMaxAccelX*-10;
    }
    //up and down tilt
    newY = currentMaxAccelY *10;

    newX = MIN(MAX(newX+self.ball.position.x,minY),maxY);
    newY = MIN(MAX(newY+self.ball.position.y,minX),maxX);

    self.ball.position = CGPointMake(newX, newY);

}

3 个答案:

答案 0 :(得分:5)

首先,Larme的评论给出了确定起点的正确答案。

但是,如果您要确定设备倾斜度(姿态),则需要使用陀螺仪,而不是加速度计。加速度计可以显示设备在每个方向上的移动速度。这对于确定用户是否正在快速移动或摇动设备非常有用,但却无法帮助您确定设备是否正在倾斜。陀螺仪提供了设备的当前姿态和旋转速度。

由于听起来你正试图在用户倾斜设备时实施一个会在桌子周围“滚动”的球,你可能想要获得态度。要获得态度,请使用startDeviceMotionUpdatesToQueue:withHandler:。然后,您可以使用CMDeviceMotion对象的attitude属性来了解设备在每个轴上的定向方式。

答案 1 :(得分:4)

如上所述,我们需要捕获初始设备位置(加速度计值)并将其用作零参考。我们在游戏开始时捕获参考值,并从每个下一个加速度计更新中减去该值。

static const double kSensivity = 1000;

@interface ViewController ()
{
    CMMotionManager *_motionManager;
    double _vx, _vy;                         // ball velocity
    CMAcceleration _referenceAcc;            // zero reference
    NSTimeInterval _lastUpdateTimeInterval;  // see update: method
}

最初,球是不动的(速度= 0)。零参考无效。我在CMAcceleration中设置了重要值,将其标记为无效:

_referenceAcc.x = DBL_MAX;

加速度计更新。由于应用程序仅使用横向右模式,我们将y加速度映射到x速度,将x加速度映射到y速度。需要accelerometerUpdateInterval因子来使速度值与更新速率无关。我们对x加速度使用负灵敏度值,因为加速度计X轴的方向与横向右方向相反。

-(id)initWithSize:(CGSize)size {
    if (self = [super initWithSize:size]) {
        _vx = 0;
        _vy = 0;
        _referenceAcc.x = DBL_MAX;

        _motionManager = [CMMotionManager new];
        _motionManager.accelerometerUpdateInterval = 0.1;

        [_motionManager
         startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue]
         withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
             CMAcceleration acc = accelerometerData.acceleration;

             if (_referenceAcc.x == DBL_MAX) {
                 _referenceAcc = acc;
                 _referenceAcc.x *= -1;
                 _referenceAcc.y *= -1;
             }

             _vy += kSensivity * (acc.x+_referenceAcc.x) * _motionManager.accelerometerUpdateInterval;
             _vx += -kSensivity * (acc.y+_referenceAcc.y) * _motionManager.accelerometerUpdateInterval;
         }];

        self.ball = [SKSpriteNode spriteNodeWithImageNamed:@"ball"];
        self.ball.position = CGPointMake(self.size.width/2, self.size.height/2);
        [self addChild:self.ball];
    }
    return self;
}

您的update:方法不尊重currentTime值。更新呼叫之间的间隔可以不同。最好根据时间间隔更新距离。

- (void)update:(NSTimeInterval)currentTime {
    CFTimeInterval timeSinceLast = currentTime - _lastUpdateTimeInterval;
    _lastUpdateTimeInterval = currentTime;

    CGSize parentSize = self.size;
    CGSize size = self.ball.frame.size;
    CGPoint pos = self.ball.position;

    pos.x += _vx * timeSinceLast;
    pos.y += _vy * timeSinceLast;

    // check bounds, reset velocity if collided
    if (pos.x < size.width/2) {
        pos.x = size.width/2;
        _vx = 0;
    }
    else if (pos.x > parentSize.width-size.width/2) {
        pos.x = parentSize.width-size.width/2;
        _vx = 0;
    }

    if (pos.y < size.height/2) {
        pos.y = size.height/2;
        _vy = 0;
    }
    else if (pos.y > parentSize.height-size.height/2) {
        pos.y = parentSize.height-size.height/2;
        _vy = 0;
    }

    self.ball.position = pos;
}

编辑:替代方式

顺便说一下,我找到了另一种解决方法。如果使用SpriteKit,则可以根据加速度计的变化配置物理世界的重力。在这种情况下,不需要用update:方法移动球。

我们需要将物理主体添加到球精灵中并使其动态化:

self.physicsWorld.gravity = CGVectorMake(0, 0);  // initial gravity
self.ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.ball.size.width/2];
self.ball.physicsBody.dynamic = YES;
[self addChild:self.ball];

并在加速度计处理程序中设置更新的重力:

// set zero reference acceleration 
...

_vy = kSensivity * (acc.x+_referenceAcc.x) * _motionManager.accelerometerUpdateInterval;
_vx = -kSensivity * (acc.y+_referenceAcc.y) * _motionManager.accelerometerUpdateInterval;

self.physicsWorld.gravity = CGVectorMake(_vx, _vy);

我们还需要设置屏幕的物理边界以限制球的移动。

答案 2 :(得分:0)

为什么不在启动时将数字作为基线并将它们保存为类属性。您拥有的任何其他读数可以简单地使用您的基线添加/减去当前数字。除非我错了,否则应该会给你想要的结果。