Почему 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, поэтому я должен предполагать, что это предполагаемое поведение.