我SKCropNode
在我的游戏中大量使用,既用于风格用途,也用于我构建自己的 SpriteKit 版本的UIScrollView
. 我注意到,当我收到触摸事件或手势识别器在某个点触发时,SKScene.nodeAtPoint(...)
仍然会从裁剪节点击中隐藏在触摸点的节点。
如何防止SKScene.nodeAtPoint(...)
点击裁剪的内容,而是返回其下方的下一个可见节点?
我SKCropNode
在我的游戏中大量使用,既用于风格用途,也用于我构建自己的 SpriteKit 版本的UIScrollView
. 我注意到,当我收到触摸事件或手势识别器在某个点触发时,SKScene.nodeAtPoint(...)
仍然会从裁剪节点击中隐藏在触摸点的节点。
如何防止SKScene.nodeAtPoint(...)
点击裁剪的内容,而是返回其下方的下一个可见节点?
你不能。一切都是基于内容的框架,加上裁剪节点,它是巨大的。您唯一能做的就是检查触摸点,使用nodesAtPoint获取所有节点,然后逐个枚举,通过检查像素数据或CGPath轮廓检查触摸点是否触摸可见像素你的精灵并检查你是否在里面。
我设法解决了这个问题,但它并不漂亮,而且并非在每种情况下都有效。
这个想法是,当记录触摸时,我会在该点遍历所有节点。如果这些节点中的任何一个是(或者是)SKCropNodes 的子节点,我会检查裁剪节点上蒙版的框架。如果触摸位于遮罩之外,我们知道该节点此时不可见,我们可以假设触摸没有击中它。
这种方法的问题是我没有对裁剪蒙版内的透明度进行任何评估——我只是假设它是一个矩形。如果掩码是异常形状,我可能会在节点触摸时给出误报。
extension SKScene {
// Get the node at a given point, but ignore those that are hidden due to SKCropNodes.
func getVisibleNodeAtPoint(point: CGPoint) -> SKNode? {
var testedNodes = Set<SKNode>()
// Iterate over all the nodes hit by this click.
for touchedNode in self.nodesAtPoint(point) {
// If we've already checked this node, skip it. This happens because nodesAtPoint
// returns both leaf and ancestor nodes, and we test the ancestor nodes while
// testing leaf nodes.
if testedNodes.contains(touchedNode) {
continue
}
var stillVisible = true
// Walk the ancestry chain of the target node starting with the touched
// node itself.
var currentNode: SKNode = touchedNode
while true {
testedNodes.insert(currentNode)
if let currentCropNode = currentNode as? SKCropNode {
if let currentCropNodeMask = currentCropNode.maskNode {
let pointNormalizedToCurrentNode = self.convertPoint(point, toNode: currentCropNode)
let currentCropNodeMaskFrame = currentCropNodeMask.frame
// Check if the touch is inside the crop node's mask. If not, we
// know we've touched a hidden point.
if
pointNormalizedToCurrentNode.x < 0 || pointNormalizedToCurrentNode.x > currentCropNodeMaskFrame.size.width ||
pointNormalizedToCurrentNode.y < 0 || pointNormalizedToCurrentNode.y > currentCropNodeMaskFrame.size.height
{
stillVisible = false
break
}
}
}
// Move to next parent.
if let parent = currentNode.parent {
currentNode = parent
} else {
break
}
}
if !stillVisible {
continue
}
// We made it through the ancestry nodes. This node must be visible.
return touchedNode
}
// No visible nodes found at this point.
return nil
}
}