2014-02-24 93 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

爲什麼不把剛剛開始時的數字作爲基線並將它們保存爲類屬性。任何進一步的讀數都可以簡單地將當前數字與基線相加/相減。除非我錯了,否則應該給你想要的結果。