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.

Community
  • 1
  • 1
ngngngn
  • 579
  • 4
  • 18
  • So the white arcs are smaller than you want them to be? – Confused Oct 22 '16 at 00:57
  • 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 want – ngngngn Oct 22 '16 at 01:01
  • I'm having enormous difficulty understanding your problem. I'm confused. Scaling is right... but position is wrong? – Confused Oct 22 '16 at 01:03
  • How is it that the grey lines are in the right position, but the white lines are not? – Confused Oct 22 '16 at 01:05
  • 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 example – ngngngn Oct 22 '16 at 01:35
  • 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. – ngngngn Oct 22 '16 at 01:40
  • 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. – Confused Oct 22 '16 at 02:01
  • 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. – Confused Oct 22 '16 at 02:07
  • 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. – Confused Oct 22 '16 at 02:09

1 Answers1

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

Confused
  • 6,048
  • 6
  • 34
  • 75
  • I think that is the issue also but how do I fix it? – ngngngn Oct 22 '16 at 16:49
  • still have this issue. Any ideas? – ngngngn Oct 25 '16 at 02:09
  • 1
    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. – Confused Oct 25 '16 at 02:47
  • 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. – ngngngn Oct 26 '16 at 00:36