对 iOS 上 SpriteKit 中的坐标、帧和子节点感到困惑?

Posted

技术标签:

【中文标题】对 iOS 上 SpriteKit 中的坐标、帧和子节点感到困惑?【英文标题】:Confusion about coordinates, frames & child nodes in SpriteKit on iOS? 【发布时间】:2014-12-31 08:19:23 【问题描述】:

我仍在尝试在 ios 中学习 SpriteKit,并且进行了大量的阅读和实验。我对我发现的关于坐标、框架和子节点的其他内容感到困惑。

考虑一下这段代码,其中我试图在我的飞船精灵周围画一个绿色框以进行调试:

func addSpaceship()

    let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
    spaceship.name = "spaceship"

    // VERSION 1
    spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))

    let debugFrame = SKShapeNode(rect: spaceship.frame)

    debugFrame.strokeColor = SKColor.greenColor()
    spaceship.addChild(debugFrame)

    // VERSION 2
   //       spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))

    spaceship.setScale(0.50)


    self.addChild(spaceship)

如果我用上面标记为“VERSION 1”的行添加宇宙飞船精灵的集合,我会得到:

这显然是错误的。但是,如果我将上面标记为“VERSION 1”的行注释,而使用标记为“VERSION 2”的行,我会得到我想要的:

请注意,标记为版本 1 和版本 2 的行的实际代码是相同的: spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))

那么为什么我设置飞船精灵的位置很重要呢?

在我看来,飞船精灵的位置与 debugFrame 的位置无关,因为 debugFrame 是飞船精灵的子节点,因此,它的坐标应该是相对于飞船精灵的框架的 - 对?

谢谢 WM

*这和我昨天问的一个问题有点相关:

In SpriteKit on iOS, scaling a textured sprite produces an incorrect frame?

但是a)我现在明白了,b)这是一个足够不同的问题,值得自己提出问题。

更新:

嗯 - 谢谢大家提供以下想法,但我还是不明白,也许这会有所帮助。

我修改了我的代码以打印出相关的位置和框架:

func addSpaceship()

    let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
    spaceship.name = "spaceship"
    println("Spaceship0 Pos \(spaceship.position) Frame = \(spaceship.frame)")

    // VERSION 1 (WRONG)
    spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
    println("Spaceship1 Pos \(spaceship.position) Frame = \(spaceship.frame)")

    let debugFrame = SKShapeNode(rect: spaceship.frame)
    println("DEBUG Pos \(debugFrame.position) Frame = \(debugFrame.frame)")

    debugFrame.strokeColor = SKColor.greenColor()
    spaceship.addChild(debugFrame)

    // VERSION 2 (RIGHT)
//      spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
//      println("Spaceship2 Pos \(spaceship.position) Frame = \(spaceship.frame)")

    spaceship.setScale(0.50)


    self.addChild(spaceship)

然后,我双向运行得到了这些结果。既然我了解第 2 版,那我们就从这里开始吧。

使用“VERSION 2 (RIGHT)”代码运行,我得到:

Spaceship0 Pos (0.0,0.0) Frame = (-159.0,-300.0,318.0,600.0)
DEBUG Pos (0.0,0.0) Frame = (-159.5,-300.5,319.0,601.0)
Spaceship2 Pos (384.0,512.0) Frame = (225.0,212.0,318.0,600.0)

默认情况下,spaceship 节点的起始位置位于屏幕的左下角 (Spaceship0)。它的框架也用它的锚点(中心)表示在屏幕的左下角,因此它的框架矩形的原点是负数。

然后创建调试框架,其位置设置为 0,默认为 0,并且其框架设置为与飞船的相同。

代码 (Spaceship2) 然后将宇宙飞船节点移动到视图坐标 (384.0,512.0) 中的位置,并通过将新位置添加到旧原点来移动其框架的原点(即 384 + -159 = 225) .

一切都好。

很遗憾,我仍然没有获得第 1 版。

当我使用“VERSION 1 (WRONG)”代码运行时,我得到了

Spaceship0 Pos (0.0,0.0) Frame = (-159.0,-300.0,318.0,600.0)
Spaceship1 Pos (384.0,512.0) Frame = (225.0,212.0,318.0,600.0)
DEBUG Pos (0.0,0.0) Frame = (0.0,0.0,543.5,812.5)

如上,飞船节点默认从屏幕左下角的位置开始(Spaceship0)。它的框架也用它的锚点(中心)表示在屏幕的左下角,因此它的框架矩形的原点是负数。

然后,代码 (Spaceship1) 将飞船节点移动到视图坐标 (384.0,512.0) 中的某个位置,并通过将新位置添加到旧原点来移动其框架的原点(即 384 + -159 = 225) .

