applyForce(0,400) - SpriteKit不一致

max*_*hud 16 physics objective-c sprite-kit skphysicsbody

所以我有一个物体有物理体,重力影响它.它也是动态的.

目前,当用户触摸屏幕时,我运行代码:

applyForce(0,400)

物体向上移动大约200,然后由于重力下降.这只会在某些时候发生.其他时候,它导致物体仅在Y方向上移动50个单位.

我找不到一个模式......我把我的项目放在Dropbox上,这样如果有人愿意看它就可以打开它.

https://www.dropbox.com/sh/z0nt79pd0l5psfg/bJTbaS2JpY

编辑:这似乎发生在玩家在撞击后稍微从地面反弹一段时间后.有没有办法让玩家不会反弹?

编辑2:我尝试使用摩擦参数解决这个问题,并且只允许玩家在摩擦力为0时"跳跃"(你会认为这将是玩家空降的所有情况)但是摩擦似乎大于0每时每刻.如果玩家正在触摸一个物体(除了使用y位置),我怎么能检测到?

谢谢

god*_*el9 50

建议的解决方案

如果你正在尝试实现跳转功能,我建议你看一下applyImpulse而不是applyForce.这是两者之间的区别,如Sprite Kit编程指南中所述:

你可以选择施加一种力量或一种冲动:

基于在施加力和处理模拟的下一帧之间经过的模拟时间量,施加力一段时间.因此,要对主体应用连续的力,每次处理新帧时都需要进行适当的方法调用.力量通常用于连续效果.

脉冲使身体的速度瞬间变化,这与已经过的模拟时间量无关.脉冲通常用于立即改变身体的速度.

跳跃实际上是身体速度的瞬间变化,这意味着你应该施加冲动而不是力.要使用该applyImpulse:方法,请计算出所需的瞬时瞬时变化,乘以身体质量,并将其作为impulse参数输入函数.我想你会看到更好的结果.

意外行为的解释

如果你在函数applyForce:之外调用update:,那么正在发生的是你的力量乘以你施加力量和处理模拟的下一帧之间经过的时间量.这个乘数不是常数,因此每次applyForce:以这种方式调用时,您都会看到速度的不同变化.


小智 19

@ godel9有一个很好的建议解决方案,尽管在我自己的测试中,对意外行为的解释是不正确的.

来自SKPhysicsBody类参考:

该力用于单个模拟步骤(一帧).

返回参考SKScene Class Reference关于-update方法部分:

...每帧只调用一次,只要场景出现在视图中并且没有暂停.

所以我们可以假设在SKScene的-update方法中调用-applyForce:不应该导致问题.但是如所观察到的那样,尽管施加的向上力远大于重力(400牛顿对9.81),但力不会超过重力.

我创建了一个测试项目,它将创建两个节点,一个自然落下,将affectedByGravity设置为TRUE,另一个调用具有相同预期重力矢量的-applyForce(x方向为0牛顿,y方向为-9.81).然后,我计算了一个时间步长中每个节点的速度差异,以及时间步长.从那以后,我记录了加速度(速度变化/时间变化).

这是我的SKScene子类的片段:

- (id)initWithSize:(CGSize)size

{

if (self = [super initWithSize:size])

{

    self.backgroundColor = [UIColor purpleColor];



    SKShapeNode *node = [[SKShapeNode alloc] init];

    node.path = CGPathCreateWithEllipseInRect(CGRectMake(0, 0, 10, 10), nil);

    node.name = @"n";

    node.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:5];

    node.position = CGPointMake(0, 450);

    node.physicsBody.linearDamping = 0;

    node.physicsBody.affectedByGravity = NO;

    [self addChild:node];



    node = [[SKShapeNode alloc] init];

    node.path = CGPathCreateWithEllipseInRect(CGRectMake(0, 0, 10, 10), nil);

    node.name = @"n2";

    node.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:5];

    node.position = CGPointMake(20, 450);

    node.physicsBody.linearDamping = 0;

    [self addChild:node];


}

return self;

}



- (void)update:(NSTimeInterval)currentTime

{

SKNode *node = [self childNodeWithName:@"n"];

SKNode *node2 = [self childNodeWithName:@"n2"];

CGFloat acc1 = (node.physicsBody.velocity.dy - self.previousVelocity) / (currentTime - self.previousTime);

CGFloat acc2 = (node2.physicsBody.velocity.dy - self.previousVelocity2) / (currentTime - self.previousTime);

[node2.physicsBody applyForce:CGVectorMake(0, node.physicsBody.mass * -150 * self.physicsWorld.gravity.dy)];

NSLog(@"x:%f, y:%f, acc1:%f, acc2:%f", node.position.x, node.position.y, acc1, acc2);

self.previousVelocity = node.physicsBody.velocity.dy;

self.previousTime = currentTime;

self.previousVelocity2 = node2.physicsBody.velocity.dy;

}
Run Code Online (Sandbox Code Playgroud)

结果很不寻常.与手动应用力的节点相比,模拟中受重力影响的节点的加速度始终乘以系数150.我尝试使用不同大小和密度的节点,但存在相同的标量乘数.

由此我必须推断,SpriteKit内部具有默认的"像素到米"比率.也就是说每个"米"恰好等于150个像素.这有时是有用的,否则场景通常太大,意味着力量反应缓慢(想想从地面观看飞机,它行进得非常快,但看起来移动非常缓慢).Sprite Kit文档经常表明不建议进行精确的物理计算(具体见"捏造数字"一节),但这种不一致需要我花费很长时间才能确定.希望这可以帮助!