I'm learning SpriteKit game development for the fun of it & I've run across a seemingly simple problem that has me stumped.
Basically, after I scale a textured SKSpriteNode, the frame is NOT what I expect. I have figured out a few hacks to force it to what I want, but I'm trying to understand what is going on. Any ideas appreciated!
Here's my code WITHOUT SCALING:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
// spaceship.setScale(0.50)
let debugFrame = SKShapeNode.init(rect: spaceship.frame)
debugFrame.strokeColor = SKColor.greenColor()
spaceship.addChild(debugFrame)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
}
And my app looks like this:
Now, if I comment back in the line of code which scales it (spaceship.setScale(0.50)), I get this:
Notice that the spaceship is scaled down in the second image, but the frame is scaled even smaller. Why?
If I move the scaling line to after I add the spaceship to the scene, it does what I expect, but that seems wrong:
Here's the code with setScale called after adding the spaceship to the scene:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
let debugFrame = SKShapeNode.init(rect: spaceship.frame)
debugFrame.strokeColor = SKColor.greenColor()
spaceship.addChild(debugFrame)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
spaceship.setScale(0.50)
}
And here is what running my app looks like then:
So that works, but why is it necessary?
It has been suggested below, that this is a bug with SKShapeNode. But, replacing the SKShapeNode with an SKLabelNode has the same problem:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
spaceship.setScale(0.50)
let scoreNode = SKLabelNode(text: "100")
scoreNode.position = CGPointMake(CGRectGetMidX(spaceship.frame), CGRectGetMaxY(spaceship.frame))
scoreNode.fontColor = SKColor.redColor()
scoreNode.fontSize = 15.0
scoreNode.fontName = "Monaco"
scoreNode.zPosition = 10.0
spaceship.addChild(scoreNode)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
}
which gives us:
The intent is to have the score label (scoreNode) centered above the rocket, but as you can see it is on top of the top porthole. There is just something wrong with the spaceship's frame after I call spaceship.setScale.
I have made one additional discovery: the setScale call does not need to be after I add the spaceship to the scene. It just needs to be after I add the debugFrame/scoreNode to the spaceship. If I setScale AFTER that point, all is well:
func addSpaceship()
{
let spaceship = SKSpriteNode.init(imageNamed: "rocketship.png")
spaceship.name = "spaceship"
let scoreNode = SKLabelNode(text: "100")
scoreNode.position = CGPointMake(CGRectGetMidX(spaceship.frame), CGRectGetMaxY(spaceship.frame))
scoreNode.fontColor = SKColor.redColor()
scoreNode.fontSize = 15.0
scoreNode.fontName = "Monaco"
scoreNode.zPosition = 10.0
spaceship.addChild(scoreNode)
spaceship.setScale(0.50)
spaceship.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) - 150)
self.addChild(spaceship)
}
which results in:
Your problem may be in the order of these two lines :
spaceship.setScale(0.50)
let debugFrame = SKShapeNode.init(rect: spaceship.frame)
You scaled down the spaceship and then calculate the size of the rectangle with the scaled spaceship. Then when rendering the rectangle is scaled down to half its size which is quarter of the original spaceship size.
If you swap the lines, it should work as expected.
In general, it is better to make compose in the real size and then scale the whole just before adding it to the scene.
First of all, let me preface with SKShapeNode
is a really funky (maybe buggy class). At least it was in previous iterations of spritekit. If your goal is to add a debug rectangle for physics purposes. You can turn on showsPhysics
on your SKView class inside of your GameViewController
Heres my experiment
let redbox = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 100, height: 100))
redbox.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
redbox.setScale(0.5)
let debugFrame = SKShapeNode(ellipseOfSize: redbox.size)
debugFrame.strokeColor = SKColor.greenColor()
self.addChild(redbox)
redbox.addChild(debugFrame)
looks same as yours. if i call setScale after i add the nodes then my circle fills up my red square.
Also if I keep everything the same, but I just add my debugframe to the scene directly it will be scaled the right way, weird huh??
ok another test. note I set greenbox to 50% of redboxes size so we can see the redbox beneath. If the bug was occuring here than greenbox would end up filling 25% of the redbox.
let redbox = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 100, height: 100))
redbox.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
redbox.setScale(0.5)
let greenbox = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 50, height: 50))
self.addChild(redbox)
redbox.addChild(greenbox)
Ok so i did the same thing using another SKSpriteNode, and it behaves the way we'd expect. So for whatever reason, when you use an SKShapeNode as a child.. setScale is being called twice on it; unless you set the scale after adding the nodes to the scene. But this doesnt happen with SKSpriteNode.
The answer is.. I don't think there's a good answer. It's probably a bug. SKShapeNode has a history of bugs. SpriteKit has a few bugs =/ Someone correct me if I'm wrong.
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