ApplyForce (0, 400) - несоответствие SpriteKit
Итак, у меня есть объект, который имеет физическое тело, и гравитация влияет на него. Он также динамичен.
В настоящее время, когда пользователи касаются экрана, я запускаю код:
applyForce (0, 400)
Объект перемещается примерно на 200, а затем падает вниз из-за силы тяжести. Это происходит только некоторое время. В других случаях это приводит к тому, что объект перемещает только 50 единиц в направлении Y.
Я не могу найти шаблон... Я поместил свой проект в dropbox, чтобы его можно было открыть, если кто-то захочет посмотреть на него.
https://www.dropbox.com/sh/z0nt79pd0l5psfg/bJTbaS2JpY
EDIT: Кажется, это происходит, когда игрок слегка отскакивает от земли на мгновение после удара. Есть ли способ сделать это, чтобы игрок не отскакивал вообще?
EDIT 2: Я попытался решить это, используя параметр трения, и разрешил игроку "прыгать", когда трение было = 0 (вы бы подумали, что это будут все случаи, когда игрок находился в воздухе), но трение кажется больше 0. Как еще я могу определить, касается ли игрок касания объекта (кроме использования y-местоположения)?
Спасибо
Ответы
Ответ 1
Рекомендуемое решение
Если вы пытаетесь реализовать функцию перехода, я предлагаю вам посмотреть applyImpulse
вместо applyForce
. Здесь разница между ними, как описано в Руководстве по программированию комплекта Sprite:
Вы можете применить либо силу, либо импульс:
Сила применяется на протяжении времени, исходя из количества времени моделирования, которое проходит между тем, когда вы применяете силу, и когда обрабатывается следующий кадр моделирования. Таким образом, чтобы применить непрерывную силу к телу, вы должны делать соответствующие вызовы методов каждый раз, когда обрабатывается новый кадр. Силы обычно используются для непрерывных эффектов.
Импульс мгновенно меняет скорость bodys, которая не зависит от количества пройденного времени моделирования. Импульсы обычно используются для немедленных изменений скорости тела.
Скачок - это действительно мгновенное изменение скорости тела, означающее, что вам следует применять импульс вместо силы. Чтобы использовать метод applyImpulse:
, укажите желаемое мгновенное изменение скорости, умножьте его на массу тела и используйте в качестве параметра impulse
функцию. Я думаю, вы увидите лучшие результаты.
Объяснение для непредвиденного поведения
Если вы вызываете applyForce:
вне вашей функции update:
, то происходит то, что ваша сила умножается на количество времени, прошедшего между тем, когда вы применяете силу, и когда обрабатывается следующий кадр моделирования, Этот множитель не является константой, поэтому вы каждый раз видите изменение скорости при каждом вызове applyForce:
.
Ответ 2
@godel9 имеет хорошее предложенное решение, хотя в моем собственном тестировании объяснение, приведенное для неожиданного поведения, неверно.
Из Ссылка на класс SKPhysicsBody:
Сила применяется для одного шага моделирования (один кадр).
Возвращаясь к разделу SKScene Reference Reference по методу -update:
... он называется ровно один раз за кадр, пока сцена представлена в представлении и не приостанавливается.
Итак, мы можем предположить, что вызов -applyForce: в методе SKScene -update не должен вызывать проблемы. Но, как было замечено, сила не превышает гравитацию, несмотря на то, что она направлена вверх, значительно превышающая гравитацию (400 ньютонов против 9,81).
Я создал тестовый проект, который создавал бы два узла: один, который падает естественным образом, устанавливая значение affectedByGravity в TRUE, а другой, который вызывает -applyForce с тем же ожидаемым вектором силы тяжести (0 ньютонов в направлении x и -9.81 в y направление). Затем я вычислил разницу в скорости каждого node за один шаг времени и шаг времени. Затем я зарегистрировал ускорение (изменение скорости/изменение во времени).
Вот фрагмент моего подкласса 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;
}
Результаты необычны. node, на который влияет сила тяжести в симуляции, имеет ускорение, которое последовательно умножается в 150 раз по сравнению с node, сила которого была применена вручную. Я попытался это с узлами разного размера и плотности, но существует один и тот же скалярный множитель.
Из этого я должен сделать вывод, что SpriteKit внутренне имеет отношение "пиксель-метр" по умолчанию. То есть каждый "метр" равен ровно 150 пикселям. Иногда это полезно, так как в противном случае сцена часто слишком велика, что означает, что силы реагируют медленно (думайте, наблюдая за самолетом с земли, он путешествует очень быстро, но, похоже, движется очень медленно).
Документация Sprite Kit часто предполагает, что точные вычисления физики не рекомендуются (см. Конкретно в разделе "Fudging the Numbers" ), но эта несогласованность заставила меня долго ждать. Надеюсь, это поможет!