然后创建调试框架,并将其位置设置为 0,默认情况下为 0,并将其框架设置为具有奇怪的宽度 (543.5) 和奇怪的高度 (812.5)。由于我正在使用 spaceship.frame 初始化 debugFrame.frame(我 认为 这是默认初始化程序所做的),我希望新的 debugFrame.frame 与飞船的框架相同 - 但它不是!调试框架的宽度和高度值显然来自将实际宽度和高度添加到飞船节点框架的原点 (543.5 = 225 + 318.5)。但是如果是这样的话,为什么t的frame rect origin还是0, 0 & not the same add (225.0 + 0 = 225.0)???

我不明白。

【问题讨论】:

记录 spaceship.frame,你会注意到它的起源取决于位置。由于形状是宇宙飞船的子对象,因此形状的位置是相对于它的。因此,如果 frame.origin 为 0,0 则一切正常,但如果为 200,200,则形状将相应偏移。 非常感谢 - 但恐怕我还是不明白。请参阅上面的更新。 【参考方案1】:

您正在使用精灵的框架创建形状节点,该框架位于场景坐标中。因为形状将是精灵的孩子,所以您应该在精灵的坐标中创建节点。例如,如果您使用宇宙飞船的大小创建SKShapeNode

let debugFrame = SKShapeNode(rectOfSize: spaceship.size)

无论您何时设置飞船的位置,debugFrame 都不会使用飞船的框架,而是以飞船为中心。此外,debugFrame 将随船适当缩放/移动。

【讨论】:

【参考方案2】:

回应您的“我不明白”。 你的代码没问题,但有一个逻辑问题。 'frame' 是相对于父坐标计算的。请注意,飞船的父级和调试窗口的父级在您的代码中是不同的。

解决问题的两种方法:

    为调试窗口添加一个零偏移,并使用宇宙飞船作为父级。 这样做的好处是调试窗口会随着飞船移动、缩放: let rectDebug = CGRectMake( 0, 0, spaceship.frame.size.width, spaceship.frame.size.height) let debugFrame = SKShapeNode(rect:rectDebug) spaceship.addChild(debugFrame) 将带有飞船“框架”坐标的调试窗口添加到飞船的父级(即“自身”)。这样做的缺点是,您必须自己在代码中移动、缩放调试窗口,因为它不会附加到飞船上。: let debugFrame = SKShapeNode(rect:spaceship.frame) self.addChild(debugFrame)

这两种解决方案都被广泛使用。在您的情况下,您应该选择更适合您的那个。 可能会出现其他三个问题:

1.我的代码中可能存在代码错误,我只是直接将这些输入到Web窗口中,没有进行xcode语法检查。

2.两个对象的锚点可能不同。因此,您可能需要在代码中为此对齐。

3.物体的zPosition也要考虑在内,这样这些物体就不会隐藏在其他一些物体下面。

针对您要解决的问题,也许 showPhysics 会有所帮助:

    skView.showsFPS = YES;
    skView.showsNodeCount = YES;
    skView.showsPhysics = YES;

【讨论】:

【参考方案3】:

这与您提到的另一个问题几乎相同。 frame 是一个包含 positionsize 的属性。它们都受制于其祖先节点的缩放。阅读https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Nodes/Nodes.html#//apple_ref/doc/uid/TP40013043-CH3-SW13 中的“一个节点将其许多属性应用于其后代”部分。

再说一遍:永远不要对节点应用缩放,在完全构建其层次结构之前永远不要移动节点,除非你想要一些特殊或奇怪的效果。

【讨论】:

但是直到在这两种情况下我完全构建了我的节点层次结构之后,我才进行缩放。我唯一做的就是移动了我设置父母位置的地方? 对,我犯了一个错误。但是你的问题和以前一样:假设 X,Y 是你的宇宙飞船的初始位置(我认为通常是 0,0),然后在版本 1 中你移动到 X=S/2,Y=S/2。然后在位置 S/2,S/2 创建一个调试帧,并缩小整体,但缩放不会缩放节点的位置,而是缩放其后代的位置,因此您的调试帧位于位置 S/4, S/4(相对于宇宙飞船)。在版本 2 中,调试帧从 0,0 开始,当您移动飞船时,它会移动到 S/2,S/2,并且调试帧相对于飞船在 0,0。 非常感谢 - 但恐怕我还是不明白。请参阅上面的更新。 似乎SKNodeShape 有问题。我尝试使用通过spriteNodeWithColor:size: 初始化的 SKSpriteNode,它运行良好。我选择的颜色是[NSColor colorWithCalibratedRed:0.5 green:0.5 blue:0.5 alpha:0.5],尺寸是spaceship.frame.size 出于这个原因,我避免使用 skshapenode。它并不总是按照您期望的方式运行。

以上是关于对 iOS 上 SpriteKit 中的坐标、帧和子节点感到困惑?的主要内容,如果未能解决你的问题,请参考以下文章

Spritekit - 屏幕坐标的底部

在 SpriteKit 中处理不同的 iOS 设备分辨率

Spritekit - 屏幕坐标的底部

iOS Spritekit 游戏中的肌酐水平

将 ARFaceAnchor 与 SpriteKit 叠加层对齐

使用 spritekit 检测释放滑动 swift 时的坐标 [关闭]