I'm still playing around with learning SpriteKit in iOS & have been doing lots of reading & lots of experimenting. I'm confused by something else I've found* regarding coordinates, frames & child nodes.
Consider this snippet of code, in which I'm trying to draw a green box around my spaceship sprite for debugging purposes:
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)
}
If I add the set the spaceship sprite with the line marked "VERSION 1" above, I get this:
which is clearly wrong. But if I comment out the line marked "VERSION 1" above, and instead use the line marked "VERSION 2", I get what I want:
Notice that the actual code for the lines marked Version 1 & Version 2 is identical: spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
So why does it matter when I set the position of the spaceship sprite?
To my way of thinking, the position of the spaceship sprite is irrelevant to the placement of the the debugFrame, because the debugFrame is a child of the spaceship sprite & thus, it's coordinates should be relative to the spaceship sprite's frame - right?
Thanks WM
*This is somewhat related to a question I asked yesterday:
In SpriteKit on iOS, scaling a textured sprite produces an incorrect frame?
but a) I understand that one now, and b) this is a different enough that it deserves its own question.
UPDATE:
Hmmm - thanks, guys for the ideas below, but I still don't get it & maybe this will help.
I modified my code to print out the relavant positions & frames:
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)
}
Then, I ran it both ways got these results. Since I understand Version 2, let's start there.
Running with the "VERSION 2 (RIGHT)" code, I got:
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)
The spaceship node starts, by default, with its position at the bottom left of the screen (Spaceship0). Its frame is also expressed in terms of its anchor point (center) being set in the bottom left of the screen, hence the negative numbers for the origin of its frame rect.
The debug frame is then created with its position set to 0, 0 by default & its frame set to be the same as the spaceship's.
The code (Spaceship2) then moves the spaceship node to a position in the view's coordinates (384.0,512.0), and its frame's origin is moved by adding the new position to the old origin (i.e. 384 + -159 = 225).
All is well.
Unfortunately, I still don't get Version 1.
When I run with the "VERSION 1 (WRONG)," code, I get
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)
As above, the spaceship node starts, by default, with its position at the bottom left of the screen (Spaceship0). Its frame is also expressed in terms of its anchor point (center) being set in the bottom left of the screen, hence the negative numbers for the origin of its frame rect.
The code (Spaceship1) then moves the spaceship node to a position in the view's coordinates (384.0,512.0), and its frame's origin is moved by adding the new position to the old origin (i.e. 384 + -159 = 225).
The debug frame is then created with its position set to 0, 0 by default & its frame set to have a strange width (543.5) & a strange height (812.5). Since I'm initializing the debugFrame.frame with spaceship.frame (i think that's what the default initializer does), I would expect the new debugFrame.frame to be the same as the spaceship's frame - but it isn't! The debug frame width & height values apparently come from adding the actual width & height to the origin of the spaceship node frame (543.5 = 225 + 318.5). But if that is the case, why is t's frame rect origin still 0, 0 & not the same adding (225.0 + 0 = 225.0)???
I don't get it.
You are creating the shape node using the sprite's frame, which is in scene coordinates. Because the shape will be a child of the sprite, you should create the node in the sprite's coordinates. For example, if you create the SKShapeNode
with spaceship's size
let debugFrame = SKShapeNode(rectOfSize: spaceship.size)
instead of using the spaceship's frame, debugFrame
will be centered on the spaceship regardless of when you set the ship's position. Also, debugFrame
will scale/move appropriately with the ship.
In response to your 'I don't get it'. Your code is ok but has a logical problem. The 'frame' is counted relative to the parent coordinates. Please realize that the parent of the spaceship and the parent of the debug window are different in your code.
Two ways to resolve your problem:
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)
Both solutions are widely used. You should chose whichever is better for you in your case. Three other problems might come up:
1.There might be code errors in my code, I just typed these into the web window directly without xcode syntax checking.
2.The anchor points of the two objects could be different. So you might need alignment in your code for this.
3.The zPosition of the objects should also be taken into consideration, so these objects will not be hidden under some other objects.
In response to the problem you are trying to solve, perhaps showPhysics would help:
skView.showsFPS = YES;
skView.showsNodeCount = YES;
skView.showsPhysics = YES;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With