2014-12-27 2 views
14

Я создал свою собственную очень простую тестовую игру на основе Breakout, изучая SpriteKit (используя игры iOS от Tutorials от Ray Wenderlich и др.), Чтобы узнать, могу ли я применить которые я узнал. Я решил упростить свой код, используя файл .sks для создания узлов спрайтов и замены проверки ручных границ и столкновения с физическими телами.Физика SpriteKit в Swift - Мяч скользит против стены вместо отражения

Однако, мой шар продолжает работать параллельно стенам/другим прямоугольникам (как в, просто сдвигая их вверх и вниз) в любое время, когда он сталкивается с ними под крутым углом. Вот соответствующий код - я переместил свойство физики тела в код, чтобы сделать их более заметными:

import SpriteKit 

struct PhysicsCategory { 
    static let None:  UInt32 = 0  // 0 
    static let Edge:  UInt32 = 0b1 // 1 
    static let Paddle:  UInt32 = 0b10 // 2 
    static let Ball:  UInt32 = 0b100 // 4 
} 

var paddle: SKSpriteNode! 
var ball: SKSpriteNode! 

class GameScene: SKScene, SKPhysicsContactDelegate { 

    override func didMoveToView(view: SKView) { 

    physicsWorld.gravity = CGVector.zeroVector 

    let edge = SKNode() 
    edge.physicsBody = SKPhysicsBody(edgeLoopFromRect: frame) 
    edge.physicsBody!.usesPreciseCollisionDetection = true 
    edge.physicsBody!.categoryBitMask = PhysicsCategory.Edge 
    edge.physicsBody!.friction = 0 
    edge.physicsBody!.restitution = 1 
    edge.physicsBody!.angularDamping = 0 
    edge.physicsBody!.linearDamping = 0 
    edge.physicsBody!.dynamic = false 
    addChild(edge) 

    ball = childNodeWithName("ball") as SKSpriteNode 
    ball.physicsBody = SKPhysicsBody(rectangleOfSize: ball.size)) 
    ball.physicsBody!.usesPreciseCollisionDetection = true 
    ball.physicsBody!.categoryBitMask = PhysicsCategory.Ball 
    ball.physicsBody!.collisionBitMask = PhysicsCategory.Edge | PhysicsCategory.Paddle 
    ball.physicsBody!.allowsRotation = false 
    ball.physicsBody!.friction = 0 
    ball.physicsBody!.restitution = 1 
    ball.physicsBody!.angularDamping = 0 
    ball.physicsBody!.linearDamping = 0 

    physicsWorld.contactDelegate = self 
} 

забыл упомянуть об этом раньше, но я добавил простую функцию touchesBegan для отладки отскоков - это просто регулирует скорость, чтобы указать мяч в точке касания:

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { 
    let touch = touches.anyObject() as UITouch 
    let moveToward = touch.locationInNode(self) 
    let targetVector = (moveToward - ball.position).normalized() * 300.0 
    ball.physicsBody!.velocity = CGVector(point: targetVector) 
} 

функция нормализована() как раз уменьшает шарик/позиция касания дельты в единичном вектор, и есть переопределение минус оператора, что позволяет CGPoint вычитания ,

Шарнир/краевые столкновения должен всегда отражать шар точно противоположным углом, но по какой-то причине шар действительно имеет вещь для прямых углов. Я могу, конечно, реализовать некоторое обходное решение, чтобы отразить угол шара вручную, но дело в том, что я хочу сделать все это, используя встроенную физическую функциональность в SpriteKit. Есть ли что-то очевидное, что мне не хватает?

+2

Вам нужно будет перемещать спрайт по применяя силу или импульс к ее физическому телу или устанавливая его свойство скоростей. Спрайт не будет участвовать в физическом моделировании, если вы переместите его с действием или, установив его положение напрямую. – 0x141E

+0

Извините, я не учитывал настройку скорости. Я только что редактировал его, но в основном это событие касания, которое задает скорость шара в точке касания в SKScene. –

+0

Вы нажимаете и отпускаете палец или удерживаете его, когда происходит контакт? – 0x141E

ответ

7

Я придумал временное решение, которое работает на удивление хорошо. Просто примените очень маленький импульс, противоположный границе. Возможно, вам придется изменить strength на основе масс в вашей системе.

func didBeginContact(contact: SKPhysicsContact) { 

    let otherNode = contact.bodyA.node == ball.sprite ? contact.bodyB.node : contact.bodyA.node 

    if let obstacle = otherNode as? Obstacle { 
     ball.onCollision(obstacle) 
    } 
    else if let border = otherNode as? SKSpriteNode { 

     assert(border.name == "border", "Bad assumption") 

     let strength = 1.0 * (ball.sprite.position.x < frame.width/2 ? 1 : -1) 
     let body = ball.sprite.physicsBody! 
     body.applyImpulse(CGVector(dx: strength, dy: 0)) 
    } 
} 

В действительности, это не должно быть необходимым, так как описано в вопросе, трений, полностью упругое столкновение диктует, что мяч должен отскакивать от не переворачивая й скорость (предполагаются, что боковые границы), независимо от того, насколько малы столкновения угол есть.

