所以我有这个应用程序我正在努力你可以通过倾斜设备(加速度计)在屏幕上滚动球。我怎样才能改变下面的代码,这样我就不必将手机保持平衡,并将其作为我的中立平衡点。我想要的是,无论你在应用程序加载时对设备的任何倾斜,这都是神经平衡点。从当前角度来看,您握住设备即中性点。中立平衡点意味着球几乎仍然存在的点。希望这清楚我想要什么。该应用程序也只是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);
}
答案 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)
为什么不在启动时将数字作为基线并将它们保存为类属性。您拥有的任何其他读数可以简单地使用您的基线添加/减去当前数字。除非我错了,否则应该会给你想要的结果。