2017-02-15 3 views
3

У меня проблема с обнаружением контактов в Swift 3 с помощью SpriteKit. Обнаружение контакта работает ... иногда. Кажется чисто случайным, когда он срабатывает, а когда нет. У меня есть желтая «пуля», которая движется вверх по экрану, чтобы попасть в красный спрайт с именем targetSprite. Желаемое поведение заключается в удалении пули, когда она попадает в цель, но иногда она просто проходит сквозь нее. Я нашел много вопросов о том, что обнаружение контактов вообще не работает, но я не нашел никаких проблем с непоследовательным обнаружением.Несогласованное обнаружение контактов в Swift 3 с помощью SpriteKit

Что можно сделать, чтобы исправить это?

Вот код:

import SpriteKit 
import GameplayKit 

enum PhysicsCategory:UInt32 { 
    case bullet = 1 
    case sprite1 = 2 
    case targetSprite = 4 
    // each new value should double the previous 
} 

class GameScene: SKScene, SKPhysicsContactDelegate { 

// Create sprites 
let sprite1 = SKSpriteNode(color: SKColor.blue, size: CGSize(width:100,height:100)) 
let targetSprite = SKSpriteNode(color: SKColor.red, size: CGSize(width:100,height:100)) 
let bullet = SKSpriteNode(color: SKColor.yellow, size: CGSize(width: 20, height: 20)) 
// show the bullet? 
var isShowingBullet = true 

// Timers 
//var timer:Timer? = nil 
var fireBulletTimer:Timer? = nil 

// set up bullet removal: 
var bulletShouldBeRemoved = false 


let bulletMask = PhysicsCategory.bullet.rawValue 


override func didMove(to view: SKView) { 

    // Physics 
    targetSprite.physicsBody = SKPhysicsBody(rectangleOf: targetSprite.centerRect.size) 
    targetSprite.physicsBody?.affectedByGravity = false 

    bullet.physicsBody = SKPhysicsBody(rectangleOf: bullet.centerRect.size) 
    bullet.physicsBody?.affectedByGravity = false 


    // Contact Detection: 
    targetSprite.physicsBody?.categoryBitMask = PhysicsCategory.targetSprite.rawValue 

    targetSprite.physicsBody?.contactTestBitMask = 
     //PhysicsCategory.sprite1.rawValue | 
     PhysicsCategory.bullet.rawValue 

    targetSprite.physicsBody?.collisionBitMask = 0 // no collision detection 


    // bullet physics 
    bullet.physicsBody?.categoryBitMask = PhysicsCategory.bullet.rawValue 

    bullet.physicsBody?.contactTestBitMask = 
     PhysicsCategory.targetSprite.rawValue 

    bullet.physicsBody?.collisionBitMask = 0 // no collision detection 


    // execute once: 
    fireBulletTimer = Timer.scheduledTimer(timeInterval: 1, 
              target: self, 
              selector: #selector(self.fireBullet), 
              userInfo: nil, 
              repeats: false) 

    // Add sprites to the scene: 
    self.addChild(sprite1) 
    self.addChild(bullet) 
    self.addChild(targetSprite) 

    // Positioning 
    targetSprite.position = CGPoint(x:0, y:300) 
    // Note: bullet and sprite1 are at 0,0 by default 

    // Delegate 
    self.physicsWorld.contactDelegate = self 

} 

func didBegin(_ contact: SKPhysicsContact) { 

    print("didBegin(contact:))") 

    //let firstBody:SKPhysicsBody 
    // let otherBody:SKPhysicsBody 

    // Use 'bitwise and' to see if both bits are 1: 
    if contact.bodyA.categoryBitMask & bulletMask > 0 { 

     //firstBody = contact.bodyA 
     //otherBody = contact.bodyB 
     print("if contact.bodyA....") 
     bulletShouldBeRemoved = true 
    } 
    else { 
     //firstBody = contact.bodyB 
     //otherBody = contact.bodyA 
     print("else - if not contacted?") 
    } 

    /* 
    // Find the type of contact: 
    switch otherBody.categoryBitMask { 
     case PhysicsCategory.targetSprite.rawValue: print(" targetSprite hit") 
     case PhysicsCategory.sprite1.rawValue: print(" sprite1 hit") 
     case PhysicsCategory.bullet.rawValue: print(" bullet hit") 

     default: print(" Contact with no game logic") 
    } 
    */ 


} // end didBegin() 


func didEnd(_ contact: SKPhysicsContact) { 
    print("didEnd()") 

} 

func fireBullet() { 

    let fireBulletAction = SKAction.move(to: CGPoint(x:0,y:500), duration: 1) 
    bullet.run(fireBulletAction) 

} 

func showBullet() { 

    // Toggle to display or not, every 1 second: 
    if isShowingBullet == true { 
     // remove (hide) it: 
     bullet.removeFromParent() 
     // set up the toggle for the next call: 
     isShowingBullet = false 
     // debug: 
     print("if") 

    } 
    else { 
     // show it again: 
     self.addChild(bullet) 
     // set up the toggle for the next call: 
     isShowingBullet = true 
     // debug: 
     print("else") 
    } 

} 

override func update(_ currentTime: TimeInterval) { 
    // Called before each frame is rendered 

    if bulletShouldBeRemoved { 
     bullet.removeFromParent() 
    } 

} 

} 

