Почему 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. Создайте переменную, которая сделает игрока неуязвимым для потери жизни после нескольких секунд удара.
- Создайте глобальную логическую переменную с именем isInvuln (установлена в FALSE) и NSTimeInterval с именем invulnTime.
- В методе, который обрабатывает игрока и врага, вступающих в контакт, проверьте, является ли isInvuln False перед тем, как покончить с жизнью. (если isInvuln верно... ничего не делать)
Если isInvuln - false, возьмите жизнь, тогда установите isInvuln в true.
if(self.isInvuln == FALSE){ self.player.lives-=1; self.isInvuln = True;}
Добавьте к вашему обновлению с текущим временем:
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, поэтому я должен предположить, что это предполагаемое поведение.