2016-09-15 8 views
3

OK - Я уверен, что это дубликат, и что Whirlwind или KnightOfDragons опубликовали решение, но я не могу его найти.Sprite-Kit, регистрирующий множественные столкновения для одного контакта

Я пишу клон Space Invader в Swift с помощью Sprite-Kit. Проблема в том, что, когда бомба-захватчик поражает мой корабль, код иногда регистрирует 2 или 3 столкновения, сразу заканчивая игру. Вот раздел «didBeginContact», который обрабатывает столкновение бомбы/корабля:

case category.bomb.rawValue | category.ship.rawValue: 
     print("Bomb hit ship!") 
     let bomb = contact.bodyA.categoryBitMask == category.bomb.rawValue ? contact.bodyA.node : contact.bodyB.node 
     bomb?.physicsBody?.contactTestBitMask = 0 // Seems to prevent multiple contacts with same bomb 
     ship.physicsBody!.contactTestBitMask = 0 // We'll reset this after the death animation 
     bomb?.removeAllActions() 
     ship.removeAllActions() 
     bomb?.removeFromParent() 
     ships -= 1 

     if ships == 0 { endScene(won: false, withMessage: "A bomb got you!") } 

и когда я запускаю игру, я вижу:

Bomb hit ship! 
2 ships left. 

после 1 попадания бомбы (это правильно)

Bomb hit ship! 
1 ships left. 
Bomb hit ship! 
0 ships left. 

после 2-го удара бомбы (что неправильно).

У меня никогда не было контакта, не будучи зарегистрированным, а иногда (50%?) Он отлично работает. В других случаях я видел себя с -4 кораблями! Я уверен, что это что-то очевидное/фундаментальное, что я ошибаюсь.

Мои комментарии относительно установки контактаTestBitMask на 0 для бомбы и корабля, очевидно, являются неправильными. Я знаю, что это не обязательно, поскольку я удаляю бомбу, когда происходит контакт, поэтому она не должна повторяться снова.

Как я могу гарантировать, что контакт обрабатывается только один раз?

================================

Обновление: Я добавил 2 print заявления, чтобы обеспечить более отладочная информация:

 print("bodyA is \(contact.bodyA.node!.name)") 
     print("bodyB is \(contact.bodyB.node!.name)") 

Это после let bomb = contact.bodyA.category ... заявление и теперь я получаю:

Bomb hit ship! 
bodyA is Optional("bomb") 
bodyB is Optional("playerShip") 
1 ships left. 

после 1 попаданием бомбы, а также:

Bomb hit ship! 
fatal error: unexpectedly found nil while unwrapping an Optional value 

после 2-го удара бомбы. Итак, после второго столкновения, bodyA равно нулю, поэтому я не понимаю, почему Sprite-Kit фактически зарегистрировал столкновение?

Любые идеи?

+0

Является ли ответ, что Sprite-Kit поставил в очередь несколько столкновений, которые должны быть обработаны между судном и бомбой (что не должно, но эй, кто знает), а затем SK называет dBC для каждого столкновения и потому, что я удалил бомбу первый раз, это ноль второго и последующих времен. Поэтому мне нужно отметить бомбу как активную, когда она упала, проверить, активна ли бомба, когда я обнаруживаю столкновение и устанавливаю его неактивным, а не удаляет его в dBC? –

+0

Из того, что я видел, didBeginContact запускается несколько раз, когда между двумя телами есть несколько точек контакта. Это может произойти, когда вы создаете тело из текстуры или даже если тела являются круговыми. На данный момент я не могу найти свои собственные ответы (я нахожусь по телефону), но в этом объясняется крах, который вы испытываете: http://stackoverflow.com/a/37003007 Существует несколько способов решить вашу проблему. Можно было бы проверить, нет ли node.parent nil и return from didBeginContact, если true. Другой способ - это флаг на пулевом узле. Возможно, некоторые другие способы, я не помню больше. – Whirlwind

+0

Да, что @Whirlwind сказал, помните, что вы действительно не хотите удаляться от родителя во время фазы didcontactbegin, потому что могут быть случаи, когда вам нужен узел, который все еще будет в сцене для второго контакта (одна пуля попадает 2 перекрытия враги, может быть, вы хотите призрак, кто знает) Мне лично нравится резервировать categoryBitMask бит 31 для обработки живых или мертвых, а во время проверки физики пропустите эти узлы – Knight0fDragon

ответ

1

OK - казалось бы, что простое:

 if bomb == nil {return} 

это все, что требуется. Это должно быть добавлено следующее:

 let bomb = contact.bodyA.categoryBitMask == category.bomb.rawValue ? contact.bodyA.node : contact.bodyB.node 
     if bomb == nil {return} 

Это работает, чтобы предотвратить появление нескольких столкновений для узла, который removeFromParent в didBeginContact. Если вы не удаляете узел, но все еще регистрируете несколько коллизий, тогда используйте свойство userData узла, чтобы установить какой-то флаг, указывающий, что узел i s'anactive «Альтернативно, подкласс SKSPriteNode и добавить пользовательское свойство isActive», которое это то, что я сделал, чтобы решить мою проблему с пулями, проходящими на стороне захватчика, и вытащить этого захватчика и над ним. Это никогда не происходит при прямом попадании.

Он не отвечает на основной вопрос, почему SK является регистрация нескольких столкновений, но это не означает, что я могу удалить все дополнительного кода относительно установки contactTestBitMasks 0, а затем обратно к тому, что они должны быть позже и т.д.

Редактирование: Таким образом, кажется, что если вы удалите узел в didBeginContact, узел будет удален, но физическое тело не будет. Таким образом, вы должны быть осторожны, так как Sprite-Kit, похоже, создает массив физических контактов, которые произошли, а затем вызывает dBC несколько раз, один раз для каждого контакта. Поэтому, если вы манипулируете узлами, а также удаляете их в dBC, имейте в виду, что вы можете столкнуться с проблемой, если вы принудительно развернете свойства узла.

+0

, это вам не поможет, вам просто повезло. Что, если bodyA была бомбой? Это означает, что bodyA теперь равен нулю, что означает, что вы столкнетесь с «let bomb = contact.bodaA».categoryBitMask' – Knight0fDragon

+0

Я думал, что у бомбы будет установлен контакт.bodyA.node, если contact.bodyA.categoryBitMask == category.bomb.rawValue, иначе он будет установлен в контакт.bodyB.node –

+0

E.G. bodyA - это бомба. На первом перевале вы убили бомбу. В корпусе secondPassA теперь мертв, так что он равен нулю. Теперь вы столкнетесь с 'contact.bodyA.categoryBitMask == category.bomb.rawValue', потому что вы делаете' nil.categoryBitMask == category.bomb.rawValue', который не разрешен, потому что contact.bodyA не является необязательным. Если это необязательно, то бомбу становится другим спрайтом, что тоже нежелательно. – Knight0fDragon

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

  • Нет связанных вопросов^_^