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

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

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

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

6 ответов

Решение

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

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

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

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

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

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

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

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

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

Я разобрался с простым решением:

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

Например:

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

      secondBody.categoryBitMask = 0;

      // DO OTHER THING HERE

}

Я столкнулся с той же проблемой. В моем случае didBeginContact() был вызван много раз (я насчитал до 5 раз) за один контакт пули с противником. Поскольку пуля представляет собой простой формат круга, я согласен с @SFX, что это не может быть ошибкой только в Texture-Bodies. Тесты показали, что не было звонка update() между didBeginContact() звонки. Так что решение простое (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() но это не называлось вообще. Я не расследовал это дальше.

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

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

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

  1. Создайте глобальную логическую переменную с именем isInvuln (установлена ​​в FALSE) и NSTimeInterval с именем invulnTime.
  2. В методе, который обрабатывает игрока и врага, вступающих в контакт, проверьте, является ли isInvuln False перед тем, как покончить с жизнью. (если isInvuln верно... ничего не делать)
  3. Если isInvuln - false, возьмите жизнь, тогда установите isInvuln в true.

     if(self.isInvuln == FALSE){
          self.player.lives-=1;
          self.isInvuln = True;}
    
  4. Добавьте к вашему обновлению с текущим временем:

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

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

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

Другие вопросы по тегам