zPosition 的 SpriteKit 问题

Posted

技术标签:

【中文标题】zPosition 的 SpriteKit 问题【英文标题】:SpriteKit issue with zPosition 【发布时间】:2016-01-06 17:14:36 【问题描述】:

我在 SpriteKit 场景中遇到节点的 zPosition 问题。

我正在构建一个管理器节点,它可以对节点 zPosition 进行“层状”管理,本质上使您能够摆脱 zPosition 属性的手动管理,而是使用 addNodeUnder(node:SKNode) 等方法添加节点以及创建组。

这是我做的一个测试场景:

问题是通过我所做的操作,绿色和黄色节点应该在红色节点的顶部

我制作了自己的小调试器,以分层顺序显示 SceneKit“场景图”:

Node of type LMManagerNode  has zPosition = 0.0 and has children :
           Node of type LMWarpperNode  has zPosition = 0.0 and has children :
                     Node of type SKSpriteNode ('Red node') has zPosition = 0.0.

           Node of type LMWarpperNode  has zPosition = -100.0 and has children :
                     Node of type SKSpriteNode ('Blue node') has zPosition = 0.0.

           Node of type LMWarpperNode  has zPosition = 100.0 and has children :
                     Node of type LMGroupNode ('Test group') has zPosition = 0.0 and has children :
                               Node of type LMWarpperNode  has zPosition = -150.0 and has children :
                                         Node of type SKSpriteNode ('Yellow node') has zPosition = 0.0.

                               Node of type LMWarpperNode  has zPosition = -200.0 and has children :
                                         Node of type SKSpriteNode ('Green node') has zPosition = 0.0.

如你所见:

红色节点包含在具有zPosition = 0 的节点内 蓝色节点包含在具有zPosition = -100 的节点内 黄色和绿色节点包含在组节点中,组节点本身包含在具有zPosition = 100 的节点中

既然包含红色节点的节点有zPosition = 0,而包含绿色和黄色节点的节点有zPosition = 100,那么这两个节点不应该在红色节点的顶部 ?

顺便说一句,我的SKViewignoresSiblingOrder 设置为false...

编辑:这很奇怪:yellowNode.zPosition = 1000 确实使黄色节点位于红色节点之上。怎么可能?

EDIT #2 这是我的调试功能的代码:

func renderNodeHieararchyForNode(node:SKNode, index:Int) 

    var i = 0
    var beginning = ""
    while i != index 
        beginning += "          "
        i++
        if i == index 
            beginning += " "
        
    

    print("\(beginning)Node of type \(node.dynamicType) \(node.name != nil ? "('\(node.name!)')" : "") has zPosition = \(node.zPosition)\(node.children.count > 0 ? " and has children :" : ".")")
    for (i,child) in node.children.enumerate() 
        renderNodeHieararchyForNode(child, index: index+1)

        if i == node.children.count-1 
            print("")
        
    



func renderNodeHiearchyForNode(node:SKNode) 
    renderNodeHieararchyForNode(node, index: 0)

如您所见,我只使用 SpriteKit 框架方法来显示它,所以我的调试器的输出肯定是正确的。

【问题讨论】:

我相信事情发生了变化,并且父节点内的节点不再以某种方式尊重 zPosition,您是否尝试设置黄色和绿色节点 zPosition 而不是父母 zPosition,看看是否名列前茅? 这是有线的:yellowNode.zPosition = 1000 确实使黄色节点位于红色节点之上。怎么可能? 回答?您的意思是评论,我目前正在为您输入答案 我建议你阅读标题为Understanding the Drawing Order for a Node Tree的部分 @TrevörAnneDenise 是的,我明白,我的意思是允许您使用常规 SKNode 作为容器节点来模拟问题,而 SKSpriteNode 用于精灵而不是使用真实代码... 【参考方案1】:

来自 Apple 的文档,

z 位置是节点相对于其父节点的高度,就像节点的位置属性表示其相对于父节点的 x 和 y 位置一样。因此,您使用 z 位置将节点放置在父级位置的上方或下方。

当你考虑 z 位置时,节点树是这样的 渲染:

    计算每个节点的全局 z 位置。 节点按从最小 z 值到最大 z 值的顺序绘制。 如果两个节点共享相同的 z 值,则先渲染祖先,然后按子顺序渲染兄弟节点。

在哪里

node's global z position = node's z position + node's parent's z position + node's parent's parent's z position + ...

使用此公式,您的节点的全局 z 位置为

red is 0        (0 + 0 + 0)
blue is -100    (0 + -100 + 0)
yellow = -50    (-150 + 0 + 100 + 0)
green = -100    (-200 + 0 + 100 + 0)

从最高到最低的全局z位置(基于规则1和2),节点在场景中出现的顺序是

red
yellow
green / blue

红色出现在黄色之上,黄色出现在绿色/蓝色之上。由于绿色和蓝色具有相同的全局 z 值,因此使用同级顺序(规则 3)来确定绘制顺序。在这种情况下,绿色的***LMWarpperNode 节点(即它的曾祖父母)被添加到蓝色的LMWarpperNode 节点之后的LMManagerNode 节点,因此先绘制蓝色,然后在蓝色上绘制绿色。

这是最终(颠倒的)抽签顺序

red                0
yellow           -50
green           -100
blue            -100 (parent was added to scene before green's great grandparent)

【讨论】:

【参考方案2】:

这就是您现在对代码的期望

 -100: Node
     # 0: Node
        # 0: Blue
   0 : Node
     # 0: Node
        # 0:Red
 100 : Node
     # 0: Node
        # -200:Green
        # -150:Yellow

但真正发生的事情是这样的

 -100: Node
     : Node
     : Blue
     : Green
 -50 : Yellow
   0 : Node
     : Node
     : Red
 100 : Node
     : Node

这是因为 zPosition 只是从 parent 中减去,然后屏幕在 1 次通过所有节点。

为了解决这个问题,您需要创建一个覆盖addChild 函数的自定义类,并在另一个变量(可能是字典)中跟踪您的zPosition,然后将子zPosition 设置为零,然后使用 insertChild(node:, atIndex:) 将您的孩子按顺序放置在 children 数组中,使用此新变量来保留您的顺序。然后如果你更疯狂,你将不得不跟踪孩子何时更改其 zPosition,并使用上述样式在数组中正确设置它

【讨论】:

哦,非常感谢您的回答!这是 IMO 的一种有线行为......但出于性能原因,这是合理的。 实际上,我已经在使用字典,因为它们的计算成本不会与场景中的节点数量成比例增加。我想我会用它来计算我应该给孩子们的 zPositions。

以上是关于zPosition 的 SpriteKit 问题的主要内容,如果未能解决你的问题,请参考以下文章

设置 SKEmitterNode 的“targetNode”会导致奇怪的“zPosition”行为

iOS 7 Sprite Kit 更改 SKAction 中的 zPosition

iOS8 Spritekit 滞后

如何应用SPRITEKIT的CAMERA实现游戏中的ENDLESS无限循环背景

如何使用 Sprite Kit 创建具有 3D 效果的戒指?

相对于不同节点的不同zPosition