检查节点是否在屏幕上可见

3
我目前有一个很大的地图,超出了屏幕范围,因此它的坐标系与我的其他节点非常不同。这导致了一个问题,因为我需要在这张地图的范围内生成一个随机的CGPoint,并且如果该点在屏幕上/框架内,我就会在那里放置一个可见的节点。然而,检查节点是否在屏幕上始终失败。
我使用以下代码检查节点是否在框架内:CGRectContainsPoint(self.frame, values)(其中values是我生成的随机CGPoint)。现在我的问题来了,框架的坐标系与地图的坐标系完全不同。
例如,在下面的图片中,箭头指向的球在场景坐标中的坐标是(479, 402),但实际上它们在地图坐标中是(9691, 9753)。
我是使用touchesBegan事件确定这些坐标的。所以基本上,我如何将地图坐标系转换为适用于框架的坐标系?
因为如下图所示,这个点显然在框架内,但CGRectContainsPoint始终失败。我尝试过scene.convertPoint(position, fromNode: map),但它没有起作用。
编辑:(澄清一些事情)
我的视图层次结构看起来像这样:
地图节点超出屏幕,大约为10,000x10,000大小。(我将其设置为滚动类型的地图)。这个节点的原点(或0,0)位于底部左侧,即地图开始的地方,这意味着原点在屏幕外。在上面的图片中,我在地图的右上部分附近。我使用以下代码(将地图框架作为参数传递)作为CGPoint的扩展来生成随机CGPoint:
static func randPoint(within: CGRect) -> CGPoint {
    var point = within.origin

    point.x += CGFloat(arc4random() % UInt32(within.size.width))
    point.y += CGFloat(arc4random() % UInt32(within.size.height))

    return point;
}

我随后有以下代码(在didMoveToView中调用,注意我正在将其应用于我生成的节点 - 我只是略去了那段代码)。其中values是随机位置。
let values = CGPoint.randPoint(map.totalFrame)
if !CGRectContainsPoint(self.frame, convertPointToView(scene!.convertPoint(values, fromNode: map))) { 
      color = UIColor.clearColor()
}

使屏幕外的节点不可见。(因为用户可以滚动地图背景)。这总是被视为真,使得所有节点都不可见,即使节点确实在框架内(如上图所示,在那里我注释掉了清除颜色代码)。


你有一个以玩家节点为中心的摄像机吗? - sangony
是的,我目前正在将相机对准屏幕中央上方看到的“未命名”节点。我将其从视图层次结构中省略了,但它应该是世界的一个节点。 - Aaron
2个回答

3
如果我理解你的问题正确,你有一个包含SKSpriteNode的SKScene,该节点比场景视图大,并且你正在该精灵的坐标系中随机生成坐标,希望将其映射到视图上。
你可以使用SKNode的convertPoint(_:fromNode:)(其中场景是SKNode,地图是fromNode)来实现这一点。这应该可以将生成的地图坐标转换为场景坐标。接下来,使用场景的convertPointToView(_:)将该坐标转换为视图的坐标系。如果该点不在视图中,它将超出边界。

很不幸,那个方法没有起作用,可能是因为你误解了问题(这是我的错,我不太擅长解释这些问题),也可能是因为我没有正确实现它。我已经添加了以下内容:CGRectContainsPoint(convertPointToView(scene!.convertPoint(values, fromNode: map)))。请参见我上面的编辑,以获取更详细的说明。非常感谢您的帮助,希望您能帮我解决这个问题。 - Aaron
是的,有一些事情我不太清楚。你说你在地图中随机生成坐标(并通过 func randPoint(within:) 显示),但同时也说你使用 touchesBegan 事件确定坐标(采取哪种方法会影响解决方案)。此外,如果您详细说明未起作用的内容并提供结果值(输入或随机地图坐标、场景坐标和视图坐标),那将非常有帮助。最后,我不清楚为什么只有在视图中才放置地图上的节点;在不在视图中的 SKScene 上的节点不会被 SpriteKit 渲染。 - Kevin Owens
我原本以为SKScene也不会渲染这些物品,但出于某种原因它实际上是在渲染。如果我产生大约700个节点,只有大约10个节点在屏幕上,那么fps就会下降到1-2。而仅添加10个节点,即屏幕上的节点,fps保持在稳定的60。不知道为什么会这样,但我一直在围绕它构建。我使用randPoint(within :)生成随机点,您可以忽略touchesBegan事件,我可能不应该提到它(这更多是为了调试)。由于我正在循环中执行此操作,循环大约要运行500次,所以很难向您展示所有值。 - Aaron
但是这里有一些示例值(左侧位置为转换前的位置,右侧位置为转换后的位置)。http://pastebin.com/XYHNMQPx。我用于转换的代码是:convertPointToView(scene!.convertPoint(values,fromNode:map))。 - Aaron

2
使用包含玩家节点的worldNode,并将摄像头对准此节点,您可以使用以下代码开启/关闭检查:
float left = player.position.x - 700;
float right = player.position.x + 700;
float up = player.position.y + 450;
float down = player.position.y - 450;

if((object.position.x > left) && (object.position.x < right) && (object.position.y > down) && (object.position.y < up)) {
    if((object.parent == nil) && (object.dead == false)) {
        [worldNode addChild:object];
    }
} else {
    if(object.parent != nil) {
        [object removeFromParent];
    }
}

我上面使用的数字是静态的。您也可以使它们动态:

CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;

把屏幕宽度除以2,得到左右两边的距离。高度也是同理。

虽然比我希望的有点乱(不仅仅是一个简单的convertPoint),但这个方法确实能够正常工作并完成任务。感谢你的帮助。 - Aaron
@Aaron - 很高兴它完成了任务。请记住,您还可以使用上面的代码(例如)来删除不在视图中的节点,以增加FPS。我特别在具有许多带物理体的节点的项目中执行此操作,以减少需要处理的节点数量。 - sangony

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接