Question

I am a little confused by how the SKNode methods convertPoint:fromNode: and convertPoint:ToNode: are working, I have looked at the docs but its not that obvious what they do. For example, this (see diagram below) is a small test I did using convertPoint:fromNode: The black area is the SKScene background, the blue area is an SKSpriteNode parented to the SKScene, the red area is another SKSpriteNode parented to the blue sprite. The anchor points for both sprites are shown by the small green dots. What I wanted to do was get the scene position for the red sprite, which I did using the following code:

CGPoint positionInScene = [self convertPoint:[self position] 
                                    fromNode:[self redSprite]];

The result from this is

positionInScene = [105, 205]

Which is what I expected, as that would be the origin of the red square in scene space. What I am confused about is the arguments. From what I can guess:

[SKNode_A convertPoint: CGPoint_B toNode: SKScene_C]
  • SKNode_A = The node coordinate space to convert to ...
  • CGPoint_B = The point to convert (not sure why its [self position] above)
  • SKNode_C = The node coordinate space to convert from ...

My initial try at this was [self convertPoint:[redSprite position] fromNode:redSprite] because I was wanting to convert the red sprites origin to the scene. It just seems a bit clunky to get your head round, if anyone can cast a little light & logic on both this and its friend convertPoint:toNode: it would be very much appreciated.

enter image description here

Was it helpful?

Solution

- (CGPoint)convertPoint:(CGPoint)point fromNode:(SKNode *)node

This essentially is saying: convert a point expressed in another node's coordinate system into the caller's(self) coordinate system. The key is that the point has to be expressed in the node's coordinate system for this to work. If you are using a sprite's position as the point, you will need to pass the sprite's parent node as the last argument.

Example: To get the red square's position in the scene's coordinate system:

CGPoint positionInScene = [self convertPoint:[redSprite position]
                                    fromNode:[self blueSprite]];

[5, 5] in blueSprite's coordinate system --> [105, 205] in the scene's coordinate system

Your Example:

CGPoint positionInScene = [self convertPoint:[self position] 
                                    fromNode:[self redSprite]];

[0, 0] in redSprite's coordinate system --> [105, 205] in the scene's coordinate system

You ended up with the same result from your code, only because [self position] was [0, 0] and you used redSprite's coordinate system. Your initial try was close, you just needed to provide the coordinate system that redSprite's position was expressed in, which is the parent sprite's coordinate system; in this case, that's blueSprite.

- (CGPoint)convertPoint:(CGPoint)point toNode:(SKNode *)node

This method is very much the opposite of the first method. It will convert a point in the caller's coordinate system into another node's coordinate system. The key for this method is that the point given has to be expressed in the caller's coordinate system.

Example: Let's say your scene received a touch at [125, 225], and you wanted to add a new sprite at that location, which happens to be within the bounding box of redSprite. So to add a new sprite as a child of redSprite, you need to get the touch's location in redSprite's coordinate system. To get that:

CGPoint positionInRedSprite = [self convertPoint:touchLocation 
                                          toNode:[self redSprite]];

[125, 225] in the scene's coordinate system --> [20, 20] in redSprite's coordinate system

OTHER TIPS

After reading kevin.'s great answer, I was able to make a few extensions to SKNode that are both a great example and a handy set of tools.

extension SKNode {

var frameInScene:CGRect {
    get {
        if let scene = self.scene {
            if let parent = self.parent {
                let rectOriginInScene = scene.convertPoint(self.frame.origin, fromNode: parent)
                return CGRect(origin: rectOriginInScene, size: self.frame.size)
            }
        }
        println("Just the normal frame, not the scene frame!!!")
        return frame
    }
}

var positionInScene:CGPoint {
    get {
        if let scene = self.scene {
            if let parent = self.parent {
                return scene.convertPoint(self.position, fromNode: parent)
            }
        }
        println("Just the normal position, not the scene position!!!")
        return position
    }
}
}

I believe you could probably delete every self. but I felt it makes the code more confusing. And I'm not entirely sure what to do if the node doesn't belong to any scene or parent node, so I just returned the regular frame and position and used a println() to give me a warning.

If anyone has any changes/additions to this code I'd love to hear it, especially if it helps answer the question further.

For simplicity: When comparing location of node B to node A (trying to find how far they are from each other)

var BInSameCoordinateSys = NodeA.parent!.convertPoint(NodeB.position, fromNode: NodeB.parent!)

Troubleshoot by adding a sprite at location if not working to help visualize the location:

let childToSeeLocation = SKShapeNode(CircleWithRadius:50)
childToSeeLocation.fillColor = UIColor.redColor
ParentofA.addChild(childToSeeLocation)

I know this is a late response, but normally, I want to use the node's parent as the coordinate system for the node's position. So, I found this extension to be very useful:

extension SCNNode {
    func convertPosition(node:SCNNode) -> SCNVector3 {
        return self.convertPosition(node.position, fromNode:node.parentNode)
    }
}

For example, instead of calling this:

let position = scene.rootNode.convertPosition(node.position, fromNode: node.parentNode)

you just call this:

let position = scene.rootNode.convertPosition(node)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top