Dmitriy Rodygin Dmitriy Rodygin - 1 year ago 114
Swift Question

SpriteKit. Stop rotate Texture with CameraNode

I'm doing a little game where the car goes around a planet. The camera is always directed to the machine at an angle of 90 degrees from the planet. I encountered a problem that the texture of the planet rotates along with the camera. If the camera does not rotate, the texture is behaving as it should. How do I fix this?

Create planet from beizerPath:

planetTexture = SKTexture(imageNamed: "asanoha")

planet = SKShapeNode(path: beizerPath.cgPath)
planet.position = CGPoint(x: 0, y: 0)
planet.zPosition = 10
planet.physicsBody = SKPhysicsBody(polygonFrom: beizerPath.cgPath)
planet.physicsBody?.isDynamic = false
planet.physicsBody?.categoryBitMask = planetGroup
planet.fillColor = SKColor.white
planet.fillTexture = planetTexture
planet.strokeColor = getRandomColor()
planet.lineWidth = 7
planet.glowWidth = 0.5


Camera position:

var cam = SKCameraNode()

override func didMove(to view: SKView) { = cam

let angle = atan2(car.position.y, car.position.x)
let theta : CGFloat = 0.0

let cx = (Float(a) * cosf(Float(angle - theta)))
let cy = (Float(a) * sinf(Float(angle - theta)))

cam.position = CGPoint(x: CGFloat(cx), y: CGFloat(cy))
cam.zRotation = angle - CGFloat(M_PI_2)

My problem on video

Answer Source

The reason for this is that shape-fills are done in screen-space. The texture is not mapped onto the SKShapeNode with texture coordinates, it is treated just like a fill colour.

The solution is to use an SKSpriteNode or an SKCropNode. If you use the sprite node, your image would have transparency around the edges already, to get the circular shape of the planet. If you use the crop node, you can add a square sprite node as a child, then set its mask node property to your shape node - this will determine the masked portions that get rendered.

The crop node method would be something like this:

planetTexture = SKTexture(imageNamed: "asanoha")
planetSprite = SKSpriteNode(texture: planetTexture)
planet = SKCropNode()
planet.maskNode = SKShapeNode(path: beizerPath.cgPath)

Update: I just watched your video and realised my answer made some incorrect assumptions about the look. However, the crop-node approach is still sound, just that you would need to set up the sprite node a bit differently, so that the texture repeats. The pure sprite with transparency wouldn't work for such a huge and irregular (and presumably generated) planet.