Вместо этого то, что происходит в игре, похоже на то, что набор спрайтов игнорирует скорость X, если он меньше определенного значения, заставляя мяч скользить по стене без отскока.

Final Примечание

После прочтения this и this, это для меня очевидно, что реальный ответ на какой-либо серьезной игре физики у вас есть, вы должны использовать Box2D вместо этого. Из миграции вы получаете слишком много льгот.

+0

Box2D отлично работает и не имеет таких проблем, как эта липкость к краю? – Jurik

+0

@Jurik Вот что ссылаются на статьи, которые я ссылаюсь. – Mazyod

+0

Ah okay - thanks - потому что я просмотрел обе статьи, и я не нашел никакой информации, относящейся к этой ошибке, или что Box2D не имеет проблем с округлением. – Jurik

8

Это, как представляется, проблема с обнаружением столкновения. Большинство из них нашли решения, используя didBeginContact и повторно применяя силу в противоположном направлении. Обратите внимание, что он говорит didMoveToView, но исправляет себя в более позднем комментарии didBeginContact.

См комментариев в нижней части учебника Ray Wenderlich here

У меня есть исправление проблемы с шариком «верхом рельсы», если она удары по небольшому углу (@ aziz76 и @colinf). Я добавил еще одну категорию , «BorderCategory» и присвоил ее границе PhysicalBody , которую мы создаем в файле didMoveToView.

и аналогичный вопрос here, объясняющий, почему это происходит.

Даже если вы сделаете это, хотя, многие физики двигателей (в том числе SpriteKit в) возникают проблемы с ситуациями, как это из-за с плавающей точкой округления ошибок. Я обнаружил, что, когда я хочу, чтобы тело до сохраняло постоянную скорость после столкновения, лучше всего это сделать - использовать обработчик didEndContact: или didSimulatePhysics для сброса скорости перемещения тела , так что скорость будет одинаковой был до столкновения (но в противоположном направлении).

Также другая вещь, которую я заметил, вы используете квадрат вместо окружности для вашего шара и вы можете рассмотреть возможность использования ...

ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width/2) 

Так получается, вы не сумасшедший, который всегда приятно слышать от кого-то еще и, надеюсь, это поможет вам найти решение, которое наилучшим образом подходит для вашего приложения.

+0

Отличное объяснение проблемы. +1 – sangony

+0

Лично я уже нашел импульсное решение, было самым интуитивным способом, но опять же, это не обязательно. Я понимаю, что у SpriteKit нет детерминированного механизма физики, но он интересовался более сложным решением для всех случаев. наконец, шаром имеет круглое тело. – Mazyod

+0

@ Мазей согласен. Это не должно быть проблемой. Если вы не можете доверять физическому движку, тогда вы боитесь, что ваше приложение не будет работать должным образом, и это может быть даже не ваша ошибка. Для чего-то вроде прорывной игры может быть лучше обработать ваше собственное позиционирование/скорость в цикле обновления, но пусть SK сообщит вам, когда начнется контакт. Таким образом, вы всегда имеете постоянную скорость, потому что у вас есть полный контроль над ней. Аналогичные вещи были сделаны с перемещением персонажа с помощью экранного джойстика. Надеюсь, Apple улучшит это со следующего обновления. –

0

Я видел точно такую ​​же проблему, но исправление для меня не было связано с проблемами обнаружения столкновений, упомянутыми в других ответах. Оказывается, я приводил мяч в движение, используя SKAction, который повторяется навсегда. В конце концов я обнаружил, что это противоречит физическому моделированию SpriteKit, ведущему к узлу/шару, перемещающемуся вдоль стены, вместо того, чтобы отскакивать от него.

Я предполагаю, что повторяющийся SKAction продолжает применяться и переопределяет/конфликтует с автоматической настройкой физического моделирования свойства physicsBody.velocity шарика.

Исправить это было, чтобы установить шар в движение, установив velocity на свой physicsBody. Как только я это сделал, мяч начал прыгать правильно. Я предполагаю, что манипулирование своим положением через physicsBody с помощью сил и импульсов также будет работать, учитывая, что они являются частью моделирования физики.

Мне потребовалось некоторое время, чтобы понять эту проблему, поэтому я размещаю это здесь, если я могу кого-нибудь сэкономить. Спасибо 0x141e! Ваш comment поставил меня (и мой мяч) на правильный путь.

1

Эта проблема возникает, только когда скорость мала в любом направлении. Однако, чтобы уменьшить эффект можно уменьшить скорость physicsWorld, например,

physicsWorld.speed = 0.1 

, а затем увеличить скорость physicsBody, например,

let targetVector = (moveToward - ball.position).normalized() * 300.0 * 10 
ball.physicsBody!.velocity = CGVector(point: targetVector) 
+0

Это сработало, но ... почему ?! – Norbert

+0

Хороший вопрос. Я действительно не знаю, но я думаю, что это связано с физикой: F = ma. Предположим, что m = 1 и численно a = dv/dt, предполагается, что установка скорости physicWorld совпадает с установкой dt, т. Е. Если мы установим pW-скорость в 0,1, получим, что F = dv/(0,1 * dt). И поэтому, если скопление происходит при F CrazyChris

Смежные вопросы