Shu*_*060 7 touch hittest sprite-kit sknode swift
当兄弟姐妹重叠时,错误 - 命中测试无法正常工作:
场景中有2个重叠节点具有相同的父节点(即兄弟节点)
最顶层的节点具有
userInteractionEnabled = NO另一个节点userInteractionEnabled = YES.如果触摸了重叠,则在最顶层节点被命中测试并且失败(因为
userInteractionEnabled = NO)之后,而不是底部节点是下一个被命中测试的节点,它被跳过并且2个兄弟节点的父节点被命中测试.应该发生的是,下一个兄弟(底部节点)是经过重击测试而不是命中测试跳到父节点.
根据Sprite Kit文档:
"在场景中,当Sprite Kit处理触摸或鼠标事件时,它会遍历场景以找到想要接受事件的最近节点.如果该节点不想要事件,Sprite Kit会检查下一个最近的节点,所以处理命中测试的顺序基本上与绘制顺序相反.对于在命中测试期间要考虑的节点,其userInteractionEnabled属性必须设置为YES.除场景外的任何节点的默认值为NO节点."
这是一个错误,因为节点的兄弟姐妹在他们的父母之前呈现 - 兄弟姐妹应该是下一个被测试的,而不是它的父母.此外,如果一个节点有userInteractionEnabled = NO,那么肯定它应该是'透明'的关于命中测试 - 但是这不是因为它导致行为的改变,因为在测试中跳过了一个节点.
我在网上搜索过,但找不到任何人报告或发布此错误.我应该报告这个吗?
然后我在这里发布这个的原因是因为我想要一个关于这个bug的'修复'的建议(即建议在某处实现某些代码,以便SpriteKit以'预期'方式工作-testing)
复制错误:
使用在Xcode中启动新的"游戏"项目时提供的"Hello World"模板(它具有"Hello World"并在您单击时添加火箭精灵).
可选:[我还从项目中删除了火箭精灵图像,因为
X当找不到图像时出现的矩形更容易用于调试,可视化]将SKSpriteNode添加到场景中
userInteractionEnabled = YES(我将从现在开始将其称为节点A).运行代码.
您会注意到,当您单击节点A时,不会生成火箭精灵.(自命中测试成功后应该停止的预期行为 - 它在节点A上成功时停止)
但是,如果你在节点A旁边产生一些火箭,然后点击节点A和火箭重叠的地方,那么就有可能在节点A上产生另一个火箭 - 但这不可能.这意味着在最顶层节点(
userInteractionEnabled = NO默认情况下为火箭)上的命中测试失败后,它不再测试下一个节点A,而是测试火箭的父节点,而不是场景.
注意:我使用的是Xcode 7.3.1,Swift,iOS - 我还没有测试过这个bug是否具有通用性.
额外的细节:我做了一些额外的调试(上面的复制有轻微的复杂性),并确定命中测试后来发送给父母,因此不一定是场景.
我怀疑这要么是一个错误,要么是文档不正确。不管怎样,这可能是您正在寻找的解决方法。
听起来您想与可能是的节点进行交互
userInteractionEnabled属性设置为的一个或多个节点遮挡falsenodesAtPoint是一个很好的起点。它返回与抽头点相交的节点数组。将其添加到场景中touchesBegan并过滤未userInteractionEnabled设置为的true节点
let nodes = nodesAtPoint(location).filter {
$0.userInteractionEnabled
}
Run Code Online (Sandbox Code Playgroud)
zPosition 此时,您可以按节点树深度对节点数组进行排序。您可以使用以下扩展来确定节点的这些属性:
extension SKNode {
var depth:(level:Int,z:CGFloat) {
var node = parent
var level = 0
var zLevel:CGFloat = zPosition
while node != nil {
zLevel += node!.zPosition
node = node!.parent
level += 1
}
return (level, zLevel)
}
}
Run Code Online (Sandbox Code Playgroud)
并对数组进行排序
let nodes = nodesAtPoint(location)
.filter {$0.userInteractionEnabled}
.sort {$0.depth.z == $1.depth.z ? $0.depth.level > $1.depth.level : $0.depth.z > $1.depth.z}
Run Code Online (Sandbox Code Playgroud)
为了测试上面的代码,定义一个SKSpriteNode允许用户交互的子类
class Sprite:SKSpriteNode {
var offset:CGPoint?
// Save the node's relative location
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let location = touch.locationInNode(self)
offset = location
}
}
// Allow the user to drag the node to a new location
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first, parentNode = parent, relativePosition = offset {
let location = touch.locationInNode(parentNode)
position = CGPointMake(location.x-relativePosition.x, location.y-relativePosition.y)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
offset = nil
}
}
Run Code Online (Sandbox Code Playgroud)
并将以下触摸处理程序添加到子SKScene类中
var selectedNode:SKNode?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let location = touch.locationInNode(self)
// Sort and filter nodes that intersect with location
let nodes = nodesAtPoint(location)
.filter {$0.userInteractionEnabled}
.sort {$0.depth.z == $1.depth.z ? $0.depth.level > $1.depth.level : $0.depth.z > $1.depth.z}
// Forward the touch events to the appropriate node
if let first = nodes.first {
first.touchesBegan(touches, withEvent: event)
selectedNode = first
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let node = selectedNode {
node.touchesMoved(touches, withEvent: event)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let node = selectedNode {
node.touchesEnded(touches, withEvent: event)
selectedNode = nil
}
}
Run Code Online (Sandbox Code Playgroud)
下面的影片展示了如何使用上述代码来拖/放其他精灵下的精灵(带有userInteractionEnabled = true)。请注意,即使精灵是覆盖整个场景的蓝色背景精灵的子级,touchesBegan当用户拖动精灵时也会调用该场景。
| 归档时间: |
|
| 查看次数: |
540 次 |
| 最近记录: |