Извините за непоследовательный отступ, я не могу найти простой способ сделать это ...

EDIT:

I что использование «рамки» вместо «centerRect» делает область столкновения размером спрайта. Например:

targetSprite.physicsBody = SKPhysicsBody(rectangleOf: targetSprite.centerRect.size) 

должно быть:

targetSprite.physicsBody = SKPhysicsBody(rectangleOf: targetSprite.frame.size) 

ответ

1

Вы пытались добавить

.physicsBody?.isDynamic = true 
.physicsBody?.usesPreciseCollisionDetrction =true 
+1

Привет sicvayne, значение по умолчанию для 'isDynamic' всегда [true] (https://developer.apple.com/reference/spritekit/skphysicsbody/1520132-isdynamic), поэтому эта строка в этом случае не нужна –

4

Первый совет - Не используйте NSTimer (ака Timer) в SpriteKit. Он не сопряжен с игровым циклом и может вызвать различные проблемы в разных ситуациях. Подробнее here (ответ, отправленный LearnCocos2D)

Таким образом, сделать это:

let wait = SKAction.wait(forDuration: 1) 

run(wait, completion: { 
    [unowned self] in 
     self.fireBullet() 
}) 

То, что я заметил, что если я запускаю свой код в симуляторе, я получаю поведение, которое вы описали. didBegin(contact:) увольняется случайным образом. Тем не менее, это не происходит на устройстве для меня, и тестирование устройств - это то, что важно.

Теперь, когда я удалил Timer и сделал то же самое с SKAction(s), все работало, значит, контакты были обнаружены каждый раз.

+0

Благодарим вас за совет, я обязательно зачитаю статью.Я немного новичок в Swift, поэтому у меня нет отличного опыта. – user1830828

0

SpriteKit физический движок будет правильно рассчитать коллизию, если вы следующие:

1) установить свойство «usesPreciseCollisionDetection» в истинно для физического тела пули. Это изменит алгоритм обнаружения столкновения для этого тела. Вы можете найти дополнительную информацию об этом объекте here, глава «Работа с конфликтами и контактами».

2) перемещайте свою пулю с помощью методов applyImpulse или applyForce. Обнаружение столкновений не будет правильно укорениться, если вы переместите тело, изменив его положение вручную. Вы можете найти дополнительную информацию here, глава «Поведение физических тел».

+0

Я согласен с вашим первым заявлением, но я не могу сказать, что ваше второе утверждение действительно верно, даже если это хороший совет. Поэтому я бы сказал так: обнаружение контактов будет работать правильно, если вы вручную перемещаете спрайты (либо напрямую изменяя положение спрайта, либо изменяя его косвенно с помощью действий), если они не подвержены влиянию силы тяжести или любых других сил. В SpriteKit разрешен использовать физический движок (его часть) в сочетании с SKActions – Whirlwind

+0

Если вы перемещаете тело вручную (например, с помощью SKAction) - ваше тело может пролететь через другое маленькое тело или не реагировать на некоторые столкновения, потому что скорость вычислений физических двигателей не такая же, как повторная рисование частоты кадров. Вот почему вам нужно будет перемещать все объекты физики, используя импульсы и силы. – Karbaman

+0

Где вы это зафиксировали (о скорости вытягивания)? Поскольку iirc, при слишком быстром перемещении узлов либо с помощью действий, либо силами, независимо от того, используете ли вы точное обнаружение столкновений, контакты могут оказаться незамеченными. – Whirlwind

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