同一个 SKPhysicsBody 多次调用 didBeginContact
Posted
技术标签:
【中文标题】同一个 SKPhysicsBody 多次调用 didBeginContact【英文标题】:didBeginContact is being called multiple times for the same SKPhysicsBody 【发布时间】:2015-01-15 04:04:08 【问题描述】: func didBeginContact(contact: SKPhysicsContact)
if ( contact.bodyA.categoryBitMask & BodyType.shield.rawValue ) == BodyType.shield.rawValue
contact.bodyB.node?.removeFromParent()
counter++
println(counter)
else if ( contact.bodyB.categoryBitMask & BodyType.shield.rawValue ) == BodyType.shield.rawValue
contact.bodyA.node?.removeFromParent()
counter++
println(counter)
一个物理体来自纹理shield.physicsBody = SKPhysicsBody(texture: shieldTexture, size: shieldTexture.size())
另一个来自圈子sand.physicsBody = SKPhysicsBody(circleOfRadius: sand.size.width/2)
当两个对象相互接触时,sand.physicsBody = SKPhysicsBody(circleOfRadius: sand.size.width/2)
会被多次调用。我如何让它只为每个对象调用一次,即使我一接触它就从父对象中删除它。
【问题讨论】:
我相信这是按预期工作的,因为纹理中的主体可能会在内部生成多个形状,每个形状都可能导致接触事件。在物理模拟步骤结束之前,移除节点不会移除实体。您必须手动将节点或主体“标记”为已在此联系事件中处理,以便您可以跳过相同主体的任何后续联系事件。 我希望 SKPhysicsBody 有一个选项可以从纹理中通过参数进行设置,因此它的行为与例如来自 circleOfRadius 的 SKPhysicsBody 所以它只计算 1 次命中/接触,因为在某些情况下,这比创建任何额外的逻辑更需要,包括下面的答案。此外,如果不需要,它可以节省资源以不检测更多联系人.. 【参考方案1】:我已经知道如何让func didBeginContact(contact: SKPhysicsContact)
只被调用一次。这允许具有纹理 SKPhysicsBody(texture: size:)
的物理体计算一次碰撞,即使在现实中(由于纹理物理体的性质)该函数将被多次调用。
第 1 步:
为 SKSpriteNode 创建一个 name 属性(我们将在本示例中使用 ball) 并将其设置为唯一名称。我们可以通过创建一个 int 来做到这一点
var number = 0
ball.name = "ball \(number)"
这允许在创建对象时使用唯一的名称。
第 2 步:
创建一个数组来保存这些,将球附加到数组中,增加数字
var array: [String] = []
var number = 0
ball.name = "ball \(number)"
array.append(ball.name!)
number ++
第 3 步:现在在 func didBeginContact(contact: SKPhysicsContact)
中查找名称是否在数组中。如果是增加分数,则删除节点,并从数组中删除名称。如果名称不在数组中,则不执行任何操作。
从数组中删除名称允许我们现在只计算一次函数调用。
func didBeginContact(contact: SKPhysicsContact)
if ( contact.bodyA.categoryBitMask & BodyType.shield.rawValue ) == BodyType.shield.rawValue
var name = contact.bodyB.node?.name!
let index = find(array, name!)
if contains(array, name!)
score++
contact.bodyB.node?.removeFromParent()
array.removeAtIndex(index!)
【讨论】:
你不能把 SKSpriteNode 添加到数组中吗?它通过引用传递,因此我们可以比较它们,我不明白为什么我们需要分配唯一的名称。【参考方案2】:在这种情况下,您可以在没有数组的情况下使其工作。而不是这个:
contact.bodyA.node?.removeFromParent()
counter++
使用这样的东西:
if let node = contact.bodyA.node as? SKSpriteNode
if node.parent != nil
node.removeFromParent()
counter++
在第一次联系时,您从父节点中删除节点,在随后的调用中,if 语句中的代码将被跳过。
【讨论】:
【参考方案3】:如果您使用contactbitmask
来确定要捕获哪些冲突,则有一个非常简单的解决方案。
只需将您不想被重复检测到的对象的categoryBitMask
更新为未使用的新值,这样系统就不再认为后续函数调用相关。
【讨论】:
【参考方案4】:当使用基于纹理的物理实体时,问题似乎更频繁地发生。如果在之前的碰撞触发器中指示了相同的“contact.bodyA.node”,我解决它的方法是禁止“didBegin(_contact:SKPhysicsContact)”继续。即:
if lastNodeA != contact.bodyA.node
lastNodeA = contact.bodyA.node
...
【讨论】:
【参考方案5】:LearnCocos2D 是对的,只要两个物体的 SKphysicsbody 接触,SKPhysicsbody didBeginContact 就会不断调用,因为我们在SKPhysicsBody(texture:xxx, size:xx)
中允许的形状可以有多种形式和形状。
对于那些只需要检测一次的人,我们只需要使用一个布尔值作为标志来检查检测是否完成并结束。
这是我的做法:
声明一个var
布尔值:
var contactDone = Bool()
在程序开始时初始化它(例如didMoveToView
下面)
contactDone = false
签入didBeginContact
:
func didBeginContact(contact:SKPhysicsBody)
if((contact.bodyA.categoryBitMask) == scoreCategory || (contact.bodyB.categoryBitMask) == scoreCategory)
if (contactDone == false)
// Increment score
score++
// Set flag to disable multiple calls by checking in didEndContact
contactDone = true
清除标志让它再次检查didEndContact
:
func didEndContact(contact: SKPhysicsContact)
if((contact.bodyA.categoryBitMask) == scoreCategory || (contact.bodyB.categoryBitMask) == scoreCategory)
if(contactDone == true)
contactDone = false
它就像我使用 SKPhysicBody(circleOfRadius: object.size.height/2)
时一样工作。
【讨论】:
当我使用'SKPhysicsBody(texture: ghostTexture, size: Obj.size)'时不起作用【参考方案6】:我的解决方案是,通过 Date().timeIntervalSince1970 花时间 在联系didBegin
let passedTime = Date().timeIntervalSince1970 - lastContactTime
lastContactTime = Date().timeIntervalSince1970
if passedTime < 0.01
// same object
return
【讨论】:
以上是关于同一个 SKPhysicsBody 多次调用 didBeginContact的主要内容,如果未能解决你的问题,请参考以下文章
调整使用 SKPhysicsBody(circleOfRadius:) 创建的 SpriteKit SKPhysicsBody