I'm trying to grasp the conversion thing in SpriteKit
but despite having read the documentation and several posts on SO I can't seem to get it right. As far as I understand there are two coordinate systems that work independently of one another, one for the scene
and one for the view
, which is why I simply can't use the things like UIScreen.main.bounds.maxX
to determine screen corners that the node can relate to. Am I getting this right?
Anyway, here's my attempt at converting coordinates:
let mySquare = SKShapeNode(rectOf: CGSize(width: 50, height: 50))
mySquare.fillColor = SKColor.blue
mySquare.lineWidth = 1
let myPoint = CGPoint(x: 200, y: 0)
let newPosition = mySquare.convert(myPoint, from: self)
mySquare.position = newPosition
print(newPosition)
self.addChild(mySquare)
The print
returns the exact same position as went in so obviously I'm not doing this right, but I have tried a number of different constellations but with pretty much no result; the coordinates remain the same. I have also tried let myPoint = CGPoint(x: UIScreen.main.bounds.maxX, y: UIScreen.main.bounds.maxY)
but same there; no conversion.
What am I missing? In my head I read the conversion above as "convert myPoint
from the view
coordinate system and use it for my node
mySquare.
There are lots of coordinate systems floating around, and so lots of potential sources of confusion:
convert(from:)
and convert(to:)
are for converting within the node hierarchy. You could ask where the knob is in the overall scene's coordinates by knob.convert(.zero, to: scene)
or joystick.convert(knob.position, to: scene)
. You very rarely need to do that sort of conversion.convertPoint(fromView:)
and convertPoint(toView:)
methods.If you don't do anything special and have the scene size the same as the view size, then the scene-view mapping will have (0,0) in the scene at the lower left corner of the view. Another common convention is to have (0,0) in the scene at the center of the screen by setting the scene's anchorPoint
to (0.5,0.5). Or perhaps you've designed the scene so that the world is 2000x2000 in size and there will be a nontrivial scaling and possible letter-boxing or cropping involved (depending on the setting of the scene's scaleMode
). Or if your game has a camera node and, e.g., the camera is set to follow the player around, then the view-to-scene mapping will be changing as the player moves.
In your code, calling mySquare.convert(from:)
doesn't really even make sense since the square hasn't been added to the scene at the time you're doing the "conversion".
Anyway, if you really want to do something like "put a square in the top-left corner of the screen", then you can take the point in the view's frame and convert it to scene coordinates and set the square's position to that.
override func didMove(to view: SKView) {
...
mySquare.position = convertPoint(fromView: CGPoint(x: view.frame.minX, y: view.frame.minY))
addChild(mySquare)
...
}
Edit: I would encourage you though to think mostly in terms of the overall scene, after some initial consideration of how the game should map to devices with screens of different sizes and aspect ratios. Once you're thinking in terms of the scene, then the scene's frame
(rather than the view's frame) becomes the most natural reference when you're imagining "at the left edge" or "near the bottom right".