2017-02-15 50 views
3

我在使用SpriteKit的Swift 3中有接觸檢測問題。接觸檢測工作...有時。它看起來完全是隨機的,什麼時候發生,什麼時候沒有發生。 我有一個黃色的「子彈」,在屏幕上向上移動,命中一個名爲targetSprite的紅色精靈。期望的行爲是當子彈擊中目標時將子彈移除,但有時只是穿過下面。 我發現很多關於聯繫檢測的問題根本無法工作,但我還沒有發現任何與不一致檢測有關的問題。使用SpriteKit在Swift 3中不一致的接觸檢測

我能做些什麼來解決這個問題?

下面的代碼:

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() 
    } 

} 

} 

對不起不一致的縮進,我似乎無法找到一個簡單的方法來做到這一點...

編輯:

我發現使用'frame'而不是'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

的第一個忠告 - 不要在SpriteKit使用的NSTimer(又名定時器)。它不與遊戲循環配對,並可能在不同情況下導致不同的問題。更多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」屬性爲true子彈的物理身體。這將改變這個機構的碰撞檢測算法。你可以找到更多關於此屬性here的信息,章節「處理碰撞和聯繫人」。

2)使用applyImpulse或applyForce方法移動子彈。如果通過手動更改位置來移動主體,碰撞檢測將不會正確地啓動。你可以找到更多的信息here,「使物體移動」章節。

+0

我同意你的第一個陳述,但我不能說你的第二個陳述是真的,即使是一個很好的建議。所以我會這樣說:接觸檢測,如果你手動移動你的精靈(或者直接改變精靈的位置,或者間接使用動作改變它),如果它們不受重力或其他力量的影響,它將正常工作。這是在SpriteKit中使用物理引擎(與其結合的一部分)與SKActions相結合的一種認可方式 – Whirlwind

+0

如果您手動移動身體(例如使用SKAction) - 您的身體可能會飛過另一個小身體或者在某些碰撞時不響應,因爲物理引擎計算速率與重新繪製幀速率不同。這就是爲什麼你需要使用衝動和力量移動所有物理對象。 – Karbaman

+0

你從哪裏看到這個記錄(關於繪圖率)?由於iirc在節點移動速度太快時(無論是通過行動還是通過力量),無論您是否使用精確的碰撞檢測,接觸都可能最終未被發現。 – Whirlwind

相關問題