Adding gradient background to SKScene

Hi.


I am trying to add background with gradient color to my SKScene:


class GameScene: SKScene
{
    override func didMove(to view: SKView)
    {
        let gradientView = UIView(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))

        let image = UIImage.gradientBackgroundImage(bounds: CGRect(x: 0, y: 0, width: frame.width, height: frame.height),
                                                    colors: [UIColor.yellow.cgColor, UIColor.blue.cgColor])

        let background = SKSpriteNode(color: UIColor(patternImage: image), size: frame.size)
        addChild(background)
    }
}

extension UIImage
{
    /**
     http://www.riptutorial.com/ios/example/14328/gradient-image-with-colors
     */
    static func gradientBackgroundImage(bounds: CGRect, colors: [CGColor]) -> UIImage
    {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = bounds
        gradientLayer.colors = colors
       
        UIGraphicsBeginImageContext(gradientLayer.bounds.size)
        gradientLayer.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image!
    }
}


This is not working. Is there a better way to add gradient background to the SKScene?


Thanks a lot!


EDIT:


If I do this in didMove(to view:):


self.view?.backgroundColor = UIColor.blue
let backgroundDep = IGABackgroundLayer().gradientMetar() // My method to create CAGradientLayer
backgroundDep.frame = self.view!.bounds
self.view!.layer.insertSublayer(backgroundDep, at: 0)


...background is created nicely but added SKNodes are not seen on top of it.

Replies

I'm not sure offhand, but I don't think color patterns work in Sprite Kit.


Unless I'm misreading your code, you create an image with a gradient of the correct size for the sprite node. Why would you not just use that image as the sprite's texture? If anything, keeping the texture around is plausibly going to be cheaper (memory and performance) than keeping the gradient image around for the UIColor.

Thank you, QuinceyMorris.


Tried to do it through SKTexture in didMove(to view:):


let backgroundImg = UIImage.imageFromLayer(layer: IGABackgroundLayer().gradientMetar())
      
if let backgroundImg = backgroundImg
{
            // https://stackoverflow.com/questions/21765128/sktexture-from-uiimage-looks-different
            let backgroundTexture = SKTexture(image: backgroundImg)
            let background = SKSpriteNode(texture: backgroundTexture)
            background.position = CGPoint(x: frame.width/2, y: frame.height/2)
            addChild(background)
}

extension UIImage
{  
    /**
     https://stackoverflow.com/questions/3454356/uiimage-from-calayer-iphone-sdk
     */
    static func imageFromLayer(layer: CALayer) -> UIImage?
    {
        UIGraphicsBeginImageContextWithOptions(layer.bounds.size,
                                               layer.isOpaque, UIScreen.main.scale)
       
        defer { UIGraphicsEndImageContext() }
       
        // Don't proceed unless we have context
        guard let context = UIGraphicsGetCurrentContext() else
        {
            return nil
        }
       
        layer.render(in: context)
        return UIGraphicsGetImageFromCurrentImageContext()
    }
}


Still no background image. Not sure where the problem is...


Thanks.

One frequent cause of "disappearing" nodes is an incorrect zPosition. If you have another background covering the screen, you'll need to put these sprite nodes above it.


Regarding the other solution, I wouldn't try to use a separate layer. It'll either be on top of the SKScene (and hide the scene) or underneath the scene (and not be visible).

In case someone is interested, this has been solved the following way:


let color1 = UIColor(red: 255/255, green: 153/255, blue: 102/255, alpha: 1)
    let color2 = UIColor(red: 255/255, green: 204/255, blue: 153/255, alpha: 1)
    let backgroundImg = UIImage.radialGradientImage(size: frame.size, outerColor: color1, innerColor: color2)
    let backgroundTexture = SKTexture(image: backgroundImg)
    let background = SKSpriteNode(texture: backgroundTexture)
    background.position = CGPoint(x: frame.width/2, y: frame.height/2)
    addChild(background)

    /**
     https://gist.github.com/zummenix/67adccc1ee893ade8fb27a5a5a790498
     */
    static func radialGradientImage(size: CGSize, outerColor: UIColor, innerColor: UIColor) -> UIImage
    {
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let gradient = CGGradient(colorsSpace: colorSpace, colors: [outerColor.cgColor, innerColor.cgColor] as CFArray, locations: [1.0, 0.0])
        let center = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
       
        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
        let imageContext = UIGraphicsGetCurrentContext()
       
        imageContext!.drawRadialGradient(gradient!, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: size.width / 2.0, options: CGGradientDrawingOptions.drawsAfterEndLocation)
       
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
       
        return image!
    }

Similar solution, but without UIKit, using just CIContext: https://gist.github.com/Tantas/7fc01803d6b559da48d6