SKSpriteNode frame way off
Asked Answered
A

1

1

I am trying to create an open circle from a UIBezierPath and turn that into an SKShapeNode which will later be turned into a SKSpriteNode.

I had an problem where I could not figure out how to shrink a Sprite while its line width did not scale down. You can see the solution here: Resize sprite without shrinking contents

I ended up fixing some things and creating a new class to implement a custom SKShapeNode with this functionality:

class ShrinkableShape: SKShapeNode {

let level: Int
let bezierPath: UIBezierPath
let scale: CGFloat
let finalLineWidth: CGFloat
let from: SKSpriteNode

let initialLineWidth = CGFloat(20)
let duration = 0.3

// level would be from 1 to 5 (it is in a for loop)
init(level: Int, from: SKSpriteNode) {
    self.level = level
    self.from = from

    // create the open circle  which is (43 * level) + 140
    bezierPath = UIBezierPath(arcCenter: CGPoint(x: 0, y: 0), radius: CGFloat(((43 * level) + 140)), startAngle: CGFloat(GLKMathDegreesToRadians(-50)), endAngle: CGFloat(M_PI * 2), clockwise: false)

    // calls static function in this class so it is more readable 
    scale = ShrinkableShape.scaleFrom(level: level) / ShrinkableShape.scaleFrom(level: level + 1)
    finalLineWidth = (initialLineWidth / scale)

    // inits shape and then sets it up to specific values
    super.init()
    setup()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

func setup() {
    // set sprite to path, set color, set line width and rotate it 
    self.path = bezierPath.cgPath
    self.strokeColor = UIColor(red:0.98, green:0.99, blue:0.99, alpha:1.00)
    self.lineWidth = 20
    self.position = from.position

    // FOR DEBUG: you will see what this makes in the screenshot
    let color = SKShapeNode(rect: self.frame)
    color.fillColor = UIColor.red
    color.alpha = 0.2
    color.position = self.position
    self.addChild(color)
}

// Makes the radius from the value given (only 1...5) 183, 226, 269, etc
static func scaleFrom(level: Int) -> CGFloat {
    return (CGFloat((level * 43) + 140) * 2.0)
}

}

enter image description here If you look at the screenshot you can notice the white circle is a little off compared to where it should be aligned (the gray bar and white circle path should be on each other). This only happens when I call the scale function shown in the solution (linked above).

I think it is because of the frame being all messed up. If you see, the frame is very off (and that same issue was the reason for past issues when converting to a texture because the frame was not exact as it should be)

Why does this happen? How do I fix it? Is there a way for me to manually set the correct frame for the SKShapeNode? Any other ideas on how I can do this?

--Edit-- I forgot to mention. The red part in the picture is a debug to see the frame of the SKShapeNode. You can see how it is created in the setup() func. But I included that to show how far off the frame was.

Ancestress answered 21/10, 2016 at 22:57 Comment(9)
So the white arcs are smaller than you want them to be?Baby
No the arc sizes are fine. When I scale the arcs, they don't scale to the right size because the frame is off. The scale calculation is shown in the code in the function scaleFrom in the class I showed. If it had the right frame it would scale to the position I wantAncestress
I'm having enormous difficulty understanding your problem. I'm confused. Scaling is right... but position is wrong?Baby
How is it that the grey lines are in the right position, but the white lines are not?Baby
Hm, I said that wrong sorry. So you know how you can use sprite.scale(0.5) and it will scale halfway down? Well, I am trying to use sprite.scale(x) to scale it to a exact length and width. To find the right scale, I am doing (level * 43 + 140) / ((level + 1) * 43) + 140 so for example, the first circle is 183x183 and the second circle is 226x226. The scale is 183/226. When you plug in sprite.scale(183/226) it will go to the exact length and width I want. But the problem is, it isnt. My thought on why it isnt is because the frame is not the right size.It is bigger than 226x226 for exampleAncestress
My first issue I linked was when I uses sprite.scale(x), it would scale the sprite down but it would make the line width thinner. So if I turned 226x226 into 183x183. the line width would be like 10 and each layer would have a different width. But I fixed that issue. Now the issue is when using scale, I cant get the sprite to the specific frame height and width I want.Ancestress
I think you're hitting one of (many) fundamental issues with how SpriteKit was "designed" and is "maintained". I don't believe there's been anyone creative or with design experience involved in the articulation and decision making. As a consequence, not only is drawing not well considered, it's horribly contrived and has performance problems, too. The name on the tin says "SpriteKit", and beyond good old fashioned bitmap sprites, they haven't really thought deeply about much else. Pretty much the entire thing is a mish-mash of plagiarised ideas from cocos2D, no bastion of creativity in itself.Baby
So you're going to have to consider that you'll need to workaround SpriteKit problems and idiosyncracies, because it simply wasn't ever made to handle intricate or even basic drawing. The fact that you can't control the outline/stroke position relative to the line you're drawing on is probably the first source of your problems, here.Baby
In ALL vector drawing apps, you can position the outline/stroke on the outside, center or inside of the line. Some of the better drawing apps permit this on open lines, too. SpriteKit does not have this option, and it's very annoying. CAShapeLayer also doesn't have this option. Yet your positioning is (in your mind and conception) probably based on an edge of the outline's width, whilst you're actually drawing on both sides of the line, half each of the width of your outline/stroke.Baby
B
1

I think part of your problem is the difference between where you have your lines versus where the bitmap edges are as a result of the stroke/outline width you have 'around' those lines.

Perhaps best described visually, whereby Red is what the system sees as the sprite size, whilst yellow is perhaps what you're thinking about. I've greatly exaggerated the line width to show the effect/problem I'm thinking about.

enter image description here

Baby answered 22/10, 2016 at 2:22 Comment(4)
I think that is the issue also but how do I fix it?Ancestress
still have this issue. Any ideas?Ancestress
You build two shapes. One is the very fine white line above. Use this for positioning and everything else you do. The blue line, make it a child of the very thin line. It will then move in accordance with the positions you give to the thin line.Baby
Ok I did a little more debugging. The real issue steamed down from the size of the GameScene I had. When I created the GameScene, I created it using GameScene.sks, and for some reason that had the wrong dimensions so it was like 700 by 400 for an iPhone. I removed that file and set it to the ViewController's bounds size. Now everything is working perfectly! I just have to change some sizes around to make up for the new size.Ancestress

© 2022 - 2024 — McMap. All rights reserved.