Sprite kit 使用SpriteKit在Swift 3中进行不一致的接触检测
我在使用SpriteKit的Swift 3中遇到了联系人检测问题。接触检测正在工作…有时。它什么时候开火,什么时候不开火,看起来完全是随机的。 我有一颗黄色的“子弹”在屏幕上向上移动,击中一个名为targetSprite的红色精灵。理想的行为是在子弹击中目标时将其取出,但有时它只是从下面穿过。 我发现很多关于接触检测的问题根本不起作用,但我没有发现任何关于不一致检测的问题 我能做些什么来解决这个问题 代码如下:Sprite kit 使用SpriteKit在Swift 3中进行不一致的接触检测,sprite-kit,swift3,Sprite Kit,Swift3,我在使用SpriteKit的Swift 3中遇到了联系人检测问题。接触检测正在工作…有时。它什么时候开火,什么时候不开火,看起来完全是随机的。 我有一颗黄色的“子弹”在屏幕上向上移动,击中一个名为targetSprite的红色精灵。理想的行为是在子弹击中目标时将其取出,但有时它只是从下面穿过。 我发现很多关于接触检测的问题根本不起作用,但我没有发现任何关于不一致检测的问题 我能做些什么来解决这个问题 代码如下: import SpriteKit import GameplayKit enum
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)
你试过添加吗
.physicsBody?.isDynamic = true
.physicsBody?.usesPreciseCollisionDetrction =true
第一条建议-不要在SpriteKit中使用NSTimer(又称计时器)。它不与游戏循环配对,在不同的情况下可能会导致不同的问题。阅读更多(答案由LearnCos2D发布) 因此,请这样做:
let wait = SKAction.wait(forDuration: 1)
run(wait, completion: {
[unowned self] in
self.fireBullet()
})
我注意到,如果我在模拟器中运行您的代码,我会得到您描述的行为<代码>DidBeagin(联系人:)正在被随机激发。不过,对我来说,这并不是在设备上发生的,设备测试才是最重要的
现在,当我移除
计时器
并对SKAction(s)
执行相同操作时,表示每次都检测到接触。如果执行以下操作,SpriteKit物理引擎将正确计算碰撞:
1) 将bullet的物理实体的“usesPreciseCollisionDetection”属性设置为true。这将更改此实体的碰撞检测算法。您可以在“使用碰撞和接触”一章中找到有关此属性的更多信息
2) 使用applyImpulse或applyForce方法移动子弹。如果您通过手动更改身体的位置来移动身体,碰撞检测将无法正常工作。你可以在“使物理物体运动”一章找到更多信息。谢谢你的建议,我一定会看这篇文章的。我对Swift有点陌生,所以我对它的背景不太了解。嗨,sicvayne,
isDynamic
的默认值总是,所以在这种情况下,这一行是不需要的。我同意你的第一句话,但我不能说你的第二句话是真的,即使这是一个好建议。所以我想这样说:如果你手动移动你的精灵(通过直接改变精灵的位置属性,或者通过动作间接改变它),如果它们不受重力或任何其他力的影响,接触检测将正常工作。在SpriteKit中,这是一种认可的方式,将物理引擎(以及其中的一部分)与SKAction结合使用。如果您手动移动身体(例如,使用SKAction),您的身体可能会穿过另一个小身体,或者在某些碰撞中没有响应,因为物理引擎计算速率与重新绘制帧速率不同。这就是为什么你需要使用脉冲和力移动所有物理对象的原因。你在哪里看到这个文档(关于绘图速率)?因为iirc,当移动节点太快时,无论是通过动作还是通过力,无论您使用的是精确的碰撞检测,接触都可能最终未被检测到。我确信这一点,因为这一规则不仅适用于SpriteKit,而且适用于大多数物理引擎,例如Box2D。你也可以看到,在大多数的游戏教程中,比如曲棍球,物体通过脉冲跟随手指。这比我们的行动要困难得多。但SKAction无法正常工作。请检查我答案中的链接。“您还可以将自己的力和脉冲应用于身体。场景完成这些计算后,它会更新节点对象的位置和方向。”因此,您可以看到,如果手动设置位置,将跳过某些计算。因此,它只能在缓慢移动和大型物体时正常工作。