2017-02-15 111 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

相关问题