Почему callBeginContact вызывается несколько раз?

В iOS-игре, которая использует Sprite Kit вместе с обнаружением контакта в встроенном физическом движке Sprite Kit, я уменьшаю число героев на каждый раз, когда он вступает в контакт с противником. Это делается с помощью метода didBeginContact. Однако кажется, что этот метод называется не раз один раз, когда контакт начинается, но называется непрерывно, пока Герой и противник перекрываются: когда я устанавливаю точку останова в этом методе, я вижу, что это то же самое экземпляры физического тела, которые существуют как contact.bodyA и contact.bodyB. В результате Герой потеряет несколько жизней, хотя он пропускает только одного врага.

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

Вопрос теперь: как бы вы удостоверились, что для каждого контакта героя/врага вычитается только один живой?

Ответ 1

У меня была та же проблема (оценка увеличивалась несколько раз за уничтожение одного врага и потери нескольких жизненных точек за один случай повреждения.) Пользователь на форумах Apple думает, что он ошибка в [SKPhysicsBody bodyWithTexture: size:], но я не верю этому случаю, потому что это происходило и с другими конструкторами.

Во-первых, очень важны categoryBitMask и contactTestBitMask. Взгляните на образец кода SpriteKit Physics Collisions:

//Контакты часто представляют собой проблему с двойной отправкой; эффект, который вы хотите, зависит от типа обоих тел в контакте. Это пример этого методом грубой силы, проверяя типы каждого. Более сложный пример может использовать методы для объектов для проверки типа.

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

То, что я сделал для его решения, это установить флаг после обработки каждого условия. В моем случае я тестировал, был ли bodyA.node.parent равен нулю в didBeginContact, потому что я вызывал removeFromParent() на ракеты/вражеские узлы, чтобы уничтожить их.

Я думаю, вы должны ожидать, что событие будет срабатывать несколько раз, и ваш код там должен убедиться, что он обрабатывается только один раз.

Ответ 2

Причина, по которой срабатывание didBeginContact срабатывает несколько раз, состоит в том, что на вогнутых фигурах происходит несколько точек контакта.

Если вы посмотрите на рисунок ниже, вы увидите, что у меня есть 2 спрайта, черная звезда и красный прямоугольник. Когда черная звезда попадает в красный прямоугольник, она ударяет по нескольким точкам, обведенным синим цветом. Затем Sprite Kit выполнит вызов для каждого пересечения линий, чтобы разработчик мог использовать переменную contactPoint для каждого из этих контактов.

введите описание изображения здесь

Ответ 3

Я столкнулся с той же проблемой. В моем случае didBeginContact() был вызван много раз (я подсчитал до 5 раз) за один контакт пули с противником. Поскольку пуля представляет собой простой формат круга, я согласен с @SFX, что это не может быть ошибкой только в Texture-Bodies. Тесты показали, что между вызовами didBeginContact() не было вызова update(). Итак, решение прост (Swift):

var updatesCalled = 0
...
internal update() {
  updatesCalled ++
}
...
internal func didBeginContact(contact: SKPhysicsContact) {
    NSLog("didBeginContact: (\(contact.contactPoint.x), \(contact.contactPoint.y)), \(updatesCalled)")
    if(updatesCalled == 0) {return} // No real change since last call
    updatesCalled = 0
    ... your code here ...
}

Я попробовал didEndContact(), но это не было вызвано вообще. Я еще не исследовал это.

BTW: я просто переключился с Android, и меня впечатлило легкость и стабильность этой системы: -)

Ответ 4

Вот опция, которая делает игрока неуязвимым после того, как его ударили в течение установленного времени:

а. Создайте переменную, которая заставит игрока неуязвимо потерять жизнь после удара в течение нескольких секунд.

  • Создайте глобальную логическую переменную с именем isInvuln (установлено в FALSE) и NSTimeInterval с именем invulnTime.
  • В методе, который обрабатывает контакт игрока и врага, проверьте, не является ли isInvuln ложным, прежде чем принимать жизнь. (если isInvuln истинно... ничего не делать)
  • Если isInvuln является ложным, возьмите жизнь, затем установите isInvuln в true.

     if(self.isInvuln == FALSE){
          self.player.lives-=1;
          self.isInvuln = True;}
    
  • Добавить в ваше updateWithCurrentTime:

     if(self.isInvuln==True){
     self.invulnTime += timeSinceLast;}
    
     if (self.invulnTime > 3) {             
         self.isInvuln = FALSE:}
         self.invulnTime= 0;
    

Это сделает так, что когда враг и игрок столкнутся, игрок теряет жизнь и станет неуязвимым на 3 секунды. Через 3 секунды игрок может снова получить урон. Если враг связывается с игроком в течение 3 неуязвимых секунд, метод контакта ничего не делает. Надеюсь, это поможет искусить идеи для решения вашей проблемы.

Ответ 5

Я понял легкое решение:

Просто измените значение bodyBitMask на 0 или неиспользованное значение сразу после обнаружения контакта.

Например:

if (firstBody.categoryBitMask == padCategory && secondBody.categoryBitMask == colorBallCategory) {

      secondBody.categoryBitMask = 0;

      // DO OTHER THING HERE

}

Ответ 6

По моему опыту, didEndContact и didBeginContact вызываются несколько раз, пока объекты перекрываются. Это также происходит в SceneKit с использованием iOS 9, поэтому я должен предполагать, что это предполагаемое поведение.