2014-02-24 50 views
10

因此,我有这个应用程序我正在通过倾斜设备(加速度计)在屏幕上滚动球的位置。我如何更改下面的代码,以便我不必将手机保持平坦,并将其作为我的中立平衡点。我想要的是,无论你在应用程序加载时与设备有什么倾斜,这都将是神经平衡点。从目前的角度来看,你拿着中立点的装置。中立平衡点意味着球仍然非常稳定。希望这清楚我想要什么。此外,该应用程序仅适用于landscapeRight。将当前设备位置识别为平坦

下面的代码工作百分之百的好,就像它需要它来为我的app.Just我需要拿着手机平板打滚球工作...

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

} 
+4

如果我理解正确:在applaunch中,注册x/y值,这是您的新“零”。然后使用这个新的零作为偏移量来完成你想要做的事情。 – Larme

+0

@Larme这应该是一个答案,因为它是做到这一点的正确方法 – Krumelur

+0

是的,但我喜欢代码中的答案,与代码中的事情是如何完成的。很难找到在我的搜索中如何使用偏移的任何代码示例。 – 4GetFullOf

回答

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

此外,我们需要设置的物理界限屏幕以限制球的移动。

+0

不起作用你知道我的球对象是SKSpriteNode吗?物业中心和超视图不被该物体识别 – 4GetFullOf

+0

也看不到如何初始化CADDisplayLink? – 4GetFullOf

+1

@Rookie是的,我错过了这是一个SpriteKit框架。答案已更新。 – vokilam

5

首先, Larme的评论给出了确定起点的正确答案。但是,如果您尝试确定设备倾斜度(姿态),则需要使用陀螺仪,而不是加速度计。加速计告诉设备在每个方向上移动的速度。这对确定用户是否快速移动或晃动设备很有用,但完全不能帮助您确定设备是否正在倾斜。陀螺仪提供设备的当前姿态和旋转速度。

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

+0

上面的代码对于我在屏幕上沿着手机倾斜的方向“滚动”球的100%起作用。我已经问过如何使用已经提供的代码作为我已经为我的应用程序工作的一个答案。感谢您的建议,虽然 – 4GetFullOf

2

为什么不把刚刚开始时的数字作为基线并将它们保存为类属性。任何进一步的读数都可以简单地将当前数字与基线相加/相减。除非我错了,否则应该给你想要的